1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3  * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  * Copyright (C) <2013> Collabora Ltd.
5  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6  * Copyright (C) <2015> Jan Schmidt <jan@centricular.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 /**
24  * SECTION:element-playbin3
25  * @title: playbin3
26  *
27  * playbin3 provides a stand-alone everything-in-one abstraction for an
28  * audio and/or video player. It differs from the previous playbin (playbin2)
29  * by supporting publication and selection of available streams via the
30  * #GstStreamCollection message and #GST_EVENT_SELECT_STREAMS event API.
31  *
32  * <emphasis>playbin3 is still experimental API and a technology preview.
33  * Its behaviour and exposed API is subject to change.</emphasis>
34  *
35  * playbin3 can handle both audio and video files and features
36  *
37  * * automatic file type recognition and based on that automatic
38  * selection and usage of the right audio/video/subtitle demuxers/decoders
39  *
40  * * auxilliary files - such as external subtitles and audio tracks
41  * * visualisations for audio files
42  * * subtitle support for video files. Subtitles can be store in external
43  *   files.
44  * * stream selection between different video/audio/subtitles streams
45  * * meta info (tag) extraction
46  * * easy access to the last video sample
47  * * buffering when playing streams over a network
48  * * volume control with mute option
49  *
50  * ## Usage
51  *
52  * A playbin element can be created just like any other element using
53  * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin3:uri
54  * property. This must be an absolute URI, relative file paths are not allowed.
55  * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
56  *
57  * Playbin3 is a #GstPipeline. It will notify the application of everything
58  * that's happening (errors, end of stream, tags found, state changes, etc.)
59  * by posting messages on its #GstBus. The application needs to watch the
60  * bus.
61  *
62  * Playback can be initiated by setting the element to PLAYING state using
63  * gst_element_set_state(). Note that the state change will take place in
64  * the background in a separate thread, when the function returns playback
65  * is probably not happening yet and any errors might not have occured yet.
66  * Applications using playbin3 should ideally be written to deal with things
67  * completely asynchroneous.
68  *
69  * When playback has finished (an EOS message has been received on the bus)
70  * or an error has occured (an ERROR message has been received on the bus) or
71  * the user wants to play a different track, playbin3 should be set back to
72  * READY or NULL state, then the #GstPlayBin3:uri property should be set to the
73  * new location and then playbin3 be set to PLAYING state again.
74  *
75  * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
76  * on the playbin3 element. Again, the seek will not be executed
77  * instantaneously, but will be done in a background thread. When the seek
78  * call returns the seek will most likely still be in process. An application
79  * may wait for the seek to finish (or fail) using gst_element_get_state() with
80  * -1 as the timeout, but this will block the user interface and is not
81  * recommended at all.
82  *
83  * Applications may query the current position and duration of the stream
84  * via gst_element_query_position() and gst_element_query_duration() and
85  * setting the format passed to GST_FORMAT_TIME. If the query was successful,
86  * the duration or position will have been returned in units of nanoseconds.
87  *
88  * ## Advanced Usage: specifying the audio and video sink
89  *
90  * By default, if no audio sink or video sink has been specified via the
91  * #GstPlayBin3:audio-sink or #GstPlayBin3:video-sink property, playbin3 will use the autoaudiosink
92  * and autovideosink elements to find the first-best available output method.
93  * This should work in most cases, but is not always desirable. Often either
94  * the user or application might want to specify more explicitly what to use
95  * for audio and video output.
96  *
97  * If the application wants more control over how audio or video should be
98  * output, it may create the audio/video sink elements itself (for example
99  * using gst_element_factory_make()) and provide them to playbin3 using the
100  * #GstPlayBin3:audio-sink or #GstPlayBin3:video-sink property.
101  *
102  * GNOME-based applications, for example, will usually want to create
103  * gconfaudiosink and gconfvideosink elements and make playbin3 use those,
104  * so that output happens to whatever the user has configured in the GNOME
105  * Multimedia System Selector configuration dialog.
106  *
107  * The sink elements do not necessarily need to be ready-made sinks. It is
108  * possible to create container elements that look like a sink to playbin3,
109  * but in reality contain a number of custom elements linked together. This
110  * can be achieved by creating a #GstBin and putting elements in there and
111  * linking them, and then creating a sink #GstGhostPad for the bin and pointing
112  * it to the sink pad of the first element within the bin. This can be used
113  * for a number of purposes, for example to force output to a particular
114  * format or to modify or observe the data before it is output.
115  *
116  * It is also possible to 'suppress' audio and/or video output by using
117  * 'fakesink' elements (or capture it from there using the fakesink element's
118  * "handoff" signal, which, nota bene, is fired from the streaming thread!).
119  *
120  * ## Retrieving Tags and Other Meta Data
121  *
122  * Most of the common meta data (artist, title, etc.) can be retrieved by
123  * watching for TAG messages on the pipeline's bus (see above).
124  *
125  * Other more specific meta information like width/height/framerate of video
126  * streams or samplerate/number of channels of audio streams can be obtained
127  * from the negotiated caps on the sink pads of the sinks.
128  *
129  * ## Buffering
130  * Playbin3 handles buffering automatically for the most part, but applications
131  * need to handle parts of the buffering process as well. Whenever playbin3 is
132  * buffering, it will post BUFFERING messages on the bus with a percentage
133  * value that shows the progress of the buffering process. Applications need
134  * to set playbin3 to PLAYING or PAUSED state in response to these messages.
135  * They may also want to convey the buffering progress to the user in some
136  * way. Here is how to extract the percentage information from the message:
137  * |[
138  * switch (GST_MESSAGE_TYPE (msg)) {
139  *   case GST_MESSAGE_BUFFERING: {
140  *     gint percent = 0;
141  *     gst_message_parse_buffering (msg, &percent);
142  *     g_print ("Buffering (%u percent done)", percent);
143  *     break;
144  *   }
145  *   ...
146  * }
147  * ]|
148  *
149  * Note that applications should keep/set the pipeline in the PAUSED state when
150  * a BUFFERING message is received with a buffer percent value < 100 and set
151  * the pipeline back to PLAYING state when a BUFFERING message with a value
152  * of 100 percent is received (if PLAYING is the desired state, that is).
153  *
154  * ## Embedding the video window in your application
155  * By default, playbin3 (or rather the video sinks used) will create their own
156  * window. Applications will usually want to force output to a window of their
157  * own, however. This can be done using the #GstVideoOverlay interface, which most
158  * video sinks implement. See the documentation there for more details.
159  *
160  * ## Specifying which CD/DVD device to use
161  *
162  * The device to use for CDs/DVDs needs to be set on the source element playbin3
163  * creates before it is opened. The most generic way of doing this is to connect
164  * to playbin3's "source-setup" signal, which will be emitted by playbin3 when
165  * it has created the source element for a particular URI. In the signal
166  * callback you can check if the source element has a "device" property and set
167  * it appropriately. In some cases the device can also be set as part of the
168  * URI, but it depends on the elements involved if this will work or not. For
169  * example, for DVD menu playback, the following syntax might work (if the
170  * resindvd plugin is used): dvd://[/path/to/device]
171  *
172  * ## Handling redirects
173  *
174  * Some elements may post 'redirect' messages on the bus to tell the
175  * application to open another location. These are element messages containing
176  * a structure named 'redirect' along with a 'new-location' field of string
177  * type. The new location may be a relative or an absolute URI. Examples
178  * for such redirects can be found in many quicktime movie trailers.
179  *
180  * ## Examples
181  * |[
182  * gst-launch-1.0 -v playbin3 uri=file:///path/to/somefile.mp4
183  * ]|
184  *  This will play back the given AVI video file, given that the video and
185  * audio decoders required to decode the content are installed. Since no
186  * special audio sink or video sink is supplied (via playbin3's audio-sink or
187  * video-sink properties) playbin3 will try to find a suitable audio and
188  * video sink automatically using the autoaudiosink and autovideosink elements.
189  * |[
190  * gst-launch-1.0 -v playbin3 uri=cdda://4
191  * ]|
192  *  This will play back track 4 on an audio CD in your disc drive (assuming
193  * the drive is detected automatically by the plugin).
194  * |[
195  * gst-launch-1.0 -v playbin3 uri=dvd://
196  * ]|
197  *  This will play back the DVD in your disc drive (assuming
198  * the drive is detected automatically by the plugin).
199  *
200  */
201 
202 #ifdef HAVE_CONFIG_H
203 #include "config.h"
204 #endif
205 
206 #include <string.h>
207 #include <gst/gst.h>
208 
209 #include <gst/gst-i18n-plugin.h>
210 #include <gst/pbutils/pbutils.h>
211 #include <gst/audio/streamvolume.h>
212 #include <gst/video/video-info.h>
213 #include <gst/video/video-multiview.h>
214 #include <gst/video/videooverlay.h>
215 #include <gst/video/navigation.h>
216 #include <gst/video/colorbalance.h>
217 #include "gstplay-enum.h"
218 #include "gstplayback.h"
219 #include "gstplaysink.h"
220 #include "gstsubtitleoverlay.h"
221 #include "gstplaybackutils.h"
222 
223 GST_DEBUG_CATEGORY_STATIC (gst_play_bin3_debug);
224 #define GST_CAT_DEFAULT gst_play_bin3_debug
225 
226 #define GST_TYPE_PLAY_BIN               (gst_play_bin3_get_type())
227 #define GST_PLAY_BIN3(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin3))
228 #define GST_PLAY_BIN3_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBin3Class))
229 #define GST_IS_PLAY_BIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
230 #define GST_IS_PLAY_BIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
231 
232 #define ULONG_TO_POINTER(number)        ((gpointer) (guintptr) (number))
233 #define POINTER_TO_ULONG(number)        ((guintptr) (number))
234 
235 #define VOLUME_MAX_DOUBLE 10.0
236 
237 typedef struct _GstPlayBin3 GstPlayBin3;
238 typedef struct _GstPlayBin3Class GstPlayBin3Class;
239 typedef struct _GstSourceGroup GstSourceGroup;
240 typedef struct _GstSourceCombine GstSourceCombine;
241 typedef struct _SourcePad SourcePad;
242 
243 typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void);
244 
245 /* GstSourceCombine controls all the information regarding a certain
246  * media type.
247  *
248  * It can control a custom combiner element (by default none)
249  */
250 struct _GstSourceCombine
251 {
252   const gchar *media_type;      /* the media type for the combiner */
253   SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */
254   GstPlaySinkType type;         /* the sink pad type of the combiner */
255   GstStreamType stream_type;    /* The GstStreamType of the combiner */
256 
257   GstElement *combiner;         /* the combiner */
258   GPtrArray *channels;          /* Array of GstPad ? */
259 
260   GstPad *srcpad;               /* the source pad of the combiner */
261   GstPad *sinkpad;              /* the sinkpad of the sink when the combiner
262                                  * is linked */
263 
264   GPtrArray *streams;           /* Sorted array of GstStream for the given type */
265 
266   gboolean has_active_pad;      /* stream combiner has the "active-pad" property */
267 
268   gboolean is_concat;           /* The stream combiner is the 'concat' element */
269 };
270 
271 #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
272 #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
273 #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
274 
275 enum
276 {
277   PLAYBIN_STREAM_AUDIO = 0,
278   PLAYBIN_STREAM_VIDEO,
279   PLAYBIN_STREAM_TEXT,
280   PLAYBIN_STREAM_LAST
281 };
282 
283 /* names matching the enum above */
284 static const gchar *stream_type_names[] = {
285   "audio", "video", "text"
286 };
287 
288 
289 #define STREAM_TYPES_FORMAT "s%s%s"
290 #define STREAM_TYPES_ARGS(s) (s) & GST_STREAM_TYPE_AUDIO ? "audio " : "", \
291     (s) & GST_STREAM_TYPE_VIDEO ? "video " : "",			\
292     (s) & GST_STREAM_TYPE_TEXT ? "text " : ""
293 
294 
295 
296 #if 0                           /* AUTOPLUG DISABLED */
297 static void avelements_free (gpointer data);
298 static GSequence *avelements_create (GstPlayBin3 * playbin,
299     gboolean isaudioelement);
300 #endif
301 
302 /* The GstAudioVideoElement structure holding the audio/video decoder
303  * and the audio/video sink factories together with field indicating
304  * the number of common caps features */
305 typedef struct
306 {
307   GstElementFactory *dec;       /* audio:video decoder */
308   GstElementFactory *sink;      /* audio:video sink */
309   guint n_comm_cf;              /* number of common caps features */
310 } GstAVElement;
311 
312 /* a structure to hold information about a uridecodebin pad */
313 struct _SourcePad
314 {
315   GstPad *pad;                  /* The controlled pad */
316   GstStreamType stream_type;    /* stream type of the controlled pad */
317   gulong event_probe_id;
318 };
319 
320 /* a structure to hold the objects for decoding a uri and the subtitle uri
321  */
322 struct _GstSourceGroup
323 {
324   GstPlayBin3 *playbin;
325 
326   GMutex lock;
327 
328   gboolean valid;               /* the group has valid info to start playback */
329   gboolean active;              /* the group is active */
330 
331   gboolean playing;             /* the group is currently playing
332                                  * (outputted on the sinks) */
333 
334   /* properties */
335   gchar *uri;
336   gchar *suburi;
337 
338   /* The currently outputted group_id */
339   guint group_id;
340 
341   /* Bit-wise set of stream types we have requested from uridecodebin3 */
342   GstStreamType selected_stream_types;
343 
344   /* Bit-wise set of stream types for which pads are present */
345   GstStreamType present_stream_types;
346 
347   /* TRUE if a 'about-to-finish' needs to be posted once we have
348    * got source pads for all requested stream types
349    *
350    * FIXME : Move this logic to uridecodebin3 later */
351   gboolean pending_about_to_finish;
352 
353   /* uridecodebin to handle uri and suburi */
354   GstElement *uridecodebin;
355 
356   /* Active sinks for each media type. These are initialized with
357    * the configured or currently used sink, otherwise
358    * left as NULL and playbin tries to automatically
359    * select a good sink */
360   GstElement *audio_sink;
361   GstElement *video_sink;
362   GstElement *text_sink;
363 
364   /* List of source pads */
365   GList *source_pads;
366 
367   /* uridecodebin signals */
368   gulong pad_added_id;
369   gulong pad_removed_id;
370   gulong select_stream_id;
371   gulong source_setup_id;
372   gulong about_to_finish_id;
373 
374 #if 0                           /* AUTOPLUG DISABLED */
375   gulong autoplug_factories_id;
376   gulong autoplug_select_id;
377   gulong autoplug_continue_id;
378   gulong autoplug_query_id;
379 #endif
380 
381   gboolean stream_changed_pending;
382 
383   /* Active stream collection */
384   GstStreamCollection *collection;
385 
386 
387   /* buffering message stored for after switching */
388   GstMessage *pending_buffering_msg;
389 };
390 
391 #define GST_PLAY_BIN3_GET_LOCK(bin) (&((GstPlayBin3*)(bin))->lock)
392 #define GST_PLAY_BIN3_LOCK(bin) (g_rec_mutex_lock (GST_PLAY_BIN3_GET_LOCK(bin)))
393 #define GST_PLAY_BIN3_UNLOCK(bin) (g_rec_mutex_unlock (GST_PLAY_BIN3_GET_LOCK(bin)))
394 
395 /* lock to protect dynamic callbacks, like no-more-pads */
396 #define GST_PLAY_BIN3_DYN_LOCK(bin)    g_mutex_lock (&(bin)->dyn_lock)
397 #define GST_PLAY_BIN3_DYN_UNLOCK(bin)  g_mutex_unlock (&(bin)->dyn_lock)
398 
399 /* lock for shutdown */
400 #define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label)			\
401   G_STMT_START {						\
402     if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown)))		\
403       goto label;						\
404     GST_PLAY_BIN3_DYN_LOCK (bin);				\
405     if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) {	\
406       GST_PLAY_BIN3_DYN_UNLOCK (bin);				\
407       goto label;						\
408     }								\
409   } G_STMT_END
410 
411 /* unlock for shutdown */
412 #define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin)	\
413   GST_PLAY_BIN3_DYN_UNLOCK (bin);		\
414 
415 /**
416  * GstPlayBin3:
417  *
418  * playbin element structure
419  */
420 struct _GstPlayBin3
421 {
422   GstPipeline parent;
423 
424   GRecMutex lock;               /* to protect group switching */
425 
426   /* the input groups, we use a double buffer to switch between current and next */
427   GstSourceGroup groups[2];     /* array with group info */
428   GstSourceGroup *curr_group;   /* pointer to the currently playing group */
429   GstSourceGroup *next_group;   /* pointer to the next group */
430 
431   /* Array of GstPad controlled by each combiner */
432   GPtrArray *channels[PLAYBIN_STREAM_LAST];     /* links to combiner pads */
433 
434   /* combiners for different streams */
435   GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
436 
437   /* Bit-wise set of stream types we have requested from uridecodebin3.
438    * Calculated as the combination of the 'selected_stream_types' of
439    * each sourcegroup */
440   GstStreamType selected_stream_types;
441 
442   /* Bit-wise set of configured output stream types (i.e. active
443      playsink inputs and combiners) */
444   GstStreamType active_stream_types;
445 
446   /* properties */
447   guint64 connection_speed;     /* connection speed in bits/sec (0 = unknown) */
448   gint current_video;           /* the currently selected stream */
449   gint current_audio;           /* the currently selected stream */
450   gint current_text;            /* the currently selected stream */
451 
452   gboolean do_stream_selections;        /* Set to TRUE when any of current-{video|audio|text} are set to
453                                            say playbin should do backwards-compatibility behaviours */
454 
455   guint64 buffer_duration;      /* When buffering, the max buffer duration (ns) */
456   guint buffer_size;            /* When buffering, the max buffer size (bytes) */
457   gboolean force_aspect_ratio;
458 
459   /* Multiview/stereoscopic overrides */
460   GstVideoMultiviewFramePacking multiview_mode;
461   GstVideoMultiviewFlags multiview_flags;
462 
463   /* our play sink */
464   GstPlaySink *playsink;
465 
466   /* Task for (de)activating groups, protected by the activation lock */
467   GstTask *activation_task;
468   GRecMutex activation_lock;
469 
470   /* lock protecting dynamic adding/removing */
471   GMutex dyn_lock;
472   /* if we are shutting down or not */
473   gint shutdown;
474   gboolean async_pending;       /* async-start has been emitted */
475 
476   GMutex elements_lock;
477   guint32 elements_cookie;
478   GList *elements;              /* factories we can use for selecting elements */
479 
480   gboolean have_selector;       /* set to FALSE when we fail to create an
481                                  * input-selector, so that we only post a
482                                  * warning once */
483 
484   gboolean video_pending_flush_finish;  /* whether we are pending to send a custom
485                                          * custom-video-flush-finish event
486                                          * on pad activation */
487   gboolean audio_pending_flush_finish;  /* whether we are pending to send a custom
488                                          * custom-audio-flush-finish event
489                                          * on pad activation */
490   gboolean text_pending_flush_finish;   /* whether we are pending to send a custom
491                                          * custom-subtitle-flush-finish event
492                                          * on pad activation */
493 
494   GstElement *audio_sink;       /* configured audio sink, or NULL      */
495   GstElement *video_sink;       /* configured video sink, or NULL      */
496   GstElement *text_sink;        /* configured text sink, or NULL       */
497 
498   GstElement *audio_stream_combiner;    /* configured audio stream combiner, or NULL */
499   GstElement *video_stream_combiner;    /* configured video stream combiner, or NULL */
500   GstElement *text_stream_combiner;     /* configured text stream combiner, or NULL */
501 
502   GSequence *aelements;         /* a list of GstAVElements for audio stream */
503   GSequence *velements;         /* a list of GstAVElements for video stream */
504 
505   guint64 ring_buffer_max_size; /* 0 means disabled */
506 };
507 
508 struct _GstPlayBin3Class
509 {
510   GstPipelineClass parent_class;
511 
512   /* notify app that the current uri finished decoding and it is possible to
513    * queue a new one for gapless playback */
514   void (*about_to_finish) (GstPlayBin3 * playbin);
515 
516   /* get the last video sample and convert it to the given caps */
517   GstSample *(*convert_sample) (GstPlayBin3 * playbin, GstCaps * caps);
518 };
519 
520 /* props */
521 #define DEFAULT_URI               NULL
522 #define DEFAULT_SUBURI            NULL
523 #define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
524                                   GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
525                                   GST_PLAY_FLAG_SOFT_COLORBALANCE | GST_PLAY_FLAG_BUFFERING
526 #define DEFAULT_CURRENT_VIDEO     -1
527 #define DEFAULT_CURRENT_AUDIO     -1
528 #define DEFAULT_CURRENT_TEXT      -1
529 #define DEFAULT_SUBTITLE_ENCODING NULL
530 #define DEFAULT_AUDIO_SINK        NULL
531 #define DEFAULT_VIDEO_SINK        NULL
532 #define DEFAULT_VIS_PLUGIN        NULL
533 #define DEFAULT_TEXT_SINK         NULL
534 #define DEFAULT_VOLUME            1.0
535 #define DEFAULT_MUTE              FALSE
536 #define DEFAULT_FRAME             NULL
537 #define DEFAULT_FONT_DESC         NULL
538 #define DEFAULT_CONNECTION_SPEED  0
539 #define DEFAULT_BUFFER_DURATION   -1
540 #define DEFAULT_BUFFER_SIZE       -1
541 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
542 
543 enum
544 {
545   PROP_0,
546   PROP_URI,
547   PROP_CURRENT_URI,
548   PROP_SUBURI,
549   PROP_CURRENT_SUBURI,
550   PROP_FLAGS,
551   PROP_SUBTITLE_ENCODING,
552   PROP_AUDIO_SINK,
553   PROP_VIDEO_SINK,
554   PROP_VIS_PLUGIN,
555   PROP_TEXT_SINK,
556   PROP_VIDEO_STREAM_COMBINER,
557   PROP_AUDIO_STREAM_COMBINER,
558   PROP_TEXT_STREAM_COMBINER,
559   PROP_VOLUME,
560   PROP_MUTE,
561   PROP_SAMPLE,
562   PROP_FONT_DESC,
563   PROP_CONNECTION_SPEED,
564   PROP_BUFFER_SIZE,
565   PROP_BUFFER_DURATION,
566   PROP_AV_OFFSET,
567   PROP_TEXT_OFFSET,
568   PROP_RING_BUFFER_MAX_SIZE,
569   PROP_FORCE_ASPECT_RATIO,
570   PROP_AUDIO_FILTER,
571   PROP_VIDEO_FILTER,
572   PROP_MULTIVIEW_MODE,
573   PROP_MULTIVIEW_FLAGS
574 };
575 
576 /* signals */
577 enum
578 {
579   SIGNAL_ABOUT_TO_FINISH,
580   SIGNAL_CONVERT_SAMPLE,
581   SIGNAL_SOURCE_SETUP,
582   SIGNAL_ELEMENT_SETUP,
583   LAST_SIGNAL
584 };
585 
586 #if 0                           /* AUTOPLUG DISABLED */
587 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
588 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
589 #endif
590 
591 static void gst_play_bin3_class_init (GstPlayBin3Class * klass);
592 static void gst_play_bin3_init (GstPlayBin3 * playbin);
593 static void gst_play_bin3_finalize (GObject * object);
594 
595 static void gst_play_bin3_set_property (GObject * object, guint prop_id,
596     const GValue * value, GParamSpec * spec);
597 static void gst_play_bin3_get_property (GObject * object, guint prop_id,
598     GValue * value, GParamSpec * spec);
599 
600 static GstStateChangeReturn gst_play_bin3_change_state (GstElement * element,
601     GstStateChange transition);
602 
603 static void gst_play_bin3_handle_message (GstBin * bin, GstMessage * message);
604 static void gst_play_bin3_deep_element_added (GstBin * playbin,
605     GstBin * sub_bin, GstElement * child);
606 static gboolean gst_play_bin3_send_event (GstElement * element,
607     GstEvent * event);
608 
609 static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin,
610     GstCaps * caps);
611 
612 static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin);
613 
614 static void gst_play_bin3_check_group_status (GstPlayBin3 * playbin);
615 static void emit_about_to_finish (GstPlayBin3 * playbin);
616 static void reconfigure_output (GstPlayBin3 * playbin);
617 static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
618     GstSourceGroup * group);
619 
620 static gint select_stream_cb (GstElement * decodebin,
621     GstStreamCollection * collection, GstStream * stream,
622     GstSourceGroup * group);
623 
624 static void do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group);
625 
626 static GstElementClass *parent_class;
627 
628 static guint gst_play_bin3_signals[LAST_SIGNAL] = { 0 };
629 
630 #define REMOVE_SIGNAL(obj,id)			\
631   if (id) {					\
632     g_signal_handler_disconnect (obj, id);	\
633     id = 0;					\
634   }
635 
636 static void gst_play_bin3_overlay_init (gpointer g_iface,
637     gpointer g_iface_data);
638 static void gst_play_bin3_navigation_init (gpointer g_iface,
639     gpointer g_iface_data);
640 static void gst_play_bin3_colorbalance_init (gpointer g_iface,
641     gpointer g_iface_data);
642 
643 static GType
gst_play_bin3_get_type(void)644 gst_play_bin3_get_type (void)
645 {
646   static GType gst_play_bin3_type = 0;
647 
648   if (!gst_play_bin3_type) {
649     static const GTypeInfo gst_play_bin3_info = {
650       sizeof (GstPlayBin3Class),
651       NULL,
652       NULL,
653       (GClassInitFunc) gst_play_bin3_class_init,
654       NULL,
655       NULL,
656       sizeof (GstPlayBin3),
657       0,
658       (GInstanceInitFunc) gst_play_bin3_init,
659       NULL
660     };
661     static const GInterfaceInfo svol_info = {
662       NULL, NULL, NULL
663     };
664     static const GInterfaceInfo ov_info = {
665       gst_play_bin3_overlay_init,
666       NULL, NULL
667     };
668     static const GInterfaceInfo nav_info = {
669       gst_play_bin3_navigation_init,
670       NULL, NULL
671     };
672     static const GInterfaceInfo col_info = {
673       gst_play_bin3_colorbalance_init,
674       NULL, NULL
675     };
676 
677     gst_play_bin3_type = g_type_register_static (GST_TYPE_PIPELINE,
678         "GstPlayBin3", &gst_play_bin3_info, 0);
679 
680     g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_STREAM_VOLUME,
681         &svol_info);
682     g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_VIDEO_OVERLAY,
683         &ov_info);
684     g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_NAVIGATION,
685         &nav_info);
686     g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_COLOR_BALANCE,
687         &col_info);
688   }
689 
690   return gst_play_bin3_type;
691 }
692 
693 static void
gst_play_bin3_class_init(GstPlayBin3Class * klass)694 gst_play_bin3_class_init (GstPlayBin3Class * klass)
695 {
696   GObjectClass *gobject_klass;
697   GstElementClass *gstelement_klass;
698   GstBinClass *gstbin_klass;
699 
700   gobject_klass = (GObjectClass *) klass;
701   gstelement_klass = (GstElementClass *) klass;
702   gstbin_klass = (GstBinClass *) klass;
703 
704   parent_class = g_type_class_peek_parent (klass);
705 
706   gobject_klass->set_property = gst_play_bin3_set_property;
707   gobject_klass->get_property = gst_play_bin3_get_property;
708 
709   gobject_klass->finalize = gst_play_bin3_finalize;
710 
711   /**
712    * GstPlayBin3:uri
713    *
714    * Set the next URI that playbin will play. This property can be set from the
715    * about-to-finish signal to queue the next media file.
716    */
717   g_object_class_install_property (gobject_klass, PROP_URI,
718       g_param_spec_string ("uri", "URI", "URI of the media to play",
719           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
720 
721   /**
722    * GstPlayBin3:current-uri
723    *
724    * The currently playing uri.
725    */
726   g_object_class_install_property (gobject_klass, PROP_CURRENT_URI,
727       g_param_spec_string ("current-uri", "Current URI",
728           "The currently playing URI", NULL,
729           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
730 
731   /**
732    * GstPlayBin3:suburi
733    *
734    * Set the next subtitle URI that playbin will play. This property can be
735    * set from the about-to-finish signal to queue the next subtitle media file.
736    */
737   g_object_class_install_property (gobject_klass, PROP_SUBURI,
738       g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
739           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
740 
741   /**
742    * GstPlayBin3:current-suburi
743    *
744    * The currently playing subtitle uri.
745    */
746   g_object_class_install_property (gobject_klass, PROP_CURRENT_SUBURI,
747       g_param_spec_string ("current-suburi", "Current .sub-URI",
748           "The currently playing URI of a subtitle",
749           NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
750 
751   /**
752    * GstPlayBin3:flags
753    *
754    * Control the behaviour of playbin.
755    */
756   g_object_class_install_property (gobject_klass, PROP_FLAGS,
757       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
758           GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
759           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
760 
761   g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
762       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
763           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
764           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
765           "be checked for an encoding to use. If that is not set either, "
766           "ISO-8859-15 will be assumed.", NULL,
767           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
768 
769   g_object_class_install_property (gobject_klass, PROP_VIDEO_FILTER,
770       g_param_spec_object ("video-filter", "Video filter",
771           "the video filter(s) to apply, if possible",
772           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
773   g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
774       g_param_spec_object ("audio-filter", "Audio filter",
775           "the audio filter(s) to apply, if possible",
776           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
777   /**
778    * GstPlayBin3:video-sink
779    *
780    * Get or set the video sink to use for video output. If set to
781    * NULL, one will be auto-selected. To disable video entirely, unset
782    * the VIDEO flag in the #GstPlayBin3:flags property.
783    *
784    */
785   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
786       g_param_spec_object ("video-sink", "Video Sink",
787           "the video output element to use (NULL = default sink)",
788           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
789   /**
790    * GstPlayBin3:audio-sink
791    *
792    * Get or set the audio sink to use for audio output. If set to
793    * NULL, one will be auto-selected. To disable audio entirely, unset
794    * the AUDIO flag in the #GstPlayBin3:flags property.
795    *
796    */
797   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
798       g_param_spec_object ("audio-sink", "Audio Sink",
799           "the audio output element to use (NULL = default sink)",
800           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
801   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
802       g_param_spec_object ("vis-plugin", "Vis plugin",
803           "the visualization element to use (NULL = default)",
804           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
805   g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
806       g_param_spec_object ("text-sink", "Text plugin",
807           "the text output element to use (NULL = default subtitleoverlay)",
808           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
809   /**
810    * GstPlayBin3:video-stream-combiner
811    *
812    * Get or set the current video stream combiner. By default, no
813    * element is used and the selected stream is used directly.
814    */
815   g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
816       g_param_spec_object ("video-stream-combiner", "Video stream combiner",
817           "Current video stream combiner (default: none)",
818           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
819 
820   /**
821    * GstPlayBin3:audio-stream-combiner
822    *
823    * Get or set the current audio stream combiner. By default, no
824    * element is used and the selected stream is used directly.
825    */
826   g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
827       g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
828           "Current audio stream combiner (default: none))",
829           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
830 
831   /**
832    * GstPlayBin3:text-stream-combiner
833    *
834    * Get or set the current text stream combiner. By default, no
835    * element is used and the selected stream is used directly.
836    */
837   g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
838       g_param_spec_object ("text-stream-combiner", "Text stream combiner",
839           "Current text stream combiner (default: none)",
840           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
841 
842   /**
843    * GstPlayBin3:volume:
844    *
845    * Get or set the current audio stream volume. 1.0 means 100%,
846    * 0.0 means mute. This uses a linear volume scale.
847    *
848    */
849   g_object_class_install_property (gobject_klass, PROP_VOLUME,
850       g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
851           0.0, VOLUME_MAX_DOUBLE, 1.0,
852           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
853   g_object_class_install_property (gobject_klass, PROP_MUTE,
854       g_param_spec_boolean ("mute", "Mute",
855           "Mute the audio channel without changing the volume", FALSE,
856           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
857 
858   /**
859    * GstPlayBin3:sample:
860    * @playbin: a #GstPlayBin3
861    *
862    * Get the currently rendered or prerolled sample in the video sink.
863    * The #GstCaps in the sample will describe the format of the buffer.
864    */
865   g_object_class_install_property (gobject_klass, PROP_SAMPLE,
866       g_param_spec_boxed ("sample", "Sample",
867           "The last sample (NULL = no video available)",
868           GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
869 
870   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
871       g_param_spec_string ("subtitle-font-desc",
872           "Subtitle font description",
873           "Pango font description of font "
874           "to be used for subtitle rendering", NULL,
875           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
876 
877   g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
878       g_param_spec_uint64 ("connection-speed", "Connection Speed",
879           "Network connection speed in kbps (0 = unknown)",
880           0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
881           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
882 
883   g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
884       g_param_spec_int ("buffer-size", "Buffer size (bytes)",
885           "Buffer size when buffering network streams",
886           -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
887           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
888   g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
889       g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
890           "Buffer duration when buffering network streams",
891           -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
892           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
893   /**
894    * GstPlayBin3:av-offset:
895    *
896    * Control the synchronisation offset between the audio and video streams.
897    * Positive values make the audio ahead of the video and negative values make
898    * the audio go behind the video.
899    */
900   g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
901       g_param_spec_int64 ("av-offset", "AV Offset",
902           "The synchronisation offset between audio and video in nanoseconds",
903           G_MININT64, G_MAXINT64, 0,
904           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
905   /**
906    * GstPlayBin3:text-offset:
907    *
908    * Control the synchronisation offset between the text and video streams.
909    * Positive values make the text ahead of the video and negative values make
910    * the text go behind the video.
911    */
912   g_object_class_install_property (gobject_klass, PROP_TEXT_OFFSET,
913       g_param_spec_int64 ("text-offset", "Text Offset",
914           "The synchronisation offset between text and video in nanoseconds",
915           G_MININT64, G_MAXINT64, 0,
916           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
917 
918   /**
919    * GstPlayBin3:ring-buffer-max-size
920    *
921    * The maximum size of the ring buffer in bytes. If set to 0, the ring
922    * buffer is disabled. Default 0.
923    */
924   g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
925       g_param_spec_uint64 ("ring-buffer-max-size",
926           "Max. ring buffer size (bytes)",
927           "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
928           0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
929           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
930 
931   /**
932    * GstPlayBin3::force-aspect-ratio:
933    *
934    * Requests the video sink to enforce the video display aspect ratio.
935    */
936   g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
937       g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
938           "When enabled, scaling will respect original aspect ratio", TRUE,
939           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
940 
941   /**
942    * GstPlayBin3::video-multiview-mode:
943    *
944    * Set the stereoscopic mode for video streams that don't contain
945    * any information in the stream, so they can be correctly played
946    * as 3D streams. If a video already has multiview information
947    * encoded, this property can override other modes in the set,
948    * but cannot be used to re-interpret MVC or mixed-mono streams.
949    *
950    * See Also: The #GstPlayBin3::video-multiview-flags property
951    *
952    */
953   g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_MODE,
954       g_param_spec_enum ("video-multiview-mode",
955           "Multiview Mode Override",
956           "Re-interpret a video stream as one of several frame-packed stereoscopic modes.",
957           GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
958           GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE,
959           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
960 
961   /**
962    * GstPlayBin3::video-multiview-flags:
963    *
964    * When overriding the multiview mode of an input stream,
965    * these flags modify details of the view layout.
966    *
967    * See Also: The #GstPlayBin3::video-multiview-mode property
968    */
969   g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_FLAGS,
970       g_param_spec_flags ("video-multiview-flags",
971           "Multiview Flags Override",
972           "Override details of the multiview frame layout",
973           GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
974           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
975 
976   /**
977    * GstPlayBin3::about-to-finish
978    * @playbin: a #GstPlayBin3
979    *
980    * This signal is emitted when the current uri is about to finish. You can
981    * set the uri and suburi to make sure that playback continues.
982    *
983    * This signal is emitted from the context of a GStreamer streaming thread.
984    */
985   gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
986       g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
987       G_SIGNAL_RUN_LAST,
988       G_STRUCT_OFFSET (GstPlayBin3Class, about_to_finish), NULL, NULL,
989       g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
990 
991 
992   /**
993    * GstPlayBin3::source-setup:
994    * @playbin: a #GstPlayBin3
995    * @source: source element
996    *
997    * This signal is emitted after the source element has been created, so
998    * it can be configured by setting additional properties (e.g. set a
999    * proxy server for an http source, or set the device and read speed for
1000    * an audio cd source). This is functionally equivalent to connecting to
1001    * the notify::source signal, but more convenient.
1002    *
1003    * This signal is usually emitted from the context of a GStreamer streaming
1004    * thread.
1005    */
1006   gst_play_bin3_signals[SIGNAL_SOURCE_SETUP] =
1007       g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
1008       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1009       g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1010 
1011   /**
1012    * GstPlayBin3::element-setup:
1013    * @playbin: a #GstPlayBin3
1014    * @element: an element that was added to the playbin hierarchy
1015    *
1016    * This signal is emitted when a new element is added to playbin or any of
1017    * its sub-bins. This signal can be used to configure elements, e.g. to set
1018    * properties on decoders. This is functionally equivalent to connecting to
1019    * the deep-element-added signal, but more convenient.
1020    *
1021    * This signal is usually emitted from the context of a GStreamer streaming
1022    * thread, so might be called at the same time as code running in the main
1023    * application thread.
1024    *
1025    * Since: 1.10
1026    */
1027   gst_play_bin3_signals[SIGNAL_ELEMENT_SETUP] =
1028       g_signal_new ("element-setup", G_TYPE_FROM_CLASS (klass),
1029       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1030       g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1031 
1032   /**
1033    * GstPlayBin3::convert-sample
1034    * @playbin: a #GstPlayBin3
1035    * @caps: the target format of the frame
1036    *
1037    * Action signal to retrieve the currently playing video frame in the format
1038    * specified by @caps.
1039    * If @caps is %NULL, no conversion will be performed and this function is
1040    * equivalent to the #GstPlayBin3::sample property.
1041    *
1042    * Returns: a #GstSample of the current video frame converted to #caps.
1043    * The caps on the sample will describe the final layout of the buffer data.
1044    * %NULL is returned when no current buffer can be retrieved or when the
1045    * conversion failed.
1046    */
1047   gst_play_bin3_signals[SIGNAL_CONVERT_SAMPLE] =
1048       g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
1049       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1050       G_STRUCT_OFFSET (GstPlayBin3Class, convert_sample), NULL, NULL,
1051       g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
1052 
1053   klass->convert_sample = gst_play_bin3_convert_sample;
1054 
1055   gst_element_class_set_static_metadata (gstelement_klass,
1056       "Player Bin 3", "Generic/Bin/Player",
1057       "Autoplug and play media from an uri",
1058       "Wim Taymans <wim.taymans@gmail.com>");
1059 
1060   gstelement_klass->change_state =
1061       GST_DEBUG_FUNCPTR (gst_play_bin3_change_state);
1062   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin3_send_event);
1063 
1064   gstbin_klass->handle_message =
1065       GST_DEBUG_FUNCPTR (gst_play_bin3_handle_message);
1066   gstbin_klass->deep_element_added =
1067       GST_DEBUG_FUNCPTR (gst_play_bin3_deep_element_added);
1068 }
1069 
1070 static void
do_async_start(GstPlayBin3 * playbin)1071 do_async_start (GstPlayBin3 * playbin)
1072 {
1073   GstMessage *message;
1074 
1075   playbin->async_pending = TRUE;
1076 
1077   message = gst_message_new_async_start (GST_OBJECT_CAST (playbin));
1078   GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1079       message);
1080 }
1081 
1082 static void
do_async_done(GstPlayBin3 * playbin)1083 do_async_done (GstPlayBin3 * playbin)
1084 {
1085   GstMessage *message;
1086 
1087   if (playbin->async_pending) {
1088     GST_DEBUG_OBJECT (playbin, "posting ASYNC_DONE");
1089     message =
1090         gst_message_new_async_done (GST_OBJECT_CAST (playbin),
1091         GST_CLOCK_TIME_NONE);
1092     GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1093         message);
1094 
1095     playbin->async_pending = FALSE;
1096   }
1097 }
1098 
1099 /* init combiners. The combiner is found by finding the first prefix that
1100  * matches the media. */
1101 static void
init_combiners(GstPlayBin3 * playbin)1102 init_combiners (GstPlayBin3 * playbin)
1103 {
1104   gint i;
1105 
1106   /* store the array for the different channels */
1107   for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
1108     playbin->channels[i] = g_ptr_array_new ();
1109 
1110   playbin->combiner[PLAYBIN_STREAM_AUDIO].media_type = "audio";
1111   playbin->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
1112   playbin->combiner[PLAYBIN_STREAM_AUDIO].stream_type = GST_STREAM_TYPE_AUDIO;
1113   playbin->combiner[PLAYBIN_STREAM_AUDIO].channels = playbin->channels[0];
1114   playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
1115       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1116 
1117   playbin->combiner[PLAYBIN_STREAM_VIDEO].media_type = "video";
1118   playbin->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
1119   playbin->combiner[PLAYBIN_STREAM_VIDEO].stream_type = GST_STREAM_TYPE_VIDEO;
1120   playbin->combiner[PLAYBIN_STREAM_VIDEO].channels = playbin->channels[1];
1121   playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
1122       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1123 
1124   playbin->combiner[PLAYBIN_STREAM_TEXT].media_type = "text";
1125   playbin->combiner[PLAYBIN_STREAM_TEXT].get_media_caps =
1126       gst_subtitle_overlay_create_factory_caps;
1127   playbin->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
1128   playbin->combiner[PLAYBIN_STREAM_TEXT].stream_type = GST_STREAM_TYPE_TEXT;
1129   playbin->combiner[PLAYBIN_STREAM_TEXT].channels = playbin->channels[2];
1130   playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
1131       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1132 }
1133 
1134 /* Update the combiner information to be in sync with the current collection
1135  *
1136  * FIXME : "current" collection doesn't mean anything until we have a "combined"
1137  *  collection of all groups */
1138 static void
update_combiner_info(GstPlayBin3 * playbin,GstStreamCollection * collection)1139 update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection)
1140 {
1141   guint i, len;
1142 
1143   if (collection == NULL)
1144     return;
1145 
1146   GST_DEBUG_OBJECT (playbin, "Updating combiner info");
1147 
1148   /* Wipe current combiner streams */
1149   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams, TRUE);
1150   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE);
1151   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
1152   playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
1153       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1154   playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
1155       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1156   playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
1157       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1158 
1159   len = gst_stream_collection_get_size (collection);
1160   for (i = 0; i < len; i++) {
1161     GstStream *stream = gst_stream_collection_get_stream (collection, i);
1162     GstStreamType stype = gst_stream_get_stream_type (stream);
1163 
1164     if (stype & GST_STREAM_TYPE_AUDIO) {
1165       g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams,
1166           gst_object_ref (stream));
1167     } else if (stype & GST_STREAM_TYPE_VIDEO) {
1168       g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams,
1169           gst_object_ref (stream));
1170     } else if (stype & GST_STREAM_TYPE_TEXT) {
1171       g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_TEXT].streams,
1172           gst_object_ref (stream));
1173     }
1174   }
1175 
1176   GST_DEBUG_OBJECT (playbin, "There are %d audio streams",
1177       playbin->combiner[PLAYBIN_STREAM_AUDIO].streams->len);
1178   GST_DEBUG_OBJECT (playbin, "There are %d video streams",
1179       playbin->combiner[PLAYBIN_STREAM_VIDEO].streams->len);
1180   GST_DEBUG_OBJECT (playbin, "There are %d text streams",
1181       playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len);
1182 }
1183 
1184 #ifndef GST_DISABLE_GST_DEBUG
1185 #define debug_groups(playbin) G_STMT_START {	\
1186     guint i;					\
1187     						\
1188     for (i = 0; i < 2; i++) {				\
1189       GstSourceGroup *group = &playbin->groups[i];	\
1190       							\
1191       GST_DEBUG ("GstSourceGroup #%d (%s)", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused"); \
1192       GST_DEBUG ("  valid:%d , active:%d , playing:%d", group->valid, group->active, group->playing); \
1193       GST_DEBUG ("  uri:%s", group->uri);				\
1194       GST_DEBUG ("  suburi:%s", group->suburi);				\
1195       GST_DEBUG ("  group_id:%d", group->group_id);			\
1196       GST_DEBUG ("  pending_about_to_finish:%d", group->pending_about_to_finish); \
1197     }									\
1198   } G_STMT_END
1199 #else
1200 #define debug_groups(p) {}
1201 #endif
1202 
1203 static void
init_group(GstPlayBin3 * playbin,GstSourceGroup * group)1204 init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
1205 {
1206   g_mutex_init (&group->lock);
1207 
1208   group->stream_changed_pending = FALSE;
1209   group->group_id = GST_GROUP_ID_INVALID;
1210 
1211   group->playbin = playbin;
1212 }
1213 
1214 static void
free_group(GstPlayBin3 * playbin,GstSourceGroup * group)1215 free_group (GstPlayBin3 * playbin, GstSourceGroup * group)
1216 {
1217   g_free (group->uri);
1218   g_free (group->suburi);
1219 
1220   g_mutex_clear (&group->lock);
1221   group->stream_changed_pending = FALSE;
1222 
1223   if (group->pending_buffering_msg)
1224     gst_message_unref (group->pending_buffering_msg);
1225   group->pending_buffering_msg = NULL;
1226 
1227   gst_object_replace ((GstObject **) & group->collection, NULL);
1228 
1229   gst_object_replace ((GstObject **) & group->audio_sink, NULL);
1230   gst_object_replace ((GstObject **) & group->video_sink, NULL);
1231   gst_object_replace ((GstObject **) & group->text_sink, NULL);
1232 }
1233 
1234 static void
notify_volume_cb(GObject * combiner,GParamSpec * pspec,GstPlayBin3 * playbin)1235 notify_volume_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin3 * playbin)
1236 {
1237   g_object_notify (G_OBJECT (playbin), "volume");
1238 }
1239 
1240 static void
notify_mute_cb(GObject * combiner,GParamSpec * pspec,GstPlayBin3 * playbin)1241 notify_mute_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin3 * playbin)
1242 {
1243   g_object_notify (G_OBJECT (playbin), "mute");
1244 }
1245 
1246 static void
colorbalance_value_changed_cb(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value,GstPlayBin3 * playbin)1247 colorbalance_value_changed_cb (GstColorBalance * balance,
1248     GstColorBalanceChannel * channel, gint value, GstPlayBin3 * playbin)
1249 {
1250   gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
1251 }
1252 
1253 #if 0                           /* AUTOPLUG DISABLED */
1254 static gint
1255 compare_factories_func (gconstpointer p1, gconstpointer p2)
1256 {
1257   GstPluginFeature *f1, *f2;
1258   gboolean is_sink1, is_sink2;
1259   gboolean is_parser1, is_parser2;
1260 
1261   f1 = (GstPluginFeature *) p1;
1262   f2 = (GstPluginFeature *) p2;
1263 
1264   is_sink1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1265       GST_ELEMENT_FACTORY_TYPE_SINK);
1266   is_sink2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1267       GST_ELEMENT_FACTORY_TYPE_SINK);
1268   is_parser1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1269       GST_ELEMENT_FACTORY_TYPE_PARSER);
1270   is_parser2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1271       GST_ELEMENT_FACTORY_TYPE_PARSER);
1272 
1273   /* First we want all sinks as we prefer a sink if it directly
1274    * supports the current caps */
1275   if (is_sink1 && !is_sink2)
1276     return -1;
1277   else if (!is_sink1 && is_sink2)
1278     return 1;
1279 
1280   /* Then we want all parsers as we always want to plug parsers
1281    * before decoders */
1282   if (is_parser1 && !is_parser2)
1283     return -1;
1284   else if (!is_parser1 && is_parser2)
1285     return 1;
1286 
1287   /* And if it's a both a parser or sink we first sort by rank
1288    * and then by factory name */
1289   return gst_plugin_feature_rank_compare_func (p1, p2);
1290 }
1291 
1292 /* Must be called with elements lock! */
1293 static void
1294 gst_play_bin3_update_elements_list (GstPlayBin3 * playbin)
1295 {
1296   GList *res, *tmp;
1297   guint cookie;
1298 
1299   cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
1300 
1301   if (!playbin->elements || playbin->elements_cookie != cookie) {
1302     if (playbin->elements)
1303       gst_plugin_feature_list_free (playbin->elements);
1304     res =
1305         gst_element_factory_list_get_elements
1306         (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1307     tmp =
1308         gst_element_factory_list_get_elements
1309         (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
1310     playbin->elements = g_list_concat (res, tmp);
1311     playbin->elements = g_list_sort (playbin->elements, compare_factories_func);
1312   }
1313 
1314   if (!playbin->aelements || playbin->elements_cookie != cookie) {
1315     if (playbin->aelements)
1316       g_sequence_free (playbin->aelements);
1317     playbin->aelements = avelements_create (playbin, TRUE);
1318   }
1319 
1320   if (!playbin->velements || playbin->elements_cookie != cookie) {
1321     if (playbin->velements)
1322       g_sequence_free (playbin->velements);
1323     playbin->velements = avelements_create (playbin, FALSE);
1324   }
1325 
1326   playbin->elements_cookie = cookie;
1327 }
1328 #endif
1329 
1330 static void
gst_play_bin3_init(GstPlayBin3 * playbin)1331 gst_play_bin3_init (GstPlayBin3 * playbin)
1332 {
1333   g_rec_mutex_init (&playbin->lock);
1334   g_mutex_init (&playbin->dyn_lock);
1335 
1336   /* assume we can create an input-selector */
1337   playbin->have_selector = TRUE;
1338 
1339   init_combiners (playbin);
1340 
1341   /* init groups */
1342   playbin->curr_group = &playbin->groups[0];
1343   playbin->next_group = &playbin->groups[1];
1344   init_group (playbin, &playbin->groups[0]);
1345   init_group (playbin, &playbin->groups[1]);
1346 
1347   /* first filter out the interesting element factories */
1348   g_mutex_init (&playbin->elements_lock);
1349 
1350   g_rec_mutex_init (&playbin->activation_lock);
1351 
1352   /* add sink */
1353   playbin->playsink =
1354       g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
1355       1, NULL);
1356   gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
1357   gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1358   /* Connect to notify::volume and notify::mute signals for proxying */
1359   g_signal_connect (playbin->playsink, "notify::volume",
1360       G_CALLBACK (notify_volume_cb), playbin);
1361   g_signal_connect (playbin->playsink, "notify::mute",
1362       G_CALLBACK (notify_mute_cb), playbin);
1363   g_signal_connect (playbin->playsink, "value-changed",
1364       G_CALLBACK (colorbalance_value_changed_cb), playbin);
1365 
1366   playbin->current_video = DEFAULT_CURRENT_VIDEO;
1367   playbin->current_audio = DEFAULT_CURRENT_AUDIO;
1368   playbin->current_text = DEFAULT_CURRENT_TEXT;
1369 
1370   playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
1371   playbin->buffer_size = DEFAULT_BUFFER_SIZE;
1372   playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
1373 
1374   playbin->force_aspect_ratio = TRUE;
1375 
1376   playbin->multiview_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
1377   playbin->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1378 }
1379 
1380 static void
gst_play_bin3_finalize(GObject * object)1381 gst_play_bin3_finalize (GObject * object)
1382 {
1383   GstPlayBin3 *playbin;
1384   gint i;
1385 
1386   playbin = GST_PLAY_BIN3 (object);
1387 
1388   free_group (playbin, &playbin->groups[0]);
1389   free_group (playbin, &playbin->groups[1]);
1390 
1391   for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
1392     g_ptr_array_free (playbin->channels[i], TRUE);
1393 
1394   /* Setting states to NULL is safe here because playsink
1395    * will already be gone and none of these sinks will be
1396    * a child of playsink
1397    */
1398   if (playbin->video_sink) {
1399     gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
1400     gst_object_unref (playbin->video_sink);
1401   }
1402   if (playbin->audio_sink) {
1403     gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
1404     gst_object_unref (playbin->audio_sink);
1405   }
1406   if (playbin->text_sink) {
1407     gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
1408     gst_object_unref (playbin->text_sink);
1409   }
1410 
1411   if (playbin->video_stream_combiner) {
1412     gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
1413     gst_object_unref (playbin->video_stream_combiner);
1414   }
1415   if (playbin->audio_stream_combiner) {
1416     gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
1417     gst_object_unref (playbin->audio_stream_combiner);
1418   }
1419   if (playbin->text_stream_combiner) {
1420     gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
1421     gst_object_unref (playbin->text_stream_combiner);
1422   }
1423 
1424   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams, TRUE);
1425   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE);
1426   g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
1427 
1428   if (playbin->elements)
1429     gst_plugin_feature_list_free (playbin->elements);
1430 
1431   if (playbin->aelements)
1432     g_sequence_free (playbin->aelements);
1433 
1434   if (playbin->velements)
1435     g_sequence_free (playbin->velements);
1436 
1437   g_rec_mutex_clear (&playbin->activation_lock);
1438   g_rec_mutex_clear (&playbin->lock);
1439   g_mutex_clear (&playbin->dyn_lock);
1440   g_mutex_clear (&playbin->elements_lock);
1441 
1442   G_OBJECT_CLASS (parent_class)->finalize (object);
1443 }
1444 
1445 static gboolean
gst_playbin_uri_is_valid(GstPlayBin3 * playbin,const gchar * uri)1446 gst_playbin_uri_is_valid (GstPlayBin3 * playbin, const gchar * uri)
1447 {
1448   const gchar *c;
1449 
1450   GST_LOG_OBJECT (playbin, "checking uri '%s'", uri);
1451 
1452   /* this just checks the protocol */
1453   if (!gst_uri_is_valid (uri))
1454     return FALSE;
1455 
1456   for (c = uri; *c != '\0'; ++c) {
1457     if (!g_ascii_isprint (*c))
1458       goto invalid;
1459     if (*c == ' ')
1460       goto invalid;
1461   }
1462 
1463   return TRUE;
1464 
1465 invalid:
1466   {
1467     GST_WARNING_OBJECT (playbin, "uri '%s' not valid, character #%u",
1468         uri, (guint) ((guintptr) c - (guintptr) uri));
1469     return FALSE;
1470   }
1471 }
1472 
1473 static void
gst_play_bin3_set_uri(GstPlayBin3 * playbin,const gchar * uri)1474 gst_play_bin3_set_uri (GstPlayBin3 * playbin, const gchar * uri)
1475 {
1476   GstSourceGroup *group;
1477 
1478   if (uri == NULL) {
1479     g_warning ("cannot set NULL uri");
1480     return;
1481   }
1482 
1483   if (!gst_playbin_uri_is_valid (playbin, uri)) {
1484     if (g_str_has_prefix (uri, "file:")) {
1485       GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
1486           "sure to escape spaces and non-ASCII characters properly and specify "
1487           "an absolute path. Use gst_filename_to_uri() to convert filenames "
1488           "to URIs", uri);
1489     } else {
1490       /* GST_ERROR_OBJECT (playbin, "malformed URI '%s'", uri); */
1491     }
1492   }
1493 
1494   GST_PLAY_BIN3_LOCK (playbin);
1495   group = playbin->next_group;
1496 
1497   GST_SOURCE_GROUP_LOCK (group);
1498   /* store the uri in the next group we will play */
1499   g_free (group->uri);
1500   group->uri = g_strdup (uri);
1501   group->valid = TRUE;
1502   GST_SOURCE_GROUP_UNLOCK (group);
1503 
1504   GST_DEBUG ("set new uri to %s", uri);
1505   GST_PLAY_BIN3_UNLOCK (playbin);
1506 }
1507 
1508 static void
gst_play_bin3_set_suburi(GstPlayBin3 * playbin,const gchar * suburi)1509 gst_play_bin3_set_suburi (GstPlayBin3 * playbin, const gchar * suburi)
1510 {
1511   GstSourceGroup *group;
1512 
1513   GST_PLAY_BIN3_LOCK (playbin);
1514   group = playbin->next_group;
1515 
1516   GST_SOURCE_GROUP_LOCK (group);
1517   g_free (group->suburi);
1518   group->suburi = g_strdup (suburi);
1519   GST_SOURCE_GROUP_UNLOCK (group);
1520 
1521   GST_DEBUG ("setting new .sub uri to %s", suburi);
1522 
1523   GST_PLAY_BIN3_UNLOCK (playbin);
1524 }
1525 
1526 static void
gst_play_bin3_set_flags(GstPlayBin3 * playbin,GstPlayFlags flags)1527 gst_play_bin3_set_flags (GstPlayBin3 * playbin, GstPlayFlags flags)
1528 {
1529   GstPlayFlags old_flags;
1530   old_flags = gst_play_sink_get_flags (playbin->playsink);
1531 
1532   if (flags != old_flags) {
1533     gst_play_sink_set_flags (playbin->playsink, flags);
1534     gst_play_sink_reconfigure (playbin->playsink);
1535   }
1536 }
1537 
1538 static GstPlayFlags
gst_play_bin3_get_flags(GstPlayBin3 * playbin)1539 gst_play_bin3_get_flags (GstPlayBin3 * playbin)
1540 {
1541   GstPlayFlags flags;
1542 
1543   flags = gst_play_sink_get_flags (playbin->playsink);
1544 
1545   return flags;
1546 }
1547 
1548 /* get the currently playing group or if nothing is playing, the next
1549  * group. Must be called with the PLAY_BIN_LOCK. */
1550 static GstSourceGroup *
get_group(GstPlayBin3 * playbin)1551 get_group (GstPlayBin3 * playbin)
1552 {
1553   GstSourceGroup *result;
1554 
1555   if (!(result = playbin->curr_group))
1556     result = playbin->next_group;
1557 
1558   return result;
1559 }
1560 
1561 
1562 static GstSample *
gst_play_bin3_convert_sample(GstPlayBin3 * playbin,GstCaps * caps)1563 gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps)
1564 {
1565   return gst_play_sink_convert_sample (playbin->playsink, caps);
1566 }
1567 
1568 static gboolean
gst_play_bin3_send_custom_event(GstObject * combiner,const gchar * event_name)1569 gst_play_bin3_send_custom_event (GstObject * combiner, const gchar * event_name)
1570 {
1571   GstPad *src;
1572   GstPad *peer;
1573   GstStructure *s;
1574   GstEvent *event;
1575   gboolean ret = FALSE;
1576 
1577   src = gst_element_get_static_pad (GST_ELEMENT_CAST (combiner), "src");
1578   peer = gst_pad_get_peer (src);
1579   if (peer) {
1580     s = gst_structure_new_empty (event_name);
1581     event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
1582     gst_pad_send_event (peer, event);
1583     gst_object_unref (peer);
1584     ret = TRUE;
1585   }
1586   gst_object_unref (src);
1587   return ret;
1588 }
1589 
1590 static gboolean
gst_play_bin3_set_current_stream(GstPlayBin3 * playbin,gint stream_type,gint * current_value,gint stream,gboolean * flush_marker)1591 gst_play_bin3_set_current_stream (GstPlayBin3 * playbin,
1592     gint stream_type, gint * current_value, gint stream,
1593     gboolean * flush_marker)
1594 {
1595   GstSourceCombine *combine;
1596   GPtrArray *channels;
1597   GstPad *sinkpad;
1598 
1599   GST_PLAY_BIN3_LOCK (playbin);
1600   /* This function is only called if the app sets
1601    * one of the current-* properties, which means it doesn't
1602    * handle collections or select-streams yet */
1603   playbin->do_stream_selections = TRUE;
1604 
1605   combine = playbin->combiner + stream_type;
1606   channels = playbin->channels[stream_type];
1607 
1608   GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
1609       stream_type_names[stream_type], *current_value, stream);
1610 
1611   if (combine->combiner == NULL || combine->is_concat) {
1612     /* FIXME: Check that the current_value is within range */
1613     *current_value = stream;
1614     do_stream_selection (playbin, playbin->curr_group);
1615     GST_PLAY_BIN3_UNLOCK (playbin);
1616     return TRUE;
1617   }
1618 
1619   GST_DEBUG_OBJECT (playbin, "Using old style combiner");
1620 
1621   if (!combine->has_active_pad)
1622     goto no_active_pad;
1623   if (channels == NULL)
1624     goto no_channels;
1625 
1626   if (stream == -1 || channels->len <= stream) {
1627     sinkpad = NULL;
1628   } else {
1629     /* take channel from selected stream */
1630     sinkpad = g_ptr_array_index (channels, stream);
1631   }
1632 
1633   if (sinkpad)
1634     gst_object_ref (sinkpad);
1635   GST_PLAY_BIN3_UNLOCK (playbin);
1636 
1637   if (sinkpad) {
1638     GstObject *combiner;
1639 
1640     if ((combiner = gst_pad_get_parent (sinkpad))) {
1641       GstPad *old_sinkpad;
1642 
1643       g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
1644 
1645       if (old_sinkpad != sinkpad) {
1646         /* FIXME: Is there actually any reason playsink
1647          * needs special names for each type of stream we flush? */
1648         gchar *flush_event_name = g_strdup_printf ("playsink-custom-%s-flush",
1649             stream_type_names[stream_type]);
1650         if (gst_play_bin3_send_custom_event (combiner, flush_event_name))
1651           *flush_marker = TRUE;
1652         g_free (flush_event_name);
1653 
1654         /* activate the selected pad */
1655         g_object_set (combiner, "active-pad", sinkpad, NULL);
1656       }
1657 
1658       if (old_sinkpad)
1659         gst_object_unref (old_sinkpad);
1660 
1661       gst_object_unref (combiner);
1662     }
1663     gst_object_unref (sinkpad);
1664   }
1665   return TRUE;
1666 
1667 no_active_pad:
1668   {
1669     GST_PLAY_BIN3_UNLOCK (playbin);
1670     GST_WARNING_OBJECT (playbin,
1671         "can't switch %s, the stream combiner's sink pads don't have the \"active-pad\" property",
1672         stream_type_names[stream_type]);
1673     return FALSE;
1674   }
1675 no_channels:
1676   {
1677     GST_PLAY_BIN3_UNLOCK (playbin);
1678     GST_DEBUG_OBJECT (playbin, "can't switch video, we have no channels");
1679     return FALSE;
1680   }
1681 }
1682 
1683 static gboolean
gst_play_bin3_set_current_video_stream(GstPlayBin3 * playbin,gint stream)1684 gst_play_bin3_set_current_video_stream (GstPlayBin3 * playbin, gint stream)
1685 {
1686   return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_VIDEO,
1687       &playbin->current_video, stream, &playbin->video_pending_flush_finish);
1688 }
1689 
1690 static gboolean
gst_play_bin3_set_current_audio_stream(GstPlayBin3 * playbin,gint stream)1691 gst_play_bin3_set_current_audio_stream (GstPlayBin3 * playbin, gint stream)
1692 {
1693   return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_AUDIO,
1694       &playbin->current_audio, stream, &playbin->audio_pending_flush_finish);
1695 }
1696 
1697 static gboolean
gst_play_bin3_set_current_text_stream(GstPlayBin3 * playbin,gint stream)1698 gst_play_bin3_set_current_text_stream (GstPlayBin3 * playbin, gint stream)
1699 {
1700   return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_TEXT,
1701       &playbin->current_text, stream, &playbin->text_pending_flush_finish);
1702 }
1703 
1704 
1705 static void
gst_play_bin3_set_sink(GstPlayBin3 * playbin,GstPlaySinkType type,const gchar * dbg,GstElement ** elem,GstElement * sink)1706 gst_play_bin3_set_sink (GstPlayBin3 * playbin, GstPlaySinkType type,
1707     const gchar * dbg, GstElement ** elem, GstElement * sink)
1708 {
1709   GST_INFO_OBJECT (playbin, "Setting %s sink to %" GST_PTR_FORMAT, dbg, sink);
1710 
1711   gst_play_sink_set_sink (playbin->playsink, type, sink);
1712 
1713   if (*elem)
1714     gst_object_unref (*elem);
1715   *elem = sink ? gst_object_ref (sink) : NULL;
1716 }
1717 
1718 static void
gst_play_bin3_set_stream_combiner(GstPlayBin3 * playbin,GstElement ** elem,const gchar * dbg,GstElement * combiner)1719 gst_play_bin3_set_stream_combiner (GstPlayBin3 * playbin, GstElement ** elem,
1720     const gchar * dbg, GstElement * combiner)
1721 {
1722   GST_INFO_OBJECT (playbin, "Setting %s stream combiner to %" GST_PTR_FORMAT,
1723       dbg, combiner);
1724 
1725   GST_PLAY_BIN3_LOCK (playbin);
1726   if (*elem != combiner) {
1727     GstElement *old;
1728 
1729     old = *elem;
1730     if (combiner)
1731       gst_object_ref_sink (combiner);
1732 
1733     *elem = combiner;
1734     if (old)
1735       gst_object_unref (old);
1736   }
1737   GST_LOG_OBJECT (playbin, "%s stream combiner now %" GST_PTR_FORMAT, dbg,
1738       *elem);
1739   GST_PLAY_BIN3_UNLOCK (playbin);
1740 }
1741 
1742 static void
gst_play_bin3_set_encoding(GstPlayBin3 * playbin,const gchar * encoding)1743 gst_play_bin3_set_encoding (GstPlayBin3 * playbin, const gchar * encoding)
1744 {
1745   GST_PLAY_BIN3_LOCK (playbin);
1746   gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
1747   GST_PLAY_BIN3_UNLOCK (playbin);
1748 }
1749 
1750 static void
gst_play_bin3_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1751 gst_play_bin3_set_property (GObject * object, guint prop_id,
1752     const GValue * value, GParamSpec * pspec)
1753 {
1754   GstPlayBin3 *playbin = GST_PLAY_BIN3 (object);
1755 
1756   switch (prop_id) {
1757     case PROP_URI:
1758       gst_play_bin3_set_uri (playbin, g_value_get_string (value));
1759       break;
1760     case PROP_SUBURI:
1761       gst_play_bin3_set_suburi (playbin, g_value_get_string (value));
1762       break;
1763     case PROP_FLAGS:
1764       gst_play_bin3_set_flags (playbin, g_value_get_flags (value));
1765       if (playbin->curr_group) {
1766         GST_SOURCE_GROUP_LOCK (playbin->curr_group);
1767         if (playbin->curr_group->uridecodebin) {
1768           g_object_set (playbin->curr_group->uridecodebin, "download",
1769               (g_value_get_flags (value) & GST_PLAY_FLAG_DOWNLOAD) != 0, NULL);
1770         }
1771         GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
1772       }
1773       break;
1774     case PROP_SUBTITLE_ENCODING:
1775       gst_play_bin3_set_encoding (playbin, g_value_get_string (value));
1776       break;
1777     case PROP_VIDEO_FILTER:
1778       gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
1779           GST_ELEMENT (g_value_get_object (value)));
1780       break;
1781     case PROP_AUDIO_FILTER:
1782       gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
1783           GST_ELEMENT (g_value_get_object (value)));
1784       break;
1785     case PROP_VIDEO_SINK:
1786       gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_VIDEO, "video",
1787           &playbin->video_sink, g_value_get_object (value));
1788       break;
1789     case PROP_AUDIO_SINK:
1790       gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_AUDIO, "audio",
1791           &playbin->audio_sink, g_value_get_object (value));
1792       break;
1793     case PROP_VIS_PLUGIN:
1794       gst_play_sink_set_vis_plugin (playbin->playsink,
1795           g_value_get_object (value));
1796       break;
1797     case PROP_TEXT_SINK:
1798       gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_TEXT, "text",
1799           &playbin->text_sink, g_value_get_object (value));
1800       break;
1801     case PROP_VIDEO_STREAM_COMBINER:
1802       gst_play_bin3_set_stream_combiner (playbin,
1803           &playbin->video_stream_combiner, "video", g_value_get_object (value));
1804       break;
1805     case PROP_AUDIO_STREAM_COMBINER:
1806       gst_play_bin3_set_stream_combiner (playbin,
1807           &playbin->audio_stream_combiner, "audio", g_value_get_object (value));
1808       break;
1809     case PROP_TEXT_STREAM_COMBINER:
1810       gst_play_bin3_set_stream_combiner (playbin,
1811           &playbin->text_stream_combiner, "text", g_value_get_object (value));
1812       break;
1813     case PROP_VOLUME:
1814       gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
1815       break;
1816     case PROP_MUTE:
1817       gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
1818       break;
1819     case PROP_FONT_DESC:
1820       gst_play_sink_set_font_desc (playbin->playsink,
1821           g_value_get_string (value));
1822       break;
1823     case PROP_CONNECTION_SPEED:
1824       GST_PLAY_BIN3_LOCK (playbin);
1825       playbin->connection_speed = g_value_get_uint64 (value) * 1000;
1826       GST_PLAY_BIN3_UNLOCK (playbin);
1827       break;
1828     case PROP_BUFFER_SIZE:
1829       playbin->buffer_size = g_value_get_int (value);
1830       break;
1831     case PROP_BUFFER_DURATION:
1832       playbin->buffer_duration = g_value_get_int64 (value);
1833       break;
1834     case PROP_AV_OFFSET:
1835       gst_play_sink_set_av_offset (playbin->playsink,
1836           g_value_get_int64 (value));
1837       break;
1838     case PROP_TEXT_OFFSET:
1839       gst_play_sink_set_text_offset (playbin->playsink,
1840           g_value_get_int64 (value));
1841       break;
1842     case PROP_RING_BUFFER_MAX_SIZE:
1843       playbin->ring_buffer_max_size = g_value_get_uint64 (value);
1844       if (playbin->curr_group) {
1845         GST_SOURCE_GROUP_LOCK (playbin->curr_group);
1846         if (playbin->curr_group->uridecodebin) {
1847           g_object_set (playbin->curr_group->uridecodebin,
1848               "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
1849         }
1850         GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
1851       }
1852       break;
1853     case PROP_FORCE_ASPECT_RATIO:
1854       g_object_set (playbin->playsink, "force-aspect-ratio",
1855           g_value_get_boolean (value), NULL);
1856       break;
1857     case PROP_MULTIVIEW_MODE:
1858       GST_PLAY_BIN3_LOCK (playbin);
1859       playbin->multiview_mode = g_value_get_enum (value);
1860       GST_PLAY_BIN3_UNLOCK (playbin);
1861       break;
1862     case PROP_MULTIVIEW_FLAGS:
1863       GST_PLAY_BIN3_LOCK (playbin);
1864       playbin->multiview_flags = g_value_get_flags (value);
1865       GST_PLAY_BIN3_UNLOCK (playbin);
1866       break;
1867     default:
1868       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1869       break;
1870   }
1871 }
1872 
1873 static GstElement *
gst_play_bin3_get_current_sink(GstPlayBin3 * playbin,GstElement ** elem,const gchar * dbg,GstPlaySinkType type)1874 gst_play_bin3_get_current_sink (GstPlayBin3 * playbin, GstElement ** elem,
1875     const gchar * dbg, GstPlaySinkType type)
1876 {
1877   GstElement *sink = gst_play_sink_get_sink (playbin->playsink, type);
1878 
1879   GST_LOG_OBJECT (playbin, "play_sink_get_sink() returned %s sink %"
1880       GST_PTR_FORMAT ", the originally set %s sink is %" GST_PTR_FORMAT,
1881       dbg, sink, dbg, *elem);
1882 
1883   if (sink == NULL) {
1884     GST_PLAY_BIN3_LOCK (playbin);
1885     if ((sink = *elem))
1886       gst_object_ref (sink);
1887     GST_PLAY_BIN3_UNLOCK (playbin);
1888   }
1889 
1890   return sink;
1891 }
1892 
1893 static GstElement *
gst_play_bin3_get_current_stream_combiner(GstPlayBin3 * playbin,GstElement ** elem,const gchar * dbg,int stream_type)1894 gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin,
1895     GstElement ** elem, const gchar * dbg, int stream_type)
1896 {
1897   GstElement *combiner;
1898 
1899   GST_PLAY_BIN3_LOCK (playbin);
1900   /* The special concat element should never be returned */
1901   if (playbin->combiner[stream_type].is_concat)
1902     combiner = NULL;
1903   else if ((combiner = playbin->combiner[stream_type].combiner))
1904     gst_object_ref (combiner);
1905   else if ((combiner = *elem))
1906     gst_object_ref (combiner);
1907   GST_PLAY_BIN3_UNLOCK (playbin);
1908 
1909   return combiner;
1910 }
1911 
1912 static void
gst_play_bin3_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1913 gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value,
1914     GParamSpec * pspec)
1915 {
1916   GstPlayBin3 *playbin = GST_PLAY_BIN3 (object);
1917 
1918   switch (prop_id) {
1919     case PROP_URI:
1920     {
1921       GstSourceGroup *group;
1922 
1923       GST_PLAY_BIN3_LOCK (playbin);
1924       group = playbin->next_group;
1925       g_value_set_string (value, group->uri);
1926       GST_PLAY_BIN3_UNLOCK (playbin);
1927       break;
1928     }
1929     case PROP_CURRENT_URI:
1930     {
1931       GstSourceGroup *group;
1932 
1933       GST_PLAY_BIN3_LOCK (playbin);
1934       group = get_group (playbin);
1935       g_value_set_string (value, group->uri);
1936       GST_PLAY_BIN3_UNLOCK (playbin);
1937       break;
1938     }
1939     case PROP_SUBURI:
1940     {
1941       GstSourceGroup *group;
1942 
1943       GST_PLAY_BIN3_LOCK (playbin);
1944       group = playbin->next_group;
1945       g_value_set_string (value, group->suburi);
1946       GST_PLAY_BIN3_UNLOCK (playbin);
1947       break;
1948     }
1949     case PROP_CURRENT_SUBURI:
1950     {
1951       GstSourceGroup *group;
1952 
1953       GST_PLAY_BIN3_LOCK (playbin);
1954       group = get_group (playbin);
1955       g_value_set_string (value, group->suburi);
1956       GST_PLAY_BIN3_UNLOCK (playbin);
1957       break;
1958     }
1959     case PROP_FLAGS:
1960       g_value_set_flags (value, gst_play_bin3_get_flags (playbin));
1961       break;
1962     case PROP_SUBTITLE_ENCODING:
1963       GST_PLAY_BIN3_LOCK (playbin);
1964       g_value_take_string (value,
1965           gst_play_sink_get_subtitle_encoding (playbin->playsink));
1966       GST_PLAY_BIN3_UNLOCK (playbin);
1967       break;
1968     case PROP_VIDEO_FILTER:
1969       g_value_take_object (value,
1970           gst_play_sink_get_filter (playbin->playsink,
1971               GST_PLAY_SINK_TYPE_VIDEO));
1972       break;
1973     case PROP_AUDIO_FILTER:
1974       g_value_take_object (value,
1975           gst_play_sink_get_filter (playbin->playsink,
1976               GST_PLAY_SINK_TYPE_AUDIO));
1977       break;
1978     case PROP_VIDEO_SINK:
1979       g_value_take_object (value,
1980           gst_play_bin3_get_current_sink (playbin, &playbin->video_sink,
1981               "video", GST_PLAY_SINK_TYPE_VIDEO));
1982       break;
1983     case PROP_AUDIO_SINK:
1984       g_value_take_object (value,
1985           gst_play_bin3_get_current_sink (playbin, &playbin->audio_sink,
1986               "audio", GST_PLAY_SINK_TYPE_AUDIO));
1987       break;
1988     case PROP_VIS_PLUGIN:
1989       g_value_take_object (value,
1990           gst_play_sink_get_vis_plugin (playbin->playsink));
1991       break;
1992     case PROP_TEXT_SINK:
1993       g_value_take_object (value,
1994           gst_play_bin3_get_current_sink (playbin, &playbin->text_sink,
1995               "text", GST_PLAY_SINK_TYPE_TEXT));
1996       break;
1997     case PROP_VIDEO_STREAM_COMBINER:
1998       g_value_take_object (value,
1999           gst_play_bin3_get_current_stream_combiner (playbin,
2000               &playbin->video_stream_combiner, "video", PLAYBIN_STREAM_VIDEO));
2001       break;
2002     case PROP_AUDIO_STREAM_COMBINER:
2003       g_value_take_object (value,
2004           gst_play_bin3_get_current_stream_combiner (playbin,
2005               &playbin->audio_stream_combiner, "audio", PLAYBIN_STREAM_AUDIO));
2006       break;
2007     case PROP_TEXT_STREAM_COMBINER:
2008       g_value_take_object (value,
2009           gst_play_bin3_get_current_stream_combiner (playbin,
2010               &playbin->text_stream_combiner, "text", PLAYBIN_STREAM_TEXT));
2011       break;
2012     case PROP_VOLUME:
2013       g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
2014       break;
2015     case PROP_MUTE:
2016       g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
2017       break;
2018     case PROP_SAMPLE:
2019       gst_value_take_sample (value,
2020           gst_play_sink_get_last_sample (playbin->playsink));
2021       break;
2022     case PROP_FONT_DESC:
2023       g_value_take_string (value,
2024           gst_play_sink_get_font_desc (playbin->playsink));
2025       break;
2026     case PROP_CONNECTION_SPEED:
2027       GST_PLAY_BIN3_LOCK (playbin);
2028       g_value_set_uint64 (value, playbin->connection_speed / 1000);
2029       GST_PLAY_BIN3_UNLOCK (playbin);
2030       break;
2031     case PROP_BUFFER_SIZE:
2032       GST_OBJECT_LOCK (playbin);
2033       g_value_set_int (value, playbin->buffer_size);
2034       GST_OBJECT_UNLOCK (playbin);
2035       break;
2036     case PROP_BUFFER_DURATION:
2037       GST_OBJECT_LOCK (playbin);
2038       g_value_set_int64 (value, playbin->buffer_duration);
2039       GST_OBJECT_UNLOCK (playbin);
2040       break;
2041     case PROP_AV_OFFSET:
2042       g_value_set_int64 (value,
2043           gst_play_sink_get_av_offset (playbin->playsink));
2044       break;
2045     case PROP_TEXT_OFFSET:
2046       g_value_set_int64 (value,
2047           gst_play_sink_get_text_offset (playbin->playsink));
2048       break;
2049     case PROP_RING_BUFFER_MAX_SIZE:
2050       g_value_set_uint64 (value, playbin->ring_buffer_max_size);
2051       break;
2052     case PROP_FORCE_ASPECT_RATIO:{
2053       gboolean v;
2054 
2055       g_object_get (playbin->playsink, "force-aspect-ratio", &v, NULL);
2056       g_value_set_boolean (value, v);
2057       break;
2058     }
2059     case PROP_MULTIVIEW_MODE:
2060       GST_OBJECT_LOCK (playbin);
2061       g_value_set_enum (value, playbin->multiview_mode);
2062       GST_OBJECT_UNLOCK (playbin);
2063       break;
2064     case PROP_MULTIVIEW_FLAGS:
2065       GST_OBJECT_LOCK (playbin);
2066       g_value_set_flags (value, playbin->multiview_flags);
2067       GST_OBJECT_UNLOCK (playbin);
2068       break;
2069     default:
2070       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2071       break;
2072   }
2073 }
2074 
2075 static gint
get_combiner_stream_id(GstPlayBin3 * playbin,GstSourceCombine * combine,GList * full_list)2076 get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine,
2077     GList * full_list)
2078 {
2079   gint i;
2080   GList *tmp;
2081 
2082   for (i = 0; i < combine->streams->len; i++) {
2083     GstStream *stream = (GstStream *) g_ptr_array_index (combine->streams, i);
2084     const gchar *sid = gst_stream_get_stream_id (stream);
2085     for (tmp = full_list; tmp; tmp = tmp->next) {
2086       gchar *orig = (gchar *) tmp->data;
2087       if (!g_strcmp0 (orig, sid))
2088         return i;
2089     }
2090   }
2091 
2092   /* Fallback */
2093   return -1;
2094 }
2095 
2096 static GList *
extend_list_of_streams(GstPlayBin3 * playbin,GstStreamType stype,GList * list,GstStreamCollection * collection)2097 extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype,
2098     GList * list, GstStreamCollection * collection)
2099 {
2100   GList *tmp, *res;
2101   gint i, nb;
2102 
2103   res = list;
2104 
2105   nb = gst_stream_collection_get_size (collection);
2106   for (i = 0; i < nb; i++) {
2107     GstStream *stream = gst_stream_collection_get_stream (collection, i);
2108     GstStreamType curtype = gst_stream_get_stream_type (stream);
2109     if (stype == curtype) {
2110       gboolean already_there = FALSE;
2111       const gchar *sid = gst_stream_get_stream_id (stream);
2112       for (tmp = res; tmp; tmp = tmp->next) {
2113         const gchar *other = (const gchar *) tmp->data;
2114         if (!g_strcmp0 (sid, other)) {
2115           already_there = TRUE;
2116           break;
2117         }
2118       }
2119       if (!already_there) {
2120         GST_DEBUG_OBJECT (playbin, "Adding stream %s", sid);
2121         res = g_list_append (res, g_strdup (sid));
2122       }
2123     }
2124   }
2125 
2126   return res;
2127 }
2128 
2129 static GstEvent *
update_select_streams_event(GstPlayBin3 * playbin,GstEvent * event,GstSourceGroup * group)2130 update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event,
2131     GstSourceGroup * group)
2132 {
2133   GList *streams = NULL;
2134   GList *to_use;
2135   gint combine_id;
2136 
2137   if (!playbin->audio_stream_combiner && !playbin->video_stream_combiner &&
2138       !playbin->text_stream_combiner) {
2139     /* Nothing to do */
2140     GST_DEBUG_OBJECT (playbin,
2141         "No custom combiners, no need to modify SELECT_STREAMS event");
2142     return event;
2143   }
2144 
2145   if (!group->collection) {
2146     GST_DEBUG_OBJECT (playbin,
2147         "No stream collection for group, no need to modify SELECT_STREAMS event");
2148     return event;
2149   }
2150 
2151   gst_event_parse_select_streams (event, &streams);
2152   to_use = g_list_copy_deep (streams, (GCopyFunc) g_strdup, NULL);
2153 
2154   /* For each combiner, we want to add all streams of that type to the
2155    * selection */
2156   if (playbin->audio_stream_combiner) {
2157     to_use =
2158         extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use,
2159         group->collection);
2160     combine_id =
2161         get_combiner_stream_id (playbin,
2162         &playbin->combiner[PLAYBIN_STREAM_AUDIO], streams);
2163     if (combine_id != -1)
2164       gst_play_bin3_set_current_audio_stream (playbin, combine_id);
2165   }
2166   if (playbin->video_stream_combiner) {
2167     to_use =
2168         extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use,
2169         group->collection);
2170     combine_id =
2171         get_combiner_stream_id (playbin,
2172         &playbin->combiner[PLAYBIN_STREAM_VIDEO], streams);
2173     if (combine_id != -1)
2174       gst_play_bin3_set_current_video_stream (playbin, combine_id);
2175   }
2176   if (playbin->text_stream_combiner) {
2177     to_use =
2178         extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use,
2179         group->collection);
2180     combine_id =
2181         get_combiner_stream_id (playbin,
2182         &playbin->combiner[PLAYBIN_STREAM_TEXT], streams);
2183     if (combine_id != -1)
2184       gst_play_bin3_set_current_text_stream (playbin, combine_id);
2185   }
2186 
2187   gst_event_unref (event);
2188   event = gst_event_new_select_streams (to_use);
2189 
2190   if (streams)
2191     g_list_free_full (streams, g_free);
2192   if (to_use)
2193     g_list_free_full (to_use, g_free);
2194 
2195   return event;
2196 }
2197 
2198 /* Returns TRUE if the given list of streams belongs to the stream collection */
2199 static gboolean
gst_streams_belong_to_collection(GList * streams,GstStreamCollection * collection)2200 gst_streams_belong_to_collection (GList * streams,
2201     GstStreamCollection * collection)
2202 {
2203   GList *tmp;
2204   guint i, nb;
2205 
2206   if (streams == NULL || collection == NULL)
2207     return FALSE;
2208   nb = gst_stream_collection_get_size (collection);
2209   if (nb == 0)
2210     return FALSE;
2211 
2212   for (tmp = streams; tmp; tmp = tmp->next) {
2213     const gchar *cand = (const gchar *) tmp->data;
2214     gboolean found = FALSE;
2215 
2216     for (i = 0; i < nb; i++) {
2217       GstStream *stream = gst_stream_collection_get_stream (collection, i);
2218       if (!g_strcmp0 (cand, gst_stream_get_stream_id (stream))) {
2219         found = TRUE;
2220         break;
2221       }
2222     }
2223     if (!found)
2224       return FALSE;
2225   }
2226   return TRUE;
2227 }
2228 
2229 static GstSourceGroup *
get_source_group_for_streams(GstPlayBin3 * playbin,GstEvent * event)2230 get_source_group_for_streams (GstPlayBin3 * playbin, GstEvent * event)
2231 {
2232   GList *streams;
2233   GstSourceGroup *res = NULL;
2234 
2235   gst_event_parse_select_streams (event, &streams);
2236   if (playbin->curr_group->collection &&
2237       gst_streams_belong_to_collection (streams,
2238           playbin->curr_group->collection))
2239     res = playbin->curr_group;
2240   else if (playbin->next_group->collection &&
2241       gst_streams_belong_to_collection (streams,
2242           playbin->next_group->collection))
2243     res = playbin->next_group;
2244   g_list_free_full (streams, g_free);
2245 
2246   return res;
2247 }
2248 
2249 static GstStreamType
get_stream_type_for_event(GstStreamCollection * collection,GstEvent * event)2250 get_stream_type_for_event (GstStreamCollection * collection, GstEvent * event)
2251 {
2252   GList *stream_list = NULL;
2253   GList *tmp;
2254   GstStreamType res = 0;
2255   guint i, len;
2256 
2257   gst_event_parse_select_streams (event, &stream_list);
2258   len = gst_stream_collection_get_size (collection);
2259   for (tmp = stream_list; tmp; tmp = tmp->next) {
2260     gchar *stid = (gchar *) tmp->data;
2261 
2262     for (i = 0; i < len; i++) {
2263       GstStream *stream = gst_stream_collection_get_stream (collection, i);
2264       if (!g_strcmp0 (stid, gst_stream_get_stream_id (stream))) {
2265         res |= gst_stream_get_stream_type (stream);
2266       }
2267     }
2268   }
2269   g_list_free_full (stream_list, g_free);
2270 
2271   return res;
2272 }
2273 
2274 static gboolean
gst_play_bin3_send_event(GstElement * element,GstEvent * event)2275 gst_play_bin3_send_event (GstElement * element, GstEvent * event)
2276 {
2277   GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
2278 
2279   if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) {
2280     gboolean res;
2281     GstSourceGroup *group;
2282 
2283     GST_PLAY_BIN3_LOCK (playbin);
2284     GST_LOG_OBJECT (playbin,
2285         "App sent select-streams, we won't do anything ourselves now");
2286     /* This is probably already false, but it doesn't hurt to be sure */
2287     playbin->do_stream_selections = FALSE;
2288 
2289     group = get_source_group_for_streams (playbin, event);
2290     if (group == NULL) {
2291       GST_WARNING_OBJECT (playbin,
2292           "Can't figure out to which uridecodebin the select-streams event should be sent to");
2293       GST_PLAY_BIN3_UNLOCK (playbin);
2294       return FALSE;
2295     }
2296 
2297     /* If we have custom combiners, we need to extend the selection with
2298      * the list of all streams for that given type since we will be handling
2299      * the selection with that combiner */
2300     event = update_select_streams_event (playbin, event, group);
2301 
2302     if (group->collection) {
2303       group->selected_stream_types =
2304           get_stream_type_for_event (group->collection, event);
2305       playbin->selected_stream_types =
2306           playbin->groups[0].selected_stream_types | playbin->
2307           groups[1].selected_stream_types;
2308       if (playbin->active_stream_types != playbin->selected_stream_types)
2309         reconfigure_output (playbin);
2310     }
2311 
2312     /* Send this event directly to uridecodebin, so it works even
2313      * if uridecodebin didn't add any pads yet */
2314     res = gst_element_send_event (group->uridecodebin, event);
2315     GST_PLAY_BIN3_UNLOCK (playbin);
2316 
2317     return res;
2318   }
2319 
2320   /* Send event directly to playsink instead of letting GstBin iterate
2321    * over all sink elements. The latter might send the event multiple times
2322    * in case the SEEK causes a reconfiguration of the pipeline, as can easily
2323    * happen with adaptive streaming demuxers.
2324    *
2325    * What would then happen is that the iterator would be reset, we send the
2326    * event again, and on the second time it will fail in the majority of cases
2327    * because the pipeline is still being reconfigured
2328    */
2329   if (GST_EVENT_IS_UPSTREAM (event)) {
2330     return gst_element_send_event (GST_ELEMENT_CAST (playbin->playsink), event);
2331   }
2332 
2333   return GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
2334 }
2335 
2336 /* Called with playbin lock held */
2337 static void
do_stream_selection(GstPlayBin3 * playbin,GstSourceGroup * group)2338 do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group)
2339 {
2340   GstStreamCollection *collection;
2341   guint i, nb_streams;
2342   GList *streams = NULL;
2343   gint nb_video = 0, nb_audio = 0, nb_text = 0;
2344   GstStreamType chosen_stream_types = 0;
2345 
2346   if (group == NULL)
2347     return;
2348 
2349   collection = group->collection;
2350   if (collection == NULL) {
2351     GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select");
2352     return;
2353   }
2354 
2355   nb_streams = gst_stream_collection_get_size (collection);
2356   if (nb_streams == 0) {
2357     GST_INFO_OBJECT (playbin, "Empty collection received! Ignoring");
2358   }
2359 
2360   GST_DEBUG_OBJECT (playbin, "Doing selection on collection with %d streams",
2361       nb_streams);
2362 
2363   /* Iterate the collection and choose the streams that match
2364    * either the current-* setting, or all streams of a type if there's
2365    * a combiner for that type */
2366   for (i = 0; i < nb_streams; i++) {
2367     GstStream *stream = gst_stream_collection_get_stream (collection, i);
2368     GstStreamType stream_type = gst_stream_get_stream_type (stream);
2369     const gchar *stream_id = gst_stream_get_stream_id (stream);
2370     gint pb_stream_type = -1;
2371     gboolean select_this = FALSE;
2372 
2373     GST_LOG_OBJECT (playbin, "Looking at stream #%d : %s", i, stream_id);
2374 
2375     if (stream_type & GST_STREAM_TYPE_AUDIO) {
2376       pb_stream_type = PLAYBIN_STREAM_AUDIO;
2377       /* Select the stream if it's the current one or if there's a custom selector */
2378       select_this =
2379           (nb_audio == playbin->current_audio ||
2380           (playbin->current_audio == -1 && nb_audio == 0) ||
2381           playbin->audio_stream_combiner != NULL);
2382       nb_audio++;
2383     } else if (stream_type & GST_STREAM_TYPE_VIDEO) {
2384       pb_stream_type = PLAYBIN_STREAM_VIDEO;
2385       select_this =
2386           (nb_video == playbin->current_video ||
2387           (playbin->current_video == -1 && nb_video == 0) ||
2388           playbin->video_stream_combiner != NULL);
2389       nb_video++;
2390     } else if (stream_type & GST_STREAM_TYPE_TEXT) {
2391       pb_stream_type = PLAYBIN_STREAM_TEXT;
2392       select_this =
2393           (nb_text == playbin->current_text ||
2394           (playbin->current_text == -1 && nb_text == 0) ||
2395           playbin->text_stream_combiner != NULL);
2396       nb_text++;
2397     }
2398     if (pb_stream_type < 0) {
2399       GST_DEBUG_OBJECT (playbin,
2400           "Stream %d (id %s) of unhandled type %s. Ignoring", i, stream_id,
2401           gst_stream_type_get_name (stream_type));
2402       continue;
2403     }
2404     if (select_this) {
2405       GST_DEBUG_OBJECT (playbin, "Selecting stream %s of type %s",
2406           stream_id, gst_stream_type_get_name (stream_type));
2407       /* Don't build the list if we're not in charge of stream selection */
2408       if (playbin->do_stream_selections)
2409         streams = g_list_append (streams, (gpointer) stream_id);
2410       chosen_stream_types |= stream_type;
2411     }
2412   }
2413 
2414   if (streams) {
2415     if (group->uridecodebin) {
2416       GstEvent *ev = gst_event_new_select_streams (streams);
2417       gst_element_send_event (group->uridecodebin, ev);
2418     }
2419     g_list_free (streams);
2420   }
2421 
2422   group->selected_stream_types = chosen_stream_types;
2423   /* Update global selected_stream_types */
2424   playbin->selected_stream_types =
2425       playbin->groups[0].selected_stream_types | playbin->
2426       groups[1].selected_stream_types;
2427   if (playbin->active_stream_types != playbin->selected_stream_types)
2428     reconfigure_output (playbin);
2429 }
2430 
2431 /* Return the GstSourceGroup to which this element belongs
2432  * Can be NULL (if it belongs to playsink for example) */
2433 static GstSourceGroup *
find_source_group_owner(GstPlayBin3 * playbin,GstObject * element)2434 find_source_group_owner (GstPlayBin3 * playbin, GstObject * element)
2435 {
2436   if (playbin->curr_group->uridecodebin
2437       && gst_object_has_as_ancestor (element,
2438           GST_OBJECT_CAST (playbin->curr_group->uridecodebin)))
2439     return playbin->curr_group;
2440   if (playbin->next_group->uridecodebin
2441       && gst_object_has_as_ancestor (element,
2442           GST_OBJECT_CAST (playbin->next_group->uridecodebin)))
2443     return playbin->next_group;
2444   return NULL;
2445 }
2446 
2447 static void
gst_play_bin3_handle_message(GstBin * bin,GstMessage * msg)2448 gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
2449 {
2450   GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
2451 
2452   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
2453     GstSourceGroup *group = NULL, *other_group = NULL;
2454     gboolean changed = FALSE;
2455     guint group_id;
2456     GstMessage *buffering_msg;
2457 
2458     if (!gst_message_parse_group_id (msg, &group_id)) {
2459       GST_ERROR_OBJECT (bin,
2460           "Could not get group_id from STREAM_START message !");
2461       goto beach;
2462     }
2463     GST_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id);
2464 
2465     /* Figure out to which group this group_id corresponds */
2466     GST_PLAY_BIN3_LOCK (playbin);
2467     if (playbin->groups[0].group_id == group_id) {
2468       group = &playbin->groups[0];
2469       other_group = &playbin->groups[1];
2470     } else if (playbin->groups[1].group_id == group_id) {
2471       group = &playbin->groups[1];
2472       other_group = &playbin->groups[0];
2473     }
2474     if (group == NULL) {
2475       GST_ERROR_OBJECT (bin, "group_id %u is not provided by any group !",
2476           group_id);
2477       GST_PLAY_BIN3_UNLOCK (playbin);
2478       goto beach;
2479     }
2480 
2481     debug_groups (playbin);
2482 
2483     /* Do the switch now ! */
2484     playbin->curr_group = group;
2485     playbin->next_group = other_group;
2486 
2487     GST_SOURCE_GROUP_LOCK (group);
2488     if (group->playing == FALSE)
2489       changed = TRUE;
2490     group->playing = TRUE;
2491     buffering_msg = group->pending_buffering_msg;
2492     group->pending_buffering_msg = NULL;
2493     GST_SOURCE_GROUP_UNLOCK (group);
2494 
2495     GST_SOURCE_GROUP_LOCK (other_group);
2496     other_group->playing = FALSE;
2497     GST_SOURCE_GROUP_UNLOCK (other_group);
2498 
2499     debug_groups (playbin);
2500     GST_PLAY_BIN3_UNLOCK (playbin);
2501     if (changed)
2502       gst_play_bin3_check_group_status (playbin);
2503     else
2504       GST_DEBUG_OBJECT (bin, "Groups didn't changed");
2505     /* If there was a pending buffering message to send, do it now */
2506     if (buffering_msg)
2507       GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
2508   } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
2509     GstSourceGroup *group;
2510 
2511     /* Only post buffering messages for group which is currently playing */
2512     group = find_source_group_owner (playbin, msg->src);
2513     GST_SOURCE_GROUP_LOCK (group);
2514     if (!group->playing) {
2515       GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group "
2516           "%p %" GST_PTR_FORMAT, group, msg);
2517       gst_message_replace (&group->pending_buffering_msg, msg);
2518       gst_message_unref (msg);
2519       msg = NULL;
2520     }
2521     GST_SOURCE_GROUP_UNLOCK (group);
2522   } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) {
2523     GstStreamCollection *collection = NULL;
2524 
2525     gst_message_parse_stream_collection (msg, &collection);
2526 
2527     if (collection) {
2528       gboolean pstate = playbin->do_stream_selections;
2529       GstSourceGroup *target_group = NULL;
2530 
2531       GST_PLAY_BIN3_LOCK (playbin);
2532       GST_DEBUG_OBJECT (playbin,
2533           "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT,
2534           msg->src);
2535       target_group = find_source_group_owner (playbin, msg->src);
2536       if (target_group)
2537         gst_object_replace ((GstObject **) & target_group->collection,
2538             (GstObject *) collection);
2539       /* FIXME: Only do the following if it's the current group? */
2540       if (target_group == playbin->curr_group)
2541         update_combiner_info (playbin, target_group->collection);
2542       if (pstate)
2543         playbin->do_stream_selections = FALSE;
2544       do_stream_selection (playbin, target_group);
2545       if (pstate)
2546         playbin->do_stream_selections = TRUE;
2547       GST_PLAY_BIN3_UNLOCK (playbin);
2548 
2549       gst_object_unref (collection);
2550     }
2551   }
2552 
2553 beach:
2554   if (msg)
2555     GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
2556 }
2557 
2558 static void
gst_play_bin3_deep_element_added(GstBin * playbin,GstBin * sub_bin,GstElement * child)2559 gst_play_bin3_deep_element_added (GstBin * playbin, GstBin * sub_bin,
2560     GstElement * child)
2561 {
2562   GST_LOG_OBJECT (playbin, "element %" GST_PTR_FORMAT " was added to "
2563       "%" GST_PTR_FORMAT, child, sub_bin);
2564 
2565   g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_ELEMENT_SETUP], 0,
2566       child);
2567 
2568   GST_BIN_CLASS (parent_class)->deep_element_added (playbin, sub_bin, child);
2569 }
2570 
2571 /* Returns current stream number, or -1 if none has been selected yet */
2572 static int
get_current_stream_number(GstPlayBin3 * playbin,GstSourceCombine * combine,GPtrArray * channels)2573 get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine,
2574     GPtrArray * channels)
2575 {
2576   /* Internal API cleanup would make this easier... */
2577   int i;
2578   GstPad *pad, *current;
2579   GstObject *combiner = NULL;
2580   int ret = -1;
2581 
2582   if (!combine->has_active_pad) {
2583     GST_WARNING_OBJECT (playbin,
2584         "combiner doesn't have the \"active-pad\" property");
2585     return ret;
2586   }
2587 
2588   for (i = 0; i < channels->len; i++) {
2589     pad = g_ptr_array_index (channels, i);
2590     if ((combiner = gst_pad_get_parent (pad))) {
2591       g_object_get (combiner, "active-pad", &current, NULL);
2592       gst_object_unref (combiner);
2593 
2594       if (pad == current) {
2595         gst_object_unref (current);
2596         ret = i;
2597         break;
2598       }
2599 
2600       if (current)
2601         gst_object_unref (current);
2602     }
2603   }
2604 
2605   return ret;
2606 }
2607 
2608 static void
combiner_active_pad_changed(GObject * combiner,GParamSpec * pspec,GstPlayBin3 * playbin)2609 combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
2610     GstPlayBin3 * playbin)
2611 {
2612   GstSourceCombine *combine = NULL;
2613   GPtrArray *channels = NULL;
2614   int i;
2615 
2616   GST_PLAY_BIN3_LOCK (playbin);
2617 
2618   for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2619     if (combiner == G_OBJECT (playbin->combiner[i].combiner)) {
2620       combine = &playbin->combiner[i];
2621       channels = playbin->channels[i];
2622     }
2623   }
2624 
2625   /* We got a pad-change after our group got switched out; no need to notify */
2626   if (!combine) {
2627     GST_PLAY_BIN3_UNLOCK (playbin);
2628     return;
2629   }
2630 
2631   switch (combine->type) {
2632     case GST_PLAY_SINK_TYPE_VIDEO:
2633     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2634       playbin->current_video = get_current_stream_number (playbin,
2635           combine, channels);
2636 
2637       if (playbin->video_pending_flush_finish) {
2638         playbin->video_pending_flush_finish = FALSE;
2639         GST_PLAY_BIN3_UNLOCK (playbin);
2640         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
2641             "playsink-custom-video-flush-finish");
2642       }
2643       break;
2644     case GST_PLAY_SINK_TYPE_AUDIO:
2645     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2646       playbin->current_audio = get_current_stream_number (playbin,
2647           combine, channels);
2648 
2649       if (playbin->audio_pending_flush_finish) {
2650         playbin->audio_pending_flush_finish = FALSE;
2651         GST_PLAY_BIN3_UNLOCK (playbin);
2652         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
2653             "playsink-custom-audio-flush-finish");
2654       }
2655       break;
2656     case GST_PLAY_SINK_TYPE_TEXT:
2657       playbin->current_text = get_current_stream_number (playbin,
2658           combine, channels);
2659 
2660       if (playbin->text_pending_flush_finish) {
2661         playbin->text_pending_flush_finish = FALSE;
2662         GST_PLAY_BIN3_UNLOCK (playbin);
2663         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
2664             "playsink-custom-subtitle-flush-finish");
2665       }
2666       break;
2667     default:
2668       break;
2669   }
2670   GST_PLAY_BIN3_UNLOCK (playbin);
2671 }
2672 
2673 static GstCaps *
update_video_multiview_caps(GstPlayBin3 * playbin,GstCaps * caps)2674 update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps)
2675 {
2676   GstVideoMultiviewMode mv_mode;
2677   GstVideoMultiviewMode cur_mv_mode;
2678   guint mv_flags, cur_mv_flags;
2679   GstStructure *s;
2680   const gchar *mview_mode_str;
2681   GstCaps *out_caps;
2682 
2683   GST_OBJECT_LOCK (playbin);
2684   mv_mode = (GstVideoMultiviewMode) playbin->multiview_mode;
2685   mv_flags = playbin->multiview_flags;
2686   GST_OBJECT_UNLOCK (playbin);
2687 
2688   if (mv_mode == GST_VIDEO_MULTIVIEW_MODE_NONE)
2689     return NULL;
2690 
2691   cur_mv_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
2692   cur_mv_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
2693 
2694   s = gst_caps_get_structure (caps, 0);
2695 
2696   gst_structure_get_flagset (s, "multiview-flags", &cur_mv_flags, NULL);
2697   if ((mview_mode_str = gst_structure_get_string (s, "multiview-mode")))
2698     cur_mv_mode = gst_video_multiview_mode_from_caps_string (mview_mode_str);
2699 
2700   /* We can't override an existing annotated multiview mode, except
2701    * maybe (in the future) we could change some flags. */
2702   if ((gint) cur_mv_mode > GST_VIDEO_MULTIVIEW_MAX_FRAME_PACKING) {
2703     GST_INFO_OBJECT (playbin, "Cannot override existing multiview mode");
2704     return NULL;
2705   }
2706 
2707   mview_mode_str = gst_video_multiview_mode_to_caps_string (mv_mode);
2708   g_assert (mview_mode_str != NULL);
2709   out_caps = gst_caps_copy (caps);
2710   s = gst_caps_get_structure (out_caps, 0);
2711 
2712   gst_structure_set (s, "multiview-mode", G_TYPE_STRING, mview_mode_str,
2713       "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, mv_flags,
2714       GST_FLAG_SET_MASK_EXACT, NULL);
2715 
2716   return out_caps;
2717 }
2718 
2719 static void
emit_about_to_finish(GstPlayBin3 * playbin)2720 emit_about_to_finish (GstPlayBin3 * playbin)
2721 {
2722   GST_DEBUG_OBJECT (playbin, "Emitting about-to-finish");
2723 
2724   /* after this call, we should have a next group to activate or we EOS */
2725   g_signal_emit (G_OBJECT (playbin),
2726       gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
2727 
2728   debug_groups (playbin);
2729 
2730   /* now activate the next group. If the app did not set a uri, this will
2731    * fail and we can do EOS */
2732   setup_next_source (playbin);
2733 }
2734 
2735 static SourcePad *
find_source_pad(GstSourceGroup * group,GstPad * target)2736 find_source_pad (GstSourceGroup * group, GstPad * target)
2737 {
2738   GList *tmp;
2739 
2740   for (tmp = group->source_pads; tmp; tmp = tmp->next) {
2741     SourcePad *res = (SourcePad *) tmp->data;
2742     if (res->pad == target)
2743       return res;
2744   }
2745   return NULL;
2746 }
2747 
2748 static GstPadProbeReturn
_decodebin_event_probe(GstPad * pad,GstPadProbeInfo * info,gpointer udata)2749 _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
2750 {
2751   GstPadProbeReturn ret = GST_PAD_PROBE_OK;
2752   GstSourceGroup *group = (GstSourceGroup *) udata;
2753   GstPlayBin3 *playbin = group->playbin;
2754   GstEvent *event = GST_PAD_PROBE_INFO_DATA (info);
2755 
2756   switch (GST_EVENT_TYPE (event)) {
2757     case GST_EVENT_CAPS:{
2758       GstCaps *caps = NULL;
2759       const GstStructure *s;
2760       const gchar *name;
2761 
2762       gst_event_parse_caps (event, &caps);
2763       /* If video caps, check if we should override multiview flags */
2764       s = gst_caps_get_structure (caps, 0);
2765       name = gst_structure_get_name (s);
2766       if (g_str_has_prefix (name, "video/")) {
2767         caps = update_video_multiview_caps (playbin, caps);
2768         if (caps) {
2769           gst_event_unref (event);
2770           event = gst_event_new_caps (caps);
2771           GST_PAD_PROBE_INFO_DATA (info) = event;
2772           gst_caps_unref (caps);
2773         }
2774       }
2775       break;
2776     }
2777     case GST_EVENT_STREAM_START:
2778     {
2779       guint group_id;
2780       if (gst_event_parse_group_id (event, &group_id)) {
2781         GST_LOG_OBJECT (pad, "STREAM_START group_id:%u", group_id);
2782         if (group->group_id == GST_GROUP_ID_INVALID)
2783           group->group_id = group_id;
2784         else if (group->group_id != group_id) {
2785           GST_DEBUG_OBJECT (pad, "group_id changing from %u to %u",
2786               group->group_id, group_id);
2787           group->group_id = group_id;
2788         }
2789       }
2790       break;
2791     }
2792     default:
2793       break;
2794   }
2795 
2796   return ret;
2797 }
2798 
2799 static void
control_source_pad(GstSourceGroup * group,GstPad * pad,GstStreamType stream_type)2800 control_source_pad (GstSourceGroup * group, GstPad * pad,
2801     GstStreamType stream_type)
2802 {
2803   SourcePad *sourcepad = g_slice_new0 (SourcePad);
2804 
2805   sourcepad->pad = pad;
2806   sourcepad->event_probe_id =
2807       gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2808       _decodebin_event_probe, group, NULL);
2809   sourcepad->stream_type = stream_type;
2810   group->source_pads = g_list_append (group->source_pads, sourcepad);
2811 }
2812 
2813 static void
remove_combiner(GstPlayBin3 * playbin,GstSourceCombine * combine)2814 remove_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
2815 {
2816   gint n;
2817 
2818   if (combine->combiner == NULL) {
2819     GST_DEBUG_OBJECT (playbin, "No combiner element to remove");
2820     return;
2821   }
2822 
2823   /* Go over all sink pads and release them ! */
2824   for (n = 0; n < combine->channels->len; n++) {
2825     GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
2826 
2827     gst_element_release_request_pad (combine->combiner, sinkpad);
2828     gst_object_unref (sinkpad);
2829   }
2830   g_ptr_array_set_size (combine->channels, 0);
2831 
2832   gst_element_set_state (combine->combiner, GST_STATE_NULL);
2833   gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
2834   combine->combiner = NULL;
2835 
2836 }
2837 
2838 /* Create the combiner element if needed for the given combine */
2839 static void
create_combiner(GstPlayBin3 * playbin,GstSourceCombine * combine)2840 create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
2841 {
2842   GstElement *custom_combiner = NULL;
2843 
2844   if (combine->combiner) {
2845     GST_WARNING_OBJECT (playbin, "Combiner element already exists!");
2846     return;
2847   }
2848 
2849   if (combine->stream_type == GST_STREAM_TYPE_VIDEO)
2850     custom_combiner = playbin->video_stream_combiner;
2851   else if (combine->stream_type == GST_STREAM_TYPE_AUDIO)
2852     custom_combiner = playbin->audio_stream_combiner;
2853   else if (combine->stream_type == GST_STREAM_TYPE_TEXT)
2854     custom_combiner = playbin->text_stream_combiner;
2855 
2856   combine->combiner = custom_combiner;
2857 
2858   if (!combine->combiner) {
2859     gchar *concat_name;
2860     GST_DEBUG_OBJECT (playbin,
2861         "No custom combiner requested, using 'concat' element");
2862     concat_name = g_strdup_printf ("%s-concat", combine->media_type);
2863     combine->combiner = gst_element_factory_make ("concat", concat_name);
2864     g_object_set (combine->combiner, "adjust-base", FALSE, NULL);
2865     g_free (concat_name);
2866     combine->is_concat = TRUE;
2867   }
2868 
2869   combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
2870 
2871   /* We only want to use 'active-pad' if it's a regular combiner that
2872    * will consume all streams, and not concat (which is just used for
2873    * gapless) */
2874   if (!combine->is_concat) {
2875     combine->has_active_pad =
2876         g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
2877         "active-pad") != NULL;
2878 
2879     if (combine->has_active_pad)
2880       g_signal_connect (combine->combiner, "notify::active-pad",
2881           G_CALLBACK (combiner_active_pad_changed), playbin);
2882   }
2883 
2884   GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT,
2885       combine->combiner);
2886   gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
2887   gst_element_sync_state_with_parent (combine->combiner);
2888 }
2889 
2890 static gboolean
combiner_control_pad(GstPlayBin3 * playbin,GstSourceCombine * combine,GstPad * srcpad)2891 combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
2892     GstPad * srcpad)
2893 {
2894   GstPadLinkReturn res;
2895 
2896   GST_DEBUG_OBJECT (playbin, "srcpad %" GST_PTR_FORMAT, srcpad);
2897 
2898   if (combine->combiner) {
2899     GstPad *sinkpad =
2900         gst_element_get_request_pad (combine->combiner, "sink_%u");
2901 
2902     if (sinkpad == NULL)
2903       goto request_pad_failed;
2904 
2905     GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT,
2906         sinkpad);
2907 
2908     /* store the pad in the array */
2909     GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array",
2910         sinkpad);
2911     g_ptr_array_add (combine->channels, sinkpad);
2912 
2913     res = gst_pad_link (srcpad, sinkpad);
2914     if (GST_PAD_LINK_FAILED (res))
2915       goto failed_combiner_link;
2916 
2917     GST_DEBUG_OBJECT (playbin,
2918         "linked pad %" GST_PTR_FORMAT " to combiner %" GST_PTR_FORMAT, srcpad,
2919         combine->combiner);
2920 
2921   } else {
2922     GST_LOG_OBJECT (playbin, "combine->sinkpad:%" GST_PTR_FORMAT,
2923         combine->sinkpad);
2924     g_assert (combine->sinkpad != NULL);
2925     /* Connect directly to playsink */
2926     if (gst_pad_is_linked (combine->sinkpad))
2927       goto sinkpad_already_linked;
2928 
2929     GST_DEBUG_OBJECT (playbin, "Linking new pad straight to playsink");
2930     res = gst_pad_link (srcpad, combine->sinkpad);
2931 
2932     if (res != GST_PAD_LINK_OK)
2933       goto failed_sinkpad_link;
2934   }
2935 
2936   return TRUE;
2937 
2938   /* Failure cases */
2939 request_pad_failed:
2940   GST_ELEMENT_ERROR (playbin, CORE, PAD,
2941       ("Internal playbin error."),
2942       ("Failed to get request pad from combiner %p.", combine->combiner));
2943   return FALSE;
2944 
2945 
2946 sinkpad_already_linked:
2947   GST_ELEMENT_ERROR (playbin, CORE, PAD,
2948       ("Internal playbin error."), ("playsink pad already used !"));
2949   return FALSE;
2950 
2951 failed_sinkpad_link:
2952   GST_ELEMENT_ERROR (playbin, CORE, PAD,
2953       ("Internal playbin error."),
2954       ("Failed to link pad to sink. Error %d", res));
2955   return FALSE;
2956 
2957 failed_combiner_link:
2958   GST_ELEMENT_ERROR (playbin, CORE, PAD,
2959       ("Internal playbin error."),
2960       ("Failed to link pad to combiner. Error %d", res));
2961   return FALSE;
2962 }
2963 
2964 static void
combiner_release_pad(GstPlayBin3 * playbin,GstSourceCombine * combine,GstPad * pad)2965 combiner_release_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
2966     GstPad * pad)
2967 {
2968   if (combine->combiner) {
2969     GstPad *peer = gst_pad_get_peer (pad);
2970 
2971     if (peer) {
2972       GST_DEBUG_OBJECT (playbin, "Removing combiner pad %" GST_PTR_FORMAT,
2973           peer);
2974       g_ptr_array_remove (combine->channels, peer);
2975 
2976       gst_element_release_request_pad (combine->combiner, peer);
2977       gst_object_unref (peer);
2978     }
2979   } else {
2980     /* Release direct link if present */
2981     if (combine->sinkpad) {
2982       GST_DEBUG_OBJECT (playbin, "Unlinking pad from playsink sinkpad");
2983       gst_pad_unlink (pad, combine->sinkpad);
2984     }
2985   }
2986 }
2987 
2988 /* Call after pad was unlinked from (potential) combiner */
2989 static void
release_source_pad(GstPlayBin3 * playbin,GstSourceGroup * group,GstPad * pad)2990 release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
2991 {
2992   SourcePad *sourcepad;
2993   GList *tmp;
2994   GstStreamType alltype = 0;
2995 
2996   sourcepad = find_source_pad (group, pad);
2997   if (!sourcepad) {
2998     GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?");
2999     return;
3000   }
3001 
3002   if (sourcepad->event_probe_id) {
3003     gst_pad_remove_probe (pad, sourcepad->event_probe_id);
3004     sourcepad->event_probe_id = 0;
3005   }
3006 
3007   /* Remove from list of controlled pads and check again for EOS status */
3008   group->source_pads = g_list_remove (group->source_pads, sourcepad);
3009   g_slice_free (SourcePad, sourcepad);
3010 
3011   /* Update present stream types */
3012   for (tmp = group->source_pads; tmp; tmp = tmp->next) {
3013     SourcePad *cand = (SourcePad *) tmp->data;
3014     alltype |= cand->stream_type;
3015   }
3016   group->present_stream_types = alltype;
3017 }
3018 
3019 /* this function is called when a new pad is added to decodebin. We check the
3020  * type of the pad and add it to the combiner element
3021  */
3022 static void
pad_added_cb(GstElement * uridecodebin,GstPad * pad,GstSourceGroup * group)3023 pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstSourceGroup * group)
3024 {
3025   GstSourceCombine *combine = NULL;
3026   gint pb_stream_type = -1;
3027   gchar *pad_name;
3028   GstPlayBin3 *playbin = group->playbin;
3029 
3030   GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
3031 
3032   pad_name = gst_object_get_name (GST_OBJECT (pad));
3033 
3034   GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
3035       GST_DEBUG_PAD_NAME (pad));
3036 
3037   /* major type of the pad, this determines the combiner to use,
3038      try exact match first */
3039   if (g_str_has_prefix (pad_name, "video")) {
3040     pb_stream_type = PLAYBIN_STREAM_VIDEO;
3041   } else if (g_str_has_prefix (pad_name, "audio")) {
3042     pb_stream_type = PLAYBIN_STREAM_AUDIO;
3043   } else if (g_str_has_prefix (pad_name, "text")) {
3044     pb_stream_type = PLAYBIN_STREAM_TEXT;
3045   }
3046 
3047   g_free (pad_name);
3048 
3049   /* no stream type found for the media type, don't bother linking it to a
3050    * combiner. This will leave the pad unlinked and thus ignored. */
3051   if (pb_stream_type < 0) {
3052     GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
3053     goto unknown_type;
3054   }
3055 
3056   combine = &playbin->combiner[pb_stream_type];
3057 
3058   combiner_control_pad (playbin, combine, pad);
3059 
3060   control_source_pad (group, pad, combine->stream_type);
3061 
3062   /* Update present stream_types and check whether we should post a pending about-to-finish */
3063   group->present_stream_types |= combine->stream_type;
3064 
3065   if (group->playing && group->pending_about_to_finish
3066       && group->present_stream_types == group->selected_stream_types) {
3067     group->pending_about_to_finish = FALSE;
3068     emit_about_to_finish (playbin);
3069   }
3070 
3071   GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
3072 
3073   return;
3074 
3075   /* ERRORS */
3076 unknown_type:
3077   GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
3078   return;
3079 
3080 shutdown:
3081   {
3082     GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
3083     /* not going to done as we didn't request the caps */
3084     return;
3085   }
3086 }
3087 
3088 /* called when a pad is removed from the decodebin. We unlink the pad from
3089  * the combiner. */
3090 static void
pad_removed_cb(GstElement * decodebin,GstPad * pad,GstSourceGroup * group)3091 pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
3092 {
3093   GstSourceCombine *combine;
3094   GstPlayBin3 *playbin = group->playbin;
3095 
3096   GST_DEBUG_OBJECT (playbin,
3097       "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
3098 
3099   GST_PLAY_BIN3_LOCK (playbin);
3100 
3101   /* Get combiner for pad */
3102   if (g_str_has_prefix (GST_PAD_NAME (pad), "video"))
3103     combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO];
3104   else if (g_str_has_prefix (GST_PAD_NAME (pad), "audio"))
3105     combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO];
3106   else if (g_str_has_prefix (GST_PAD_NAME (pad), "text"))
3107     combine = &playbin->combiner[PLAYBIN_STREAM_TEXT];
3108   else
3109     return;
3110 
3111   combiner_release_pad (playbin, combine, pad);
3112   release_source_pad (playbin, group, pad);
3113 
3114   GST_PLAY_BIN3_UNLOCK (playbin);
3115 }
3116 
3117 
3118 static gint
select_stream_cb(GstElement * decodebin,GstStreamCollection * collection,GstStream * stream,GstSourceGroup * group)3119 select_stream_cb (GstElement * decodebin, GstStreamCollection * collection,
3120     GstStream * stream, GstSourceGroup * group)
3121 {
3122   GstStreamType stype = gst_stream_get_stream_type (stream);
3123   GstElement *combiner = NULL;
3124   GstPlayBin3 *playbin = group->playbin;
3125 
3126   if (stype & GST_STREAM_TYPE_AUDIO)
3127     combiner = playbin->audio_stream_combiner;
3128   else if (stype & GST_STREAM_TYPE_VIDEO)
3129     combiner = playbin->video_stream_combiner;
3130   else if (stype & GST_STREAM_TYPE_TEXT)
3131     combiner = playbin->text_stream_combiner;
3132 
3133   if (combiner) {
3134     GST_DEBUG_OBJECT (playbin, "Got a combiner, requesting stream activation");
3135     return 1;
3136   }
3137 
3138   /* Let decodebin3 decide otherwise */
3139   return -1;
3140 }
3141 
3142 /* We get called when the selected stream types change and
3143  * reconfiguration of output (i.e. playsink and potential combiners)
3144  * are required.
3145  */
3146 static void
reconfigure_output(GstPlayBin3 * playbin)3147 reconfigure_output (GstPlayBin3 * playbin)
3148 {
3149   GstPadLinkReturn res;
3150   gint i;
3151 
3152   g_assert (playbin->selected_stream_types != playbin->active_stream_types);
3153 
3154   GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT,
3155       STREAM_TYPES_ARGS (playbin->selected_stream_types));
3156   GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT,
3157       STREAM_TYPES_ARGS (playbin->active_stream_types));
3158 
3159   GST_PLAY_BIN3_LOCK (playbin);
3160 
3161   /* Make sure combiners/playsink are in sync with selected stream types */
3162   for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3163     GstSourceCombine *combine = &playbin->combiner[i];
3164     gboolean is_selected =
3165         (combine->stream_type & playbin->selected_stream_types) ==
3166         combine->stream_type;
3167     gboolean is_active =
3168         (combine->stream_type & playbin->active_stream_types) ==
3169         combine->stream_type;
3170 
3171     GST_DEBUG_OBJECT (playbin, "Stream type status: '%s' %s %s",
3172         combine->media_type, is_selected ? "selected" : "NOT selected",
3173         is_active ? "active" : "NOT active");
3174     /* FIXME : Remove asserts below once enough testing has been done */
3175 
3176     if (is_selected && is_active) {
3177       GST_DEBUG_OBJECT (playbin, "Stream type '%s' already active",
3178           combine->media_type);
3179     } else if (is_active && !is_selected) {
3180       GST_DEBUG_OBJECT (playbin, "Stream type '%s' is no longer requested",
3181           combine->media_type);
3182 
3183       /* Unlink combiner from sink */
3184       if (combine->srcpad) {
3185         GST_LOG_OBJECT (playbin, "Unlinking from sink");
3186         if (combine->sinkpad)
3187           gst_pad_unlink (combine->srcpad, combine->sinkpad);
3188         gst_object_unref (combine->srcpad);
3189         combine->srcpad = NULL;
3190       }
3191 
3192       if (combine->sinkpad) {
3193         /* Release playsink sink pad */
3194         GST_LOG_OBJECT (playbin, "Releasing playsink pad");
3195         gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
3196         gst_object_unref (combine->sinkpad);
3197         combine->sinkpad = NULL;
3198       }
3199 
3200       /* Release combiner */
3201       GST_FIXME_OBJECT (playbin, "Release combiner");
3202       remove_combiner (playbin, combine);
3203     } else if (!is_active && is_selected) {
3204       GST_DEBUG_OBJECT (playbin, "Stream type '%s' is now requested",
3205           combine->media_type);
3206 
3207       /* If we are shutting down, do *not* add more combiners */
3208       if (g_atomic_int_get (&playbin->shutdown))
3209         continue;
3210 
3211       g_assert (combine->sinkpad == NULL);
3212 
3213       /* Request playsink sink pad */
3214       combine->sinkpad =
3215           gst_play_sink_request_pad (playbin->playsink, combine->type);
3216       gst_object_ref (combine->sinkpad);
3217       /* Create combiner if needed and link it */
3218       create_combiner (playbin, combine);
3219       if (combine->combiner) {
3220         res = gst_pad_link (combine->srcpad, combine->sinkpad);
3221         GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
3222             combine->media_type, res);
3223         if (res != GST_PAD_LINK_OK) {
3224           GST_ELEMENT_ERROR (playbin, CORE, PAD,
3225               ("Internal playbin error."),
3226               ("Failed to link combiner to sink. Error %d", res));
3227         }
3228 
3229       }
3230     }
3231   }
3232 
3233   playbin->active_stream_types = playbin->selected_stream_types;
3234 
3235   GST_PLAY_BIN3_UNLOCK (playbin);
3236 
3237   gst_play_sink_reconfigure (playbin->playsink);
3238 
3239   do_async_done (playbin);
3240 
3241   GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT,
3242       STREAM_TYPES_ARGS (playbin->selected_stream_types));
3243   GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT,
3244       STREAM_TYPES_ARGS (playbin->active_stream_types));
3245 
3246   return;
3247 }
3248 
3249 static void
about_to_finish_cb(GstElement * uridecodebin,GstSourceGroup * group)3250 about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group)
3251 {
3252   GstPlayBin3 *playbin = group->playbin;
3253   GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
3254 
3255   GST_LOG_OBJECT (playbin, "selected_stream_types:%" STREAM_TYPES_FORMAT,
3256       STREAM_TYPES_ARGS (group->selected_stream_types));
3257   GST_LOG_OBJECT (playbin, "present_stream_types:%" STREAM_TYPES_FORMAT,
3258       STREAM_TYPES_ARGS (group->present_stream_types));
3259 
3260   if (group->selected_stream_types == 0
3261       || (group->selected_stream_types != group->present_stream_types)) {
3262     GST_LOG_OBJECT (playbin,
3263         "Delaying emission of signal until this group is ready");
3264     group->pending_about_to_finish = TRUE;
3265   } else
3266     emit_about_to_finish (playbin);
3267 }
3268 
3269 #if 0                           /* AUTOPLUG DISABLED */
3270 /* Like gst_element_factory_can_sink_any_caps() but doesn't
3271  * allow ANY caps on the sinkpad template */
3272 static gboolean
3273 _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
3274 {
3275   const GList *templs;
3276 
3277   templs = gst_element_factory_get_static_pad_templates (factory);
3278 
3279   while (templs) {
3280     GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data;
3281 
3282     if (templ->direction == GST_PAD_SINK) {
3283       GstCaps *templcaps = gst_static_caps_get (&templ->static_caps);
3284 
3285       if (!gst_caps_is_any (templcaps)
3286           && gst_caps_is_subset (caps, templcaps)) {
3287         gst_caps_unref (templcaps);
3288         return TRUE;
3289       }
3290       gst_caps_unref (templcaps);
3291     }
3292     templs = g_list_next (templs);
3293   }
3294 
3295   return FALSE;
3296 }
3297 
3298 static void
3299 avelements_free (gpointer avelement)
3300 {
3301   GstAVElement *elm = (GstAVElement *) avelement;
3302 
3303   if (elm->dec)
3304     gst_object_unref (elm->dec);
3305   if (elm->sink)
3306     gst_object_unref (elm->sink);
3307   g_slice_free (GstAVElement, elm);
3308 }
3309 
3310 static gint
3311 avelement_compare_decoder (gconstpointer p1, gconstpointer p2,
3312     gpointer user_data)
3313 {
3314   GstAVElement *v1, *v2;
3315 
3316   v1 = (GstAVElement *) p1;
3317   v2 = (GstAVElement *) p2;
3318 
3319   return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (v2->dec));
3320 }
3321 
3322 static gint
3323 avelement_lookup_decoder (gconstpointer p1, gconstpointer p2,
3324     gpointer user_data)
3325 {
3326   GstAVElement *v1;
3327   GstElementFactory *f2;
3328 
3329   v1 = (GstAVElement *) p1;
3330   f2 = (GstElementFactory *) p2;
3331 
3332   return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (f2));
3333 }
3334 
3335 static gint
3336 avelement_compare (gconstpointer p1, gconstpointer p2)
3337 {
3338   GstAVElement *v1, *v2;
3339   GstPluginFeature *fd1, *fd2, *fs1, *fs2;
3340   gint64 diff, v1_rank, v2_rank;
3341 
3342   v1 = (GstAVElement *) p1;
3343   v2 = (GstAVElement *) p2;
3344 
3345   fd1 = (GstPluginFeature *) v1->dec;
3346   fd2 = (GstPluginFeature *) v2->dec;
3347 
3348   /* If both have a sink, we also compare their ranks */
3349   if (v1->sink && v2->sink) {
3350     fs1 = (GstPluginFeature *) v1->sink;
3351     fs2 = (GstPluginFeature *) v2->sink;
3352     v1_rank =
3353         gst_plugin_feature_get_rank (fd1) * gst_plugin_feature_get_rank (fs1);
3354     v2_rank =
3355         gst_plugin_feature_get_rank (fd2) * gst_plugin_feature_get_rank (fs2);
3356   } else {
3357     v1_rank = gst_plugin_feature_get_rank (fd1);
3358     v2_rank = gst_plugin_feature_get_rank (fd2);
3359     fs1 = fs2 = NULL;
3360   }
3361 
3362   /* comparison based on the rank */
3363   diff = v2_rank - v1_rank;
3364   if (diff < 0)
3365     return -1;
3366   else if (diff > 0)
3367     return 1;
3368 
3369   /* comparison based on number of common caps features */
3370   diff = v2->n_comm_cf - v1->n_comm_cf;
3371   if (diff != 0)
3372     return diff;
3373 
3374   if (fs1 && fs2) {
3375     /* comparison based on the name of sink elements */
3376     diff = strcmp (GST_OBJECT_NAME (fs1), GST_OBJECT_NAME (fs2));
3377     if (diff != 0)
3378       return diff;
3379   }
3380 
3381   /* comparison based on the name of decoder elements */
3382   return strcmp (GST_OBJECT_NAME (fd1), GST_OBJECT_NAME (fd2));
3383 }
3384 
3385 static GSequence *
3386 avelements_create (GstPlayBin3 * playbin, gboolean isaudioelement)
3387 {
3388   GstElementFactory *d_factory, *s_factory;
3389   GList *dec_list, *sink_list, *dl, *sl;
3390   GSequence *ave_seq = NULL;
3391   GstAVElement *ave;
3392   guint n_common_cf = 0;
3393 
3394   if (isaudioelement) {
3395     sink_list = gst_element_factory_list_get_elements
3396         (GST_ELEMENT_FACTORY_TYPE_SINK |
3397         GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
3398     dec_list =
3399         gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
3400         | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
3401   } else {
3402     sink_list = gst_element_factory_list_get_elements
3403         (GST_ELEMENT_FACTORY_TYPE_SINK |
3404         GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3405         GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
3406 
3407     dec_list =
3408         gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
3409         | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3410         GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
3411   }
3412 
3413   /* create a list of audio/video elements. Each element in the list
3414    * is holding an audio/video decoder and an audio/video sink in which
3415    * the decoders srcpad template caps and sink element's sinkpad template
3416    * caps are compatible */
3417   dl = dec_list;
3418   sl = sink_list;
3419 
3420   ave_seq = g_sequence_new ((GDestroyNotify) avelements_free);
3421 
3422   for (; dl; dl = dl->next) {
3423     d_factory = (GstElementFactory *) dl->data;
3424     for (; sl; sl = sl->next) {
3425       s_factory = (GstElementFactory *) sl->data;
3426 
3427       n_common_cf =
3428           gst_playback_utils_get_n_common_capsfeatures (d_factory, s_factory,
3429           gst_play_bin3_get_flags (playbin), isaudioelement);
3430       if (n_common_cf < 1)
3431         continue;
3432 
3433       ave = g_slice_new (GstAVElement);
3434       ave->dec = gst_object_ref (d_factory);
3435       ave->sink = gst_object_ref (s_factory);
3436       ave->n_comm_cf = n_common_cf;
3437       g_sequence_append (ave_seq, ave);
3438     }
3439     sl = sink_list;
3440   }
3441   g_sequence_sort (ave_seq, (GCompareDataFunc) avelement_compare_decoder, NULL);
3442 
3443   gst_plugin_feature_list_free (dec_list);
3444   gst_plugin_feature_list_free (sink_list);
3445 
3446   return ave_seq;
3447 }
3448 
3449 static gboolean
3450 avelement_iter_is_equal (GSequenceIter * iter, GstElementFactory * factory)
3451 {
3452   GstAVElement *ave;
3453 
3454   if (!iter)
3455     return FALSE;
3456 
3457   ave = g_sequence_get (iter);
3458   if (!ave)
3459     return FALSE;
3460 
3461   return strcmp (GST_OBJECT_NAME (ave->dec), GST_OBJECT_NAME (factory)) == 0;
3462 }
3463 
3464 static GList *
3465 create_decoders_list (GList * factory_list, GSequence * avelements)
3466 {
3467   GList *dec_list = NULL, *tmp;
3468   GList *ave_list = NULL;
3469   GList *ave_free_list = NULL;
3470   GstAVElement *ave, *best_ave;
3471 
3472   g_return_val_if_fail (factory_list != NULL, NULL);
3473   g_return_val_if_fail (avelements != NULL, NULL);
3474 
3475   for (tmp = factory_list; tmp; tmp = tmp->next) {
3476     GstElementFactory *factory = (GstElementFactory *) tmp->data;
3477 
3478     /* if there are parsers or sink elements, add them first */
3479     if (gst_element_factory_list_is_type (factory,
3480             GST_ELEMENT_FACTORY_TYPE_PARSER) ||
3481         gst_element_factory_list_is_type (factory,
3482             GST_ELEMENT_FACTORY_TYPE_SINK)) {
3483       dec_list = g_list_prepend (dec_list, gst_object_ref (factory));
3484     } else {
3485       GSequenceIter *seq_iter;
3486 
3487       seq_iter =
3488           g_sequence_lookup (avelements, factory,
3489           (GCompareDataFunc) avelement_lookup_decoder, NULL);
3490       if (!seq_iter) {
3491         GstAVElement *ave = g_slice_new0 (GstAVElement);
3492 
3493         ave->dec = factory;
3494         ave->sink = NULL;
3495         /* There's at least raw */
3496         ave->n_comm_cf = 1;
3497 
3498         ave_list = g_list_prepend (ave_list, ave);
3499 
3500         /* We need to free these later */
3501         ave_free_list = g_list_prepend (ave_free_list, ave);
3502         continue;
3503       }
3504 
3505       /* Go to first iter with that decoder */
3506       do {
3507         GSequenceIter *tmp_seq_iter;
3508 
3509         tmp_seq_iter = g_sequence_iter_prev (seq_iter);
3510         if (!avelement_iter_is_equal (tmp_seq_iter, factory))
3511           break;
3512         seq_iter = tmp_seq_iter;
3513       } while (!g_sequence_iter_is_begin (seq_iter));
3514 
3515       /* Get the best ranked GstAVElement for that factory */
3516       best_ave = NULL;
3517       while (!g_sequence_iter_is_end (seq_iter)
3518           && avelement_iter_is_equal (seq_iter, factory)) {
3519         ave = g_sequence_get (seq_iter);
3520 
3521         if (!best_ave || avelement_compare (ave, best_ave) < 0)
3522           best_ave = ave;
3523 
3524         seq_iter = g_sequence_iter_next (seq_iter);
3525       }
3526       ave_list = g_list_prepend (ave_list, best_ave);
3527     }
3528   }
3529 
3530   /* Sort all GstAVElements by their relative ranks and insert
3531    * into the decoders list */
3532   ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
3533   for (tmp = ave_list; tmp; tmp = tmp->next) {
3534     ave = (GstAVElement *) tmp->data;
3535     dec_list = g_list_prepend (dec_list, gst_object_ref (ave->dec));
3536   }
3537   g_list_free (ave_list);
3538   gst_plugin_feature_list_free (factory_list);
3539 
3540   for (tmp = ave_free_list; tmp; tmp = tmp->next)
3541     g_slice_free (GstAVElement, tmp->data);
3542   g_list_free (ave_free_list);
3543 
3544   dec_list = g_list_reverse (dec_list);
3545 
3546   return dec_list;
3547 }
3548 
3549 /* Called when we must provide a list of factories to plug to @pad with @caps.
3550  * We first check if we have a sink that can handle the format and if we do, we
3551  * return NULL, to expose the pad. If we have no sink (or the sink does not
3552  * work), we return the list of elements that can connect. */
3553 static GValueArray *
3554 autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
3555     GstCaps * caps, GstSourceGroup * group)
3556 {
3557   GstPlayBin3 *playbin;
3558   GList *factory_list, *tmp;
3559   GValueArray *result;
3560   gboolean unref_caps = FALSE;
3561   gboolean isaudiodeclist = FALSE;
3562   gboolean isvideodeclist = FALSE;
3563 
3564   if (!caps) {
3565     caps = gst_caps_new_any ();
3566     unref_caps = TRUE;
3567   }
3568 
3569   playbin = group->playbin;
3570 
3571   GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
3572       group, GST_DEBUG_PAD_NAME (pad), caps);
3573 
3574   /* filter out the elements based on the caps. */
3575   g_mutex_lock (&playbin->elements_lock);
3576   gst_play_bin3_update_elements_list (playbin);
3577   factory_list =
3578       gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
3579       gst_caps_is_fixed (caps));
3580   g_mutex_unlock (&playbin->elements_lock);
3581 
3582   GST_DEBUG_OBJECT (playbin, "found factories %p", factory_list);
3583   GST_PLUGIN_FEATURE_LIST_DEBUG (factory_list);
3584 
3585   /* check whether the caps are asking for a list of audio/video decoders */
3586   tmp = factory_list;
3587   if (!gst_caps_is_any (caps)) {
3588     for (; tmp; tmp = tmp->next) {
3589       GstElementFactory *factory = (GstElementFactory *) tmp->data;
3590 
3591       isvideodeclist = gst_element_factory_list_is_type (factory,
3592           GST_ELEMENT_FACTORY_TYPE_DECODER |
3593           GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3594           GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
3595       isaudiodeclist = gst_element_factory_list_is_type (factory,
3596           GST_ELEMENT_FACTORY_TYPE_DECODER |
3597           GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
3598 
3599       if (isaudiodeclist || isvideodeclist)
3600         break;
3601     }
3602   }
3603 
3604   if (isaudiodeclist || isvideodeclist) {
3605     GSequence **ave_list;
3606     if (isaudiodeclist)
3607       ave_list = &playbin->aelements;
3608     else
3609       ave_list = &playbin->velements;
3610 
3611     g_mutex_lock (&playbin->elements_lock);
3612     /* sort factory_list based on the GstAVElement list priority */
3613     factory_list = create_decoders_list (factory_list, *ave_list);
3614     g_mutex_unlock (&playbin->elements_lock);
3615   }
3616 
3617   /* 2 additional elements for the already set audio/video sinks */
3618   result = g_value_array_new (g_list_length (factory_list) + 2);
3619 
3620   /* Check if we already have an audio/video sink and if this is the case
3621    * put it as the first element of the array */
3622   if (group->audio_sink) {
3623     GstElementFactory *factory = gst_element_get_factory (group->audio_sink);
3624 
3625     if (factory && _factory_can_sink_caps (factory, caps)) {
3626       GValue val = { 0, };
3627 
3628       g_value_init (&val, G_TYPE_OBJECT);
3629       g_value_set_object (&val, factory);
3630       result = g_value_array_append (result, &val);
3631       g_value_unset (&val);
3632     }
3633   }
3634 
3635   if (group->video_sink) {
3636     GstElementFactory *factory = gst_element_get_factory (group->video_sink);
3637 
3638     if (factory && _factory_can_sink_caps (factory, caps)) {
3639       GValue val = { 0, };
3640 
3641       g_value_init (&val, G_TYPE_OBJECT);
3642       g_value_set_object (&val, factory);
3643       result = g_value_array_append (result, &val);
3644       g_value_unset (&val);
3645     }
3646   }
3647 
3648   for (tmp = factory_list; tmp; tmp = tmp->next) {
3649     GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
3650     GValue val = { 0, };
3651 
3652     if (group->audio_sink && gst_element_factory_list_is_type (factory,
3653             GST_ELEMENT_FACTORY_TYPE_SINK |
3654             GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
3655       continue;
3656     }
3657     if (group->video_sink && gst_element_factory_list_is_type (factory,
3658             GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO
3659             | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
3660       continue;
3661     }
3662 
3663     g_value_init (&val, G_TYPE_OBJECT);
3664     g_value_set_object (&val, factory);
3665     g_value_array_append (result, &val);
3666     g_value_unset (&val);
3667   }
3668   gst_plugin_feature_list_free (factory_list);
3669 
3670   if (unref_caps)
3671     gst_caps_unref (caps);
3672 
3673   return result;
3674 }
3675 #endif
3676 
3677 static GstBusSyncReply
activate_sink_bus_handler(GstBus * bus,GstMessage * msg,GstPlayBin3 * playbin)3678 activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
3679     GstPlayBin3 * playbin)
3680 {
3681   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
3682     /* Only proxy errors from a fixed sink. If that fails we can just error out
3683      * early as stuff will fail later anyway */
3684     if (playbin->audio_sink
3685         && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
3686             GST_OBJECT_CAST (playbin->audio_sink)))
3687       gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
3688     else if (playbin->video_sink
3689         && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
3690             GST_OBJECT_CAST (playbin->video_sink)))
3691       gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
3692     else if (playbin->text_sink
3693         && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
3694             GST_OBJECT_CAST (playbin->text_sink)))
3695       gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
3696     else
3697       gst_message_unref (msg);
3698   } else {
3699     gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
3700   }
3701 
3702   /* Doesn't really matter, nothing is using this bus */
3703   return GST_BUS_DROP;
3704 }
3705 
3706 static gboolean
activate_sink(GstPlayBin3 * playbin,GstElement * sink,gboolean * activated)3707 activate_sink (GstPlayBin3 * playbin, GstElement * sink, gboolean * activated)
3708 {
3709   GstState state;
3710   GstBus *bus = NULL;
3711   GstStateChangeReturn sret;
3712   gboolean ret = FALSE;
3713 
3714   if (activated)
3715     *activated = FALSE;
3716 
3717   GST_OBJECT_LOCK (sink);
3718   state = GST_STATE (sink);
3719   GST_OBJECT_UNLOCK (sink);
3720   if (state >= GST_STATE_READY) {
3721     ret = TRUE;
3722     goto done;
3723   }
3724 
3725   if (!GST_OBJECT_PARENT (sink)) {
3726     bus = gst_bus_new ();
3727     gst_bus_set_sync_handler (bus,
3728         (GstBusSyncHandler) activate_sink_bus_handler, playbin, NULL);
3729     gst_element_set_bus (sink, bus);
3730   }
3731 
3732   sret = gst_element_set_state (sink, GST_STATE_READY);
3733   if (sret == GST_STATE_CHANGE_FAILURE)
3734     goto done;
3735 
3736   if (activated)
3737     *activated = TRUE;
3738   ret = TRUE;
3739 
3740 done:
3741   if (bus) {
3742     gst_element_set_bus (sink, NULL);
3743     gst_object_unref (bus);
3744   }
3745 
3746   return ret;
3747 }
3748 
3749 #if 0                           /* AUTOPLUG DISABLED */
3750 /* autoplug-continue decides, if a pad has raw caps that can be exposed
3751  * directly or if further decoding is necessary. We use this to expose
3752  * supported subtitles directly */
3753 
3754 /* FIXME 0.11: Remove the checks for ANY caps, a sink should specify
3755  * explicitly the caps it supports and if it claims to support ANY
3756  * caps it really should support everything */
3757 static gboolean
3758 autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps,
3759     GstSourceGroup * group)
3760 {
3761   gboolean ret = TRUE;
3762   GstPad *sinkpad = NULL;
3763   gboolean activated_sink;
3764 
3765   GST_SOURCE_GROUP_LOCK (group);
3766 
3767   if (group->text_sink &&
3768       activate_sink (group->playbin, group->text_sink, &activated_sink)) {
3769     sinkpad = gst_element_get_static_pad (group->text_sink, "sink");
3770     if (sinkpad) {
3771       GstCaps *sinkcaps;
3772 
3773       sinkcaps = gst_pad_query_caps (sinkpad, NULL);
3774       if (!gst_caps_is_any (sinkcaps))
3775         ret = !gst_pad_query_accept_caps (sinkpad, caps);
3776       gst_caps_unref (sinkcaps);
3777       gst_object_unref (sinkpad);
3778     }
3779     if (activated_sink)
3780       gst_element_set_state (group->text_sink, GST_STATE_NULL);
3781   } else {
3782     GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
3783     ret = !gst_caps_is_subset (caps, subcaps);
3784     gst_caps_unref (subcaps);
3785   }
3786   /* If autoplugging can stop don't do additional checks */
3787   if (!ret)
3788     goto done;
3789 
3790   if (group->audio_sink &&
3791       activate_sink (group->playbin, group->audio_sink, &activated_sink)) {
3792 
3793     sinkpad = gst_element_get_static_pad (group->audio_sink, "sink");
3794     if (sinkpad) {
3795       GstCaps *sinkcaps;
3796 
3797       sinkcaps = gst_pad_query_caps (sinkpad, NULL);
3798       if (!gst_caps_is_any (sinkcaps))
3799         ret = !gst_pad_query_accept_caps (sinkpad, caps);
3800       gst_caps_unref (sinkcaps);
3801       gst_object_unref (sinkpad);
3802     }
3803     if (activated_sink)
3804       gst_element_set_state (group->audio_sink, GST_STATE_NULL);
3805   }
3806   if (!ret)
3807     goto done;
3808 
3809   if (group->video_sink
3810       && activate_sink (group->playbin, group->video_sink, &activated_sink)) {
3811     sinkpad = gst_element_get_static_pad (group->video_sink, "sink");
3812     if (sinkpad) {
3813       GstCaps *sinkcaps;
3814 
3815       sinkcaps = gst_pad_query_caps (sinkpad, NULL);
3816       if (!gst_caps_is_any (sinkcaps))
3817         ret = !gst_pad_query_accept_caps (sinkpad, caps);
3818       gst_caps_unref (sinkcaps);
3819       gst_object_unref (sinkpad);
3820     }
3821     if (activated_sink)
3822       gst_element_set_state (group->video_sink, GST_STATE_NULL);
3823   }
3824 
3825 done:
3826   GST_SOURCE_GROUP_UNLOCK (group);
3827 
3828   GST_DEBUG_OBJECT (group->playbin,
3829       "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d",
3830       group, GST_DEBUG_PAD_NAME (pad), caps, ret);
3831 
3832   return ret;
3833 }
3834 
3835 static gboolean
3836 sink_accepts_caps (GstPlayBin3 * playbin, GstElement * sink, GstCaps * caps)
3837 {
3838   GstPad *sinkpad;
3839 
3840   if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
3841     /* Got the sink pad, now let's see if the element actually does accept the
3842      * caps that we have */
3843     if (!gst_pad_query_accept_caps (sinkpad, caps)) {
3844       gst_object_unref (sinkpad);
3845       return FALSE;
3846     }
3847     gst_object_unref (sinkpad);
3848   }
3849 
3850   return TRUE;
3851 }
3852 
3853 /* We are asked to select an element. See if the next element to check
3854  * is a sink. If this is the case, we see if the sink works by setting it to
3855  * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
3856  * expose the raw pad so that we can setup the mixers. */
3857 static GstAutoplugSelectResult
3858 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
3859     GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
3860 {
3861   GstPlayBin3 *playbin;
3862   GstElement *element;
3863   const gchar *klass;
3864   GstPlaySinkType type;
3865   GstElement **sinkp;
3866   GList *ave_list = NULL, *l;
3867   GstAVElement *ave = NULL;
3868   GSequence *ave_seq = NULL;
3869   GSequenceIter *seq_iter;
3870 
3871   playbin = group->playbin;
3872 
3873   GST_DEBUG_OBJECT (playbin, "select group %p for %s:%s, %" GST_PTR_FORMAT,
3874       group, GST_DEBUG_PAD_NAME (pad), caps);
3875 
3876   GST_DEBUG_OBJECT (playbin, "checking factory %s", GST_OBJECT_NAME (factory));
3877 
3878   /* if it's not a sink, we make sure the element is compatible with
3879    * the fixed sink */
3880   if (!gst_element_factory_list_is_type (factory,
3881           GST_ELEMENT_FACTORY_TYPE_SINK)) {
3882     gboolean isvideodec = gst_element_factory_list_is_type (factory,
3883         GST_ELEMENT_FACTORY_TYPE_DECODER |
3884         GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3885         GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
3886     gboolean isaudiodec = gst_element_factory_list_is_type (factory,
3887         GST_ELEMENT_FACTORY_TYPE_DECODER |
3888         GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
3889 
3890     if (!isvideodec && !isaudiodec)
3891       return GST_AUTOPLUG_SELECT_TRY;
3892 
3893     GST_SOURCE_GROUP_LOCK (group);
3894     g_mutex_lock (&playbin->elements_lock);
3895 
3896     if (isaudiodec) {
3897       ave_seq = playbin->aelements;
3898       sinkp = &group->audio_sink;
3899     } else {
3900       ave_seq = playbin->velements;
3901       sinkp = &group->video_sink;
3902     }
3903 
3904     seq_iter =
3905         g_sequence_lookup (ave_seq, factory,
3906         (GCompareDataFunc) avelement_lookup_decoder, NULL);
3907     if (seq_iter) {
3908       /* Go to first iter with that decoder */
3909       do {
3910         GSequenceIter *tmp_seq_iter;
3911 
3912         tmp_seq_iter = g_sequence_iter_prev (seq_iter);
3913         if (!avelement_iter_is_equal (tmp_seq_iter, factory))
3914           break;
3915         seq_iter = tmp_seq_iter;
3916       } while (!g_sequence_iter_is_begin (seq_iter));
3917 
3918       while (!g_sequence_iter_is_end (seq_iter)
3919           && avelement_iter_is_equal (seq_iter, factory)) {
3920         ave = g_sequence_get (seq_iter);
3921         ave_list = g_list_prepend (ave_list, ave);
3922         seq_iter = g_sequence_iter_next (seq_iter);
3923       }
3924 
3925       /* Sort all GstAVElements by their relative ranks and insert
3926        * into the decoders list */
3927       ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
3928     } else {
3929       ave_list = g_list_prepend (ave_list, NULL);
3930     }
3931 
3932     /* if it is a decoder and we don't have a fixed sink, then find out
3933      * the matching audio/video sink from GstAVElements list */
3934     for (l = ave_list; l; l = l->next) {
3935       gboolean created_sink = FALSE;
3936 
3937       ave = (GstAVElement *) l->data;
3938 
3939       if (((isaudiodec && !group->audio_sink) ||
3940               (isvideodec && !group->video_sink))) {
3941         if (ave && ave->sink) {
3942           GST_DEBUG_OBJECT (playbin,
3943               "Trying to create sink '%s' for decoder '%s'",
3944               gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)),
3945               gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
3946           if ((*sinkp = gst_element_factory_create (ave->sink, NULL)) == NULL) {
3947             GST_WARNING_OBJECT (playbin,
3948                 "Could not create an element from %s",
3949                 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
3950             continue;
3951           } else {
3952             if (!activate_sink (playbin, *sinkp, NULL)) {
3953               gst_object_unref (*sinkp);
3954               *sinkp = NULL;
3955               GST_WARNING_OBJECT (playbin,
3956                   "Could not activate sink %s",
3957                   gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
3958               continue;
3959             }
3960             gst_object_ref_sink (*sinkp);
3961             created_sink = TRUE;
3962           }
3963         }
3964       }
3965 
3966       /* If it is a decoder and we have a fixed sink for the media
3967        * type it outputs, check that the decoder is compatible with this sink */
3968       if ((isaudiodec && group->audio_sink) || (isvideodec
3969               && group->video_sink)) {
3970         gboolean compatible = FALSE;
3971         GstPad *sinkpad;
3972         GstCaps *caps;
3973         GstElement *sink;
3974 
3975         sink = *sinkp;
3976 
3977         if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
3978           GstPlayFlags flags = gst_play_bin3_get_flags (playbin);
3979           GstCaps *raw_caps =
3980               (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
3981               gst_static_caps_get (&raw_video_caps);
3982 
3983           caps = gst_pad_query_caps (sinkpad, NULL);
3984 
3985           /* If the sink supports raw audio/video, we first check
3986            * if the decoder could output any raw audio/video format
3987            * and assume it is compatible with the sink then. We don't
3988            * do a complete compatibility check here if converters
3989            * are plugged between the decoder and the sink because
3990            * the converters will convert between raw formats and
3991            * even if the decoder format is not supported by the decoder
3992            * a converter will convert it.
3993            *
3994            * We assume here that the converters can convert between
3995            * any raw format.
3996            */
3997           if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
3998                   && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
3999                   && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
4000                   && gst_caps_can_intersect (caps, raw_caps))) {
4001             compatible =
4002                 gst_element_factory_can_src_any_caps (factory, raw_caps)
4003                 || gst_element_factory_can_src_any_caps (factory, caps);
4004           } else {
4005             compatible = gst_element_factory_can_src_any_caps (factory, caps);
4006           }
4007 
4008           gst_object_unref (sinkpad);
4009           gst_caps_unref (caps);
4010         }
4011 
4012         if (compatible)
4013           break;
4014 
4015         GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
4016             GST_OBJECT_NAME (factory));
4017 
4018         /* If it is not compatible, either continue with the next possible
4019          * sink or if we have a fixed sink, skip the decoder */
4020         if (created_sink) {
4021           gst_element_set_state (*sinkp, GST_STATE_NULL);
4022           gst_object_unref (*sinkp);
4023           *sinkp = NULL;
4024         } else {
4025           g_mutex_unlock (&playbin->elements_lock);
4026           GST_SOURCE_GROUP_UNLOCK (group);
4027           return GST_AUTOPLUG_SELECT_SKIP;
4028         }
4029       }
4030     }
4031     g_list_free (ave_list);
4032     g_mutex_unlock (&playbin->elements_lock);
4033     GST_SOURCE_GROUP_UNLOCK (group);
4034     return GST_AUTOPLUG_SELECT_TRY;
4035   }
4036 
4037   /* it's a sink, see if an instance of it actually works */
4038   GST_DEBUG_OBJECT (playbin, "we found a sink '%s'", GST_OBJECT_NAME (factory));
4039 
4040   klass =
4041       gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
4042 
4043   /* figure out the klass */
4044   if (strstr (klass, "Audio")) {
4045     GST_DEBUG_OBJECT (playbin, "we found an audio sink");
4046     type = GST_PLAY_SINK_TYPE_AUDIO;
4047     sinkp = &group->audio_sink;
4048   } else if (strstr (klass, "Video")) {
4049     GST_DEBUG_OBJECT (playbin, "we found a video sink");
4050     type = GST_PLAY_SINK_TYPE_VIDEO;
4051     sinkp = &group->video_sink;
4052   } else {
4053     /* unknown klass, skip this element */
4054     GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
4055     return GST_AUTOPLUG_SELECT_SKIP;
4056   }
4057 
4058   /* if we are asked to do visualisations and it's an audio sink, skip the
4059    * element. We can only do visualisations with raw sinks */
4060   if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
4061     if (type == GST_PLAY_SINK_TYPE_AUDIO) {
4062       GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
4063       return GST_AUTOPLUG_SELECT_SKIP;
4064     }
4065   }
4066 
4067   /* now see if we already have a sink element */
4068   GST_SOURCE_GROUP_LOCK (group);
4069   if (*sinkp && GST_STATE (*sinkp) >= GST_STATE_READY) {
4070     GstElement *sink = gst_object_ref (*sinkp);
4071 
4072     if (sink_accepts_caps (playbin, sink, caps)) {
4073       GST_DEBUG_OBJECT (playbin,
4074           "Existing sink '%s' accepts caps: %" GST_PTR_FORMAT,
4075           GST_ELEMENT_NAME (sink), caps);
4076       gst_object_unref (sink);
4077       GST_SOURCE_GROUP_UNLOCK (group);
4078       return GST_AUTOPLUG_SELECT_EXPOSE;
4079     } else {
4080       GST_DEBUG_OBJECT (playbin,
4081           "Existing sink '%s' does not accept caps: %" GST_PTR_FORMAT,
4082           GST_ELEMENT_NAME (sink), caps);
4083       gst_object_unref (sink);
4084       GST_SOURCE_GROUP_UNLOCK (group);
4085       return GST_AUTOPLUG_SELECT_SKIP;
4086     }
4087   }
4088   GST_DEBUG_OBJECT (playbin, "we have no pending sink, try to create '%s'",
4089       gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4090 
4091   if ((*sinkp = gst_element_factory_create (factory, NULL)) == NULL) {
4092     GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
4093         gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4094     GST_SOURCE_GROUP_UNLOCK (group);
4095     return GST_AUTOPLUG_SELECT_SKIP;
4096   }
4097 
4098   element = *sinkp;
4099 
4100   if (!activate_sink (playbin, element, NULL)) {
4101     GST_WARNING_OBJECT (playbin, "Could not activate sink %s",
4102         gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4103     *sinkp = NULL;
4104     gst_object_unref (element);
4105     GST_SOURCE_GROUP_UNLOCK (group);
4106     return GST_AUTOPLUG_SELECT_SKIP;
4107   }
4108 
4109   /* Check if the selected sink actually supports the
4110    * caps and can be set to READY*/
4111   if (!sink_accepts_caps (playbin, element, caps)) {
4112     *sinkp = NULL;
4113     gst_element_set_state (element, GST_STATE_NULL);
4114     gst_object_unref (element);
4115     GST_SOURCE_GROUP_UNLOCK (group);
4116     return GST_AUTOPLUG_SELECT_SKIP;
4117   }
4118 
4119   /* remember the sink in the group now, the element is floating, we take
4120    * ownership now
4121    *
4122    * store the sink in the group, we will configure it later when we
4123    * reconfigure the sink */
4124   GST_DEBUG_OBJECT (playbin, "remember sink");
4125   gst_object_ref_sink (element);
4126   GST_SOURCE_GROUP_UNLOCK (group);
4127 
4128   /* tell decodebin to expose the pad because we are going to use this
4129    * sink */
4130   GST_DEBUG_OBJECT (playbin, "we found a working sink, expose pad");
4131 
4132   return GST_AUTOPLUG_SELECT_EXPOSE;
4133 }
4134 
4135 #define GST_PLAY_BIN3_FILTER_CAPS(filter,caps) G_STMT_START {                  \
4136   if ((filter)) {                                                             \
4137     GstCaps *intersection =                                                   \
4138         gst_caps_intersect_full ((filter), (caps), GST_CAPS_INTERSECT_FIRST); \
4139     gst_caps_unref ((caps));                                                  \
4140     (caps) = intersection;                                                    \
4141   }                                                                           \
4142 } G_STMT_END
4143 
4144 static gboolean
4145 autoplug_query_caps (GstElement * uridecodebin, GstPad * pad,
4146     GstElement * element, GstQuery * query, GstSourceGroup * group)
4147 {
4148   GstCaps *filter, *result = NULL;
4149   GstElement *sink;
4150   GstPad *sinkpad = NULL;
4151   GstElementFactory *factory;
4152   GstElementFactoryListType factory_type;
4153   gboolean have_sink = FALSE;
4154 
4155   GST_SOURCE_GROUP_LOCK (group);
4156   gst_query_parse_caps (query, &filter);
4157 
4158   factory = gst_element_get_factory (element);
4159   if (!factory)
4160     goto done;
4161 
4162   if (gst_element_factory_list_is_type (factory,
4163           GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4164           GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4165     factory_type =
4166         GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4167         GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE;
4168 
4169     if ((sink = group->video_sink)) {
4170       sinkpad = gst_element_get_static_pad (sink, "sink");
4171       if (sinkpad) {
4172         GstCaps *sinkcaps;
4173 
4174         sinkcaps = gst_pad_query_caps (sinkpad, filter);
4175         if (!gst_caps_is_any (sinkcaps)) {
4176           if (!result)
4177             result = sinkcaps;
4178           else
4179             result = gst_caps_merge (result, sinkcaps);
4180         } else {
4181           gst_caps_unref (sinkcaps);
4182         }
4183         gst_object_unref (sinkpad);
4184       }
4185       have_sink = TRUE;
4186     }
4187   } else if (gst_element_factory_list_is_type (factory,
4188           GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4189     factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
4190 
4191     if ((sink = group->audio_sink)) {
4192       sinkpad = gst_element_get_static_pad (sink, "sink");
4193       if (sinkpad) {
4194         GstCaps *sinkcaps;
4195 
4196         sinkcaps = gst_pad_query_caps (sinkpad, filter);
4197         if (!gst_caps_is_any (sinkcaps)) {
4198           if (!result)
4199             result = sinkcaps;
4200           else
4201             result = gst_caps_merge (result, sinkcaps);
4202         } else {
4203           gst_caps_unref (sinkcaps);
4204         }
4205         gst_object_unref (sinkpad);
4206       }
4207       have_sink = TRUE;
4208     }
4209   } else if (gst_element_factory_list_is_type (factory,
4210           GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
4211     factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE;
4212 
4213     if ((sink = group->playbin->text_sink)) {
4214       sinkpad = gst_element_get_static_pad (sink, "sink");
4215       if (sinkpad) {
4216         GstCaps *sinkcaps;
4217 
4218         sinkcaps = gst_pad_query_caps (sinkpad, filter);
4219         if (!gst_caps_is_any (sinkcaps)) {
4220           if (!result)
4221             result = sinkcaps;
4222           else
4223             result = gst_caps_merge (result, sinkcaps);
4224         } else {
4225           gst_caps_unref (sinkcaps);
4226         }
4227         gst_object_unref (sinkpad);
4228       }
4229       have_sink = TRUE;
4230     } else {
4231       GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4232       GST_PLAY_BIN3_FILTER_CAPS (filter, subcaps);
4233       if (!result)
4234         result = subcaps;
4235       else
4236         result = gst_caps_merge (result, subcaps);
4237     }
4238   } else {
4239     goto done;
4240   }
4241 
4242   if (!have_sink) {
4243     GValueArray *factories;
4244     gint i, n;
4245 
4246     factories = autoplug_factories_cb (uridecodebin, pad, NULL, group);
4247     n = factories->n_values;
4248     for (i = 0; i < n; i++) {
4249       GValue *v = g_value_array_get_nth (factories, i);
4250       GstElementFactory *f = g_value_get_object (v);
4251       const GList *templates;
4252       const GList *l;
4253       GstCaps *templ_caps;
4254 
4255       if (!gst_element_factory_list_is_type (f, factory_type))
4256         continue;
4257 
4258       templates = gst_element_factory_get_static_pad_templates (f);
4259 
4260       for (l = templates; l; l = l->next) {
4261         templ_caps = gst_static_pad_template_get_caps (l->data);
4262 
4263         if (!gst_caps_is_any (templ_caps)) {
4264           GST_PLAY_BIN3_FILTER_CAPS (filter, templ_caps);
4265           if (!result)
4266             result = templ_caps;
4267           else
4268             result = gst_caps_merge (result, templ_caps);
4269         } else {
4270           gst_caps_unref (templ_caps);
4271         }
4272       }
4273     }
4274     g_value_array_free (factories);
4275   }
4276 
4277 done:
4278   GST_SOURCE_GROUP_UNLOCK (group);
4279 
4280   if (!result)
4281     return FALSE;
4282 
4283   /* Add the actual decoder/parser/etc caps at the very end to
4284    * make sure we don't cause empty caps to be returned, e.g.
4285    * if a parser asks us but a decoder is required after it
4286    * because no sink can handle the format directly.
4287    */
4288   {
4289     GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4290 
4291     if (target) {
4292       GstCaps *target_caps = gst_pad_get_pad_template_caps (target);
4293       GST_PLAY_BIN3_FILTER_CAPS (filter, target_caps);
4294       result = gst_caps_merge (result, target_caps);
4295       gst_object_unref (target);
4296     }
4297   }
4298 
4299 
4300   gst_query_set_caps_result (query, result);
4301   gst_caps_unref (result);
4302 
4303   return TRUE;
4304 }
4305 
4306 static gboolean
4307 autoplug_query_context (GstElement * uridecodebin, GstPad * pad,
4308     GstElement * element, GstQuery * query, GstSourceGroup * group)
4309 {
4310   GstElement *sink;
4311   GstPad *sinkpad = NULL;
4312   GstElementFactory *factory;
4313   gboolean res = FALSE;
4314 
4315   GST_SOURCE_GROUP_LOCK (group);
4316 
4317   factory = gst_element_get_factory (element);
4318   if (!factory)
4319     goto done;
4320 
4321   if (gst_element_factory_list_is_type (factory,
4322           GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4323           GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4324     if ((sink = group->video_sink)) {
4325       sinkpad = gst_element_get_static_pad (sink, "sink");
4326       if (sinkpad) {
4327         res = gst_pad_query (sinkpad, query);
4328         gst_object_unref (sinkpad);
4329       }
4330     }
4331   } else if (gst_element_factory_list_is_type (factory,
4332           GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4333     if ((sink = group->audio_sink)) {
4334       sinkpad = gst_element_get_static_pad (sink, "sink");
4335       if (sinkpad) {
4336         res = gst_pad_query (sinkpad, query);
4337         gst_object_unref (sinkpad);
4338       }
4339     }
4340   } else if (gst_element_factory_list_is_type (factory,
4341           GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
4342     if ((sink = group->playbin->text_sink)) {
4343       sinkpad = gst_element_get_static_pad (sink, "sink");
4344       if (sinkpad) {
4345         res = gst_pad_query (sinkpad, query);
4346         gst_object_unref (sinkpad);
4347       }
4348     }
4349   } else {
4350     goto done;
4351   }
4352 
4353 done:
4354   GST_SOURCE_GROUP_UNLOCK (group);
4355 
4356   return res;
4357 }
4358 
4359 static gboolean
4360 autoplug_query_cb (GstElement * uridecodebin, GstPad * pad,
4361     GstElement * element, GstQuery * query, GstSourceGroup * group)
4362 {
4363 
4364   switch (GST_QUERY_TYPE (query)) {
4365     case GST_QUERY_CAPS:
4366       return autoplug_query_caps (uridecodebin, pad, element, query, group);
4367     case GST_QUERY_CONTEXT:
4368       return autoplug_query_context (uridecodebin, pad, element, query, group);
4369     default:
4370       return FALSE;
4371   }
4372 }
4373 #endif
4374 
4375 /* must be called with the group lock */
4376 static gboolean
group_set_locked_state_unlocked(GstPlayBin3 * playbin,GstSourceGroup * group,gboolean locked)4377 group_set_locked_state_unlocked (GstPlayBin3 * playbin, GstSourceGroup * group,
4378     gboolean locked)
4379 {
4380   GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
4381 
4382   if (group->uridecodebin)
4383     gst_element_set_locked_state (group->uridecodebin, locked);
4384 
4385   return TRUE;
4386 }
4387 
4388 static gboolean
make_or_reuse_element(GstPlayBin3 * playbin,const gchar * name,GstElement ** elem)4389 make_or_reuse_element (GstPlayBin3 * playbin, const gchar * name,
4390     GstElement ** elem)
4391 {
4392   if (*elem) {
4393     GST_DEBUG_OBJECT (playbin, "reusing existing %s", name);
4394     gst_element_set_state (*elem, GST_STATE_READY);
4395     /* no need to take extra ref, we already have one
4396      * and the bin will add one since it is no longer floating,
4397      * as we added a non-floating ref when removing it from the
4398      * bin earlier */
4399   } else {
4400     GstElement *new_elem;
4401     GST_DEBUG_OBJECT (playbin, "making new %s", name);
4402     new_elem = gst_element_factory_make (name, NULL);
4403     if (!new_elem)
4404       return FALSE;
4405     *elem = gst_object_ref (new_elem);
4406   }
4407 
4408   if (GST_OBJECT_PARENT (*elem) != GST_OBJECT_CAST (playbin))
4409     gst_bin_add (GST_BIN_CAST (playbin), *elem);
4410   return TRUE;
4411 }
4412 
4413 
4414 static void
source_setup_cb(GstElement * element,GstElement * source,GstSourceGroup * group)4415 source_setup_cb (GstElement * element, GstElement * source,
4416     GstSourceGroup * group)
4417 {
4418   g_signal_emit (group->playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0,
4419       source);
4420 }
4421 
4422 /* must be called with PLAY_BIN_LOCK */
4423 static GstStateChangeReturn
activate_group(GstPlayBin3 * playbin,GstSourceGroup * group)4424 activate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
4425 {
4426   GstElement *uridecodebin = NULL;
4427   GstPlayFlags flags;
4428   gboolean audio_sink_activated = FALSE;
4429   gboolean video_sink_activated = FALSE;
4430   gboolean text_sink_activated = FALSE;
4431   GstStateChangeReturn state_ret;
4432 
4433   g_return_val_if_fail (group->valid, GST_STATE_CHANGE_FAILURE);
4434   g_return_val_if_fail (!group->active, GST_STATE_CHANGE_FAILURE);
4435 
4436   GST_DEBUG_OBJECT (playbin, "activating group %p", group);
4437 
4438   GST_SOURCE_GROUP_LOCK (group);
4439 
4440   /* First set up the custom sinks */
4441   if (playbin->audio_sink)
4442     group->audio_sink = gst_object_ref (playbin->audio_sink);
4443   else
4444     group->audio_sink =
4445         gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO);
4446 
4447   if (group->audio_sink) {
4448     if (!activate_sink (playbin, group->audio_sink, &audio_sink_activated)) {
4449       if (group->audio_sink == playbin->audio_sink) {
4450         goto sink_failure;
4451       } else {
4452         gst_object_unref (group->audio_sink);
4453         group->audio_sink = NULL;
4454       }
4455     }
4456   }
4457 
4458   if (playbin->video_sink)
4459     group->video_sink = gst_object_ref (playbin->video_sink);
4460   else
4461     group->video_sink =
4462         gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO);
4463 
4464   if (group->video_sink) {
4465     if (!activate_sink (playbin, group->video_sink, &video_sink_activated)) {
4466       if (group->video_sink == playbin->video_sink) {
4467         goto sink_failure;
4468       } else {
4469         gst_object_unref (group->video_sink);
4470         group->video_sink = NULL;
4471       }
4472     }
4473   }
4474 
4475   if (playbin->text_sink)
4476     group->text_sink = gst_object_ref (playbin->text_sink);
4477   else
4478     group->text_sink =
4479         gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT);
4480 
4481   if (group->text_sink) {
4482     if (!activate_sink (playbin, group->text_sink, &text_sink_activated)) {
4483       if (group->text_sink == playbin->text_sink) {
4484         goto sink_failure;
4485       } else {
4486         gst_object_unref (group->text_sink);
4487         group->text_sink = NULL;
4488       }
4489     }
4490   }
4491 
4492 
4493   if (!make_or_reuse_element (playbin, "uridecodebin3", &group->uridecodebin))
4494     goto no_uridecodebin;
4495   uridecodebin = group->uridecodebin;
4496 
4497   flags = gst_play_sink_get_flags (playbin->playsink);
4498 
4499   g_object_set (uridecodebin,
4500       /* configure connection speed */
4501       "connection-speed", playbin->connection_speed / 1000,
4502       /* configure uri */
4503       "uri", group->uri,
4504       /* configure download buffering */
4505       "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
4506       /* configure buffering of demuxed/parsed data */
4507       "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
4508       /* configure buffering parameters */
4509       "buffer-duration", playbin->buffer_duration,
4510       "buffer-size", playbin->buffer_size,
4511       "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
4512 
4513   group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",
4514       G_CALLBACK (pad_added_cb), group);
4515   group->pad_removed_id = g_signal_connect (uridecodebin,
4516       "pad-removed", G_CALLBACK (pad_removed_cb), group);
4517   group->select_stream_id = g_signal_connect (uridecodebin, "select-stream",
4518       G_CALLBACK (select_stream_cb), group);
4519   group->source_setup_id = g_signal_connect (uridecodebin, "source-setup",
4520       G_CALLBACK (source_setup_cb), group);
4521   group->about_to_finish_id =
4522       g_signal_connect (uridecodebin, "about-to-finish",
4523       G_CALLBACK (about_to_finish_cb), group);
4524 
4525   if (group->suburi)
4526     g_object_set (group->uridecodebin, "suburi", group->suburi, NULL);
4527 
4528   /* release the group lock before setting the state of the source bins, they
4529    * might fire signals in this thread that we need to handle with the
4530    * group_lock taken. */
4531   GST_SOURCE_GROUP_UNLOCK (group);
4532 
4533   if ((state_ret =
4534           gst_element_set_state (uridecodebin,
4535               GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
4536     goto uridecodebin_failure;
4537 
4538   GST_SOURCE_GROUP_LOCK (group);
4539   /* allow state changes of the playbin affect the group elements now */
4540   group_set_locked_state_unlocked (playbin, group, FALSE);
4541   group->active = TRUE;
4542   GST_SOURCE_GROUP_UNLOCK (group);
4543 
4544   return state_ret;
4545 
4546   /* ERRORS */
4547 no_uridecodebin:
4548   {
4549     GstMessage *msg;
4550 
4551     GST_SOURCE_GROUP_UNLOCK (group);
4552     msg =
4553         gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
4554         "uridecodebin3");
4555     gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4556 
4557     GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
4558         (_("Could not create \"uridecodebin3\" element.")), (NULL));
4559 
4560     GST_SOURCE_GROUP_LOCK (group);
4561 
4562     goto error_cleanup;
4563   }
4564 uridecodebin_failure:
4565   {
4566     GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
4567     GST_SOURCE_GROUP_LOCK (group);
4568     goto error_cleanup;
4569   }
4570 sink_failure:
4571   {
4572     GST_ERROR_OBJECT (playbin, "failed to activate sinks");
4573     goto error_cleanup;
4574   }
4575 
4576 error_cleanup:
4577   {
4578     group->selected_stream_types = 0;
4579 
4580     /* delete any custom sinks we might have */
4581     if (group->audio_sink) {
4582       /* If this is a automatically created sink set it to NULL */
4583       if (audio_sink_activated)
4584         gst_element_set_state (group->audio_sink, GST_STATE_NULL);
4585       gst_object_unref (group->audio_sink);
4586     }
4587     group->audio_sink = NULL;
4588 
4589     if (group->video_sink) {
4590       /* If this is a automatically created sink set it to NULL */
4591       if (video_sink_activated)
4592         gst_element_set_state (group->video_sink, GST_STATE_NULL);
4593       gst_object_unref (group->video_sink);
4594     }
4595     group->video_sink = NULL;
4596 
4597     if (group->text_sink) {
4598       /* If this is a automatically created sink set it to NULL */
4599       if (text_sink_activated)
4600         gst_element_set_state (group->text_sink, GST_STATE_NULL);
4601       gst_object_unref (group->text_sink);
4602     }
4603     group->text_sink = NULL;
4604 
4605     if (uridecodebin) {
4606       REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
4607       REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
4608       REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id);
4609       REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
4610       REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
4611 #if 0
4612       REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
4613       REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
4614       REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
4615       REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
4616 #endif
4617 
4618       gst_element_set_state (uridecodebin, GST_STATE_NULL);
4619       gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin);
4620     }
4621 
4622     GST_SOURCE_GROUP_UNLOCK (group);
4623 
4624     return GST_STATE_CHANGE_FAILURE;
4625   }
4626 }
4627 
4628 /* must be called with PLAY_BIN_LOCK */
4629 static gboolean
deactivate_group(GstPlayBin3 * playbin,GstSourceGroup * group)4630 deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
4631 {
4632   g_return_val_if_fail (group->active, FALSE);
4633   g_return_val_if_fail (group->valid, FALSE);
4634 
4635   GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
4636 
4637   GST_SOURCE_GROUP_LOCK (group);
4638   group->active = FALSE;
4639   group->playing = FALSE;
4640   group->group_id = GST_GROUP_ID_INVALID;
4641 
4642   group->selected_stream_types = 0;
4643   /* Update global selected_stream_types */
4644   playbin->selected_stream_types =
4645       playbin->groups[0].selected_stream_types | playbin->
4646       groups[1].selected_stream_types;
4647   if (playbin->active_stream_types != playbin->selected_stream_types)
4648     reconfigure_output (playbin);
4649 
4650 #if 0
4651   /* delete any custom sinks we might have.
4652    * conditionally set them to null if they aren't inside playsink yet */
4653   if (group->audio_sink) {
4654     if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->audio_sink),
4655             GST_OBJECT_CAST (playbin->playsink))) {
4656       gst_element_set_state (group->audio_sink, GST_STATE_NULL);
4657     }
4658     gst_object_unref (group->audio_sink);
4659   }
4660   group->audio_sink = NULL;
4661   if (group->video_sink) {
4662     if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->video_sink),
4663             GST_OBJECT_CAST (playbin->playsink))) {
4664       gst_element_set_state (group->video_sink, GST_STATE_NULL);
4665     }
4666     gst_object_unref (group->video_sink);
4667   }
4668   group->video_sink = NULL;
4669   if (group->text_sink) {
4670     if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->text_sink),
4671             GST_OBJECT_CAST (playbin->playsink))) {
4672       gst_element_set_state (group->text_sink, GST_STATE_NULL);
4673     }
4674     gst_object_unref (group->text_sink);
4675   }
4676   group->text_sink = NULL;
4677 #endif
4678 
4679   if (group->uridecodebin) {
4680     REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id);
4681     REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
4682     REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
4683 
4684     gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
4685     gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
4686 
4687     REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
4688     REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
4689 #if 0
4690     REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
4691     REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
4692     REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
4693     REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
4694 #endif
4695   }
4696 
4697   GST_SOURCE_GROUP_UNLOCK (group);
4698 
4699   GST_DEBUG_OBJECT (playbin, "Done");
4700 
4701   return TRUE;
4702 }
4703 
4704 /* setup the next group to play, this assumes the next_group is valid and
4705  * configured. It swaps out the current_group and activates the valid
4706  * next_group. */
4707 static GstStateChangeReturn
setup_next_source(GstPlayBin3 * playbin)4708 setup_next_source (GstPlayBin3 * playbin)
4709 {
4710   GstSourceGroup *new_group;
4711   GstStateChangeReturn state_ret;
4712 
4713   GST_DEBUG_OBJECT (playbin, "setup next source");
4714 
4715   debug_groups (playbin);
4716 
4717   /* see if there is a next group */
4718   GST_PLAY_BIN3_LOCK (playbin);
4719   new_group = playbin->next_group;
4720   if (!new_group || !new_group->valid || new_group->active)
4721     goto no_next_group;
4722 
4723   /* activate the new group */
4724   state_ret = activate_group (playbin, new_group);
4725   if (state_ret == GST_STATE_CHANGE_FAILURE)
4726     goto activate_failed;
4727 
4728   GST_PLAY_BIN3_UNLOCK (playbin);
4729 
4730   debug_groups (playbin);
4731 
4732   return state_ret;
4733 
4734   /* ERRORS */
4735 no_next_group:
4736   {
4737     GST_DEBUG_OBJECT (playbin, "no next group");
4738     GST_PLAY_BIN3_UNLOCK (playbin);
4739     return GST_STATE_CHANGE_FAILURE;
4740   }
4741 activate_failed:
4742   {
4743     new_group->stream_changed_pending = FALSE;
4744     GST_DEBUG_OBJECT (playbin, "activate failed");
4745     new_group->valid = FALSE;
4746     GST_PLAY_BIN3_UNLOCK (playbin);
4747     return GST_STATE_CHANGE_FAILURE;
4748   }
4749 }
4750 
4751 /* The group that is currently playing is copied again to the
4752  * next_group so that it will start playing the next time.
4753  */
4754 static gboolean
save_current_group(GstPlayBin3 * playbin)4755 save_current_group (GstPlayBin3 * playbin)
4756 {
4757   GstSourceGroup *curr_group;
4758 
4759   GST_DEBUG_OBJECT (playbin, "save current group");
4760 
4761   /* see if there is a current group */
4762   GST_PLAY_BIN3_LOCK (playbin);
4763   curr_group = playbin->curr_group;
4764   if (curr_group && curr_group->valid && curr_group->active) {
4765     /* unlink our pads with the sink */
4766     deactivate_group (playbin, curr_group);
4767   }
4768   /* swap old and new */
4769   playbin->curr_group = playbin->next_group;
4770   playbin->next_group = curr_group;
4771   GST_PLAY_BIN3_UNLOCK (playbin);
4772 
4773   return TRUE;
4774 }
4775 
4776 /* clear the locked state from all groups. This function is called before a
4777  * state change to NULL is performed on them. */
4778 static gboolean
groups_set_locked_state(GstPlayBin3 * playbin,gboolean locked)4779 groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked)
4780 {
4781   GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
4782       locked);
4783 
4784   GST_PLAY_BIN3_LOCK (playbin);
4785   GST_SOURCE_GROUP_LOCK (playbin->curr_group);
4786   group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
4787   GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4788   GST_SOURCE_GROUP_LOCK (playbin->next_group);
4789   group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
4790   GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
4791   GST_PLAY_BIN3_UNLOCK (playbin);
4792 
4793   return TRUE;
4794 }
4795 
4796 static void
gst_play_bin3_check_group_status(GstPlayBin3 * playbin)4797 gst_play_bin3_check_group_status (GstPlayBin3 * playbin)
4798 {
4799   if (playbin->activation_task)
4800     gst_task_start (playbin->activation_task);
4801 }
4802 
4803 static void
gst_play_bin3_activation_thread(GstPlayBin3 * playbin)4804 gst_play_bin3_activation_thread (GstPlayBin3 * playbin)
4805 {
4806   GST_DEBUG_OBJECT (playbin, "starting");
4807 
4808   debug_groups (playbin);
4809 
4810   /* Check if next_group needs to be deactivated */
4811   GST_PLAY_BIN3_LOCK (playbin);
4812   if (playbin->next_group->active) {
4813     deactivate_group (playbin, playbin->next_group);
4814     playbin->next_group->valid = FALSE;
4815   }
4816 
4817   /* Is there a pending about-to-finish to be emitted ? */
4818   GST_SOURCE_GROUP_LOCK (playbin->curr_group);
4819   if (playbin->curr_group->pending_about_to_finish) {
4820     GST_LOG_OBJECT (playbin, "Propagating about-to-finish");
4821     playbin->curr_group->pending_about_to_finish = FALSE;
4822     GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4823     /* This will activate the next source afterwards */
4824     emit_about_to_finish (playbin);
4825   } else
4826     GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4827 
4828   GST_LOG_OBJECT (playbin, "Pausing task");
4829   if (playbin->activation_task)
4830     gst_task_pause (playbin->activation_task);
4831   GST_PLAY_BIN3_UNLOCK (playbin);
4832 
4833   GST_DEBUG_OBJECT (playbin, "done");
4834   return;
4835 }
4836 
4837 static gboolean
gst_play_bin3_start(GstPlayBin3 * playbin)4838 gst_play_bin3_start (GstPlayBin3 * playbin)
4839 {
4840   GST_DEBUG_OBJECT (playbin, "starting");
4841 
4842   GST_PLAY_BIN3_LOCK (playbin);
4843 
4844   if (playbin->activation_task == NULL) {
4845     playbin->activation_task =
4846         gst_task_new ((GstTaskFunction) gst_play_bin3_activation_thread,
4847         playbin, NULL);
4848     if (playbin->activation_task == NULL)
4849       goto task_error;
4850     gst_task_set_lock (playbin->activation_task, &playbin->activation_lock);
4851   }
4852   GST_LOG_OBJECT (playbin, "clearing shutdown flag");
4853   g_atomic_int_set (&playbin->shutdown, 0);
4854   do_async_start (playbin);
4855 
4856   GST_PLAY_BIN3_UNLOCK (playbin);
4857 
4858   return TRUE;
4859 
4860 task_error:
4861   {
4862     GST_PLAY_BIN3_UNLOCK (playbin);
4863     GST_ERROR_OBJECT (playbin, "Failed to create task");
4864     return FALSE;
4865   }
4866 }
4867 
4868 static void
gst_play_bin3_stop(GstPlayBin3 * playbin)4869 gst_play_bin3_stop (GstPlayBin3 * playbin)
4870 {
4871   GstTask *task;
4872 
4873   GST_DEBUG_OBJECT (playbin, "stopping");
4874 
4875   /* FIXME unlock our waiting groups */
4876   GST_LOG_OBJECT (playbin, "setting shutdown flag");
4877   g_atomic_int_set (&playbin->shutdown, 1);
4878 
4879   /* wait for all callbacks to end by taking the lock.
4880    * No dynamic (critical) new callbacks will
4881    * be able to happen as we set the shutdown flag. */
4882   GST_PLAY_BIN3_DYN_LOCK (playbin);
4883   GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
4884   GST_PLAY_BIN3_DYN_UNLOCK (playbin);
4885 
4886   /* Stop the activation task */
4887   GST_PLAY_BIN3_LOCK (playbin);
4888   if ((task = playbin->activation_task)) {
4889     playbin->activation_task = NULL;
4890     GST_PLAY_BIN3_UNLOCK (playbin);
4891 
4892     gst_task_stop (task);
4893 
4894     /* Make sure task is not running */
4895     g_rec_mutex_lock (&playbin->activation_lock);
4896     g_rec_mutex_unlock (&playbin->activation_lock);
4897 
4898     /* Wait for task to finish and unref it */
4899     gst_task_join (task);
4900     gst_object_unref (task);
4901 
4902     GST_PLAY_BIN3_LOCK (playbin);
4903   }
4904   GST_PLAY_BIN3_UNLOCK (playbin);
4905 }
4906 
4907 static GstStateChangeReturn
gst_play_bin3_change_state(GstElement * element,GstStateChange transition)4908 gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
4909 {
4910   GstStateChangeReturn ret;
4911   GstPlayBin3 *playbin;
4912   gboolean do_save = FALSE;
4913 
4914   playbin = GST_PLAY_BIN3 (element);
4915 
4916   switch (transition) {
4917     case GST_STATE_CHANGE_READY_TO_PAUSED:
4918       if (!gst_play_bin3_start (playbin))
4919         return GST_STATE_CHANGE_FAILURE;
4920       break;
4921     case GST_STATE_CHANGE_PAUSED_TO_READY:
4922     async_down:
4923       gst_play_bin3_stop (playbin);
4924       if (!do_save)
4925         break;
4926     case GST_STATE_CHANGE_READY_TO_NULL:
4927       /* we go async to PAUSED, so if that fails, we never make it to PAUSED
4928        * and we will never be called with the GST_STATE_CHANGE_PAUSED_TO_READY.
4929        * Make sure we do go through the same steps (see above) for
4930        * proper cleanup */
4931       if (!g_atomic_int_get (&playbin->shutdown)) {
4932         do_save = TRUE;
4933         goto async_down;
4934       }
4935 
4936       /* unlock so that all groups go to NULL */
4937       groups_set_locked_state (playbin, FALSE);
4938       break;
4939     default:
4940       break;
4941   }
4942 
4943   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4944   if (ret == GST_STATE_CHANGE_FAILURE)
4945     goto failure;
4946 
4947   switch (transition) {
4948     case GST_STATE_CHANGE_READY_TO_PAUSED:
4949       if ((ret = setup_next_source (playbin)) == GST_STATE_CHANGE_FAILURE)
4950         goto failure;
4951       if (ret == GST_STATE_CHANGE_SUCCESS)
4952         ret = GST_STATE_CHANGE_ASYNC;
4953 
4954       break;
4955     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4956       do_async_done (playbin);
4957       /* FIXME Release audio device when we implement that */
4958       break;
4959     case GST_STATE_CHANGE_PAUSED_TO_READY:
4960       save_current_group (playbin);
4961       break;
4962     case GST_STATE_CHANGE_READY_TO_NULL:
4963     {
4964       guint i;
4965 
4966       /* also do missed state change down to READY */
4967       if (do_save)
4968         save_current_group (playbin);
4969       /* Deactive the groups, set uridecodebin to NULL and unref it */
4970       for (i = 0; i < 2; i++) {
4971         if (playbin->groups[i].active && playbin->groups[i].valid) {
4972           deactivate_group (playbin, &playbin->groups[i]);
4973           playbin->groups[i].valid = FALSE;
4974         }
4975 
4976         if (playbin->groups[i].uridecodebin) {
4977           gst_element_set_state (playbin->groups[i].uridecodebin,
4978               GST_STATE_NULL);
4979           gst_object_unref (playbin->groups[i].uridecodebin);
4980           playbin->groups[i].uridecodebin = NULL;
4981         }
4982 
4983       }
4984 
4985       /* Set our sinks back to NULL, they might not be child of playbin */
4986       if (playbin->audio_sink)
4987         gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
4988       if (playbin->video_sink)
4989         gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
4990       if (playbin->text_sink)
4991         gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
4992 
4993       if (playbin->video_stream_combiner)
4994         gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
4995       if (playbin->audio_stream_combiner)
4996         gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
4997       if (playbin->text_stream_combiner)
4998         gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
4999 
5000       /* make sure the groups don't perform a state change anymore until we
5001        * enable them again */
5002       groups_set_locked_state (playbin, TRUE);
5003       break;
5004     }
5005     default:
5006       break;
5007   }
5008 
5009   if (ret == GST_STATE_CHANGE_NO_PREROLL)
5010     do_async_done (playbin);
5011 
5012   return ret;
5013 
5014   /* ERRORS */
5015 failure:
5016   {
5017     do_async_done (playbin);
5018 
5019     if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
5020       GstSourceGroup *curr_group;
5021 
5022       curr_group = playbin->curr_group;
5023       if (curr_group) {
5024         if (curr_group->active && curr_group->valid) {
5025           /* unlink our pads with the sink */
5026           deactivate_group (playbin, curr_group);
5027         }
5028         curr_group->valid = FALSE;
5029       }
5030 
5031       /* Swap current and next group back */
5032       playbin->curr_group = playbin->next_group;
5033       playbin->next_group = curr_group;
5034     }
5035     return ret;
5036   }
5037 }
5038 
5039 static void
gst_play_bin3_overlay_expose(GstVideoOverlay * overlay)5040 gst_play_bin3_overlay_expose (GstVideoOverlay * overlay)
5041 {
5042   GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5043 
5044   gst_video_overlay_expose (GST_VIDEO_OVERLAY (playbin->playsink));
5045 }
5046 
5047 static void
gst_play_bin3_overlay_handle_events(GstVideoOverlay * overlay,gboolean handle_events)5048 gst_play_bin3_overlay_handle_events (GstVideoOverlay * overlay,
5049     gboolean handle_events)
5050 {
5051   GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5052 
5053   gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (playbin->playsink),
5054       handle_events);
5055 }
5056 
5057 static void
gst_play_bin3_overlay_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)5058 gst_play_bin3_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
5059     gint y, gint width, gint height)
5060 {
5061   GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5062 
5063   gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (playbin->playsink),
5064       x, y, width, height);
5065 }
5066 
5067 static void
gst_play_bin3_overlay_set_window_handle(GstVideoOverlay * overlay,guintptr handle)5068 gst_play_bin3_overlay_set_window_handle (GstVideoOverlay * overlay,
5069     guintptr handle)
5070 {
5071   GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5072 
5073   gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (playbin->playsink),
5074       handle);
5075 }
5076 
5077 static void
gst_play_bin3_overlay_init(gpointer g_iface,gpointer g_iface_data)5078 gst_play_bin3_overlay_init (gpointer g_iface, gpointer g_iface_data)
5079 {
5080   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
5081   iface->expose = gst_play_bin3_overlay_expose;
5082   iface->handle_events = gst_play_bin3_overlay_handle_events;
5083   iface->set_render_rectangle = gst_play_bin3_overlay_set_render_rectangle;
5084   iface->set_window_handle = gst_play_bin3_overlay_set_window_handle;
5085 }
5086 
5087 static void
gst_play_bin3_navigation_send_event(GstNavigation * navigation,GstStructure * structure)5088 gst_play_bin3_navigation_send_event (GstNavigation * navigation,
5089     GstStructure * structure)
5090 {
5091   GstPlayBin3 *playbin = GST_PLAY_BIN3 (navigation);
5092 
5093   gst_navigation_send_event (GST_NAVIGATION (playbin->playsink), structure);
5094 }
5095 
5096 static void
gst_play_bin3_navigation_init(gpointer g_iface,gpointer g_iface_data)5097 gst_play_bin3_navigation_init (gpointer g_iface, gpointer g_iface_data)
5098 {
5099   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
5100 
5101   iface->send_event = gst_play_bin3_navigation_send_event;
5102 }
5103 
5104 static const GList *
gst_play_bin3_colorbalance_list_channels(GstColorBalance * balance)5105 gst_play_bin3_colorbalance_list_channels (GstColorBalance * balance)
5106 {
5107   GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
5108 
5109   return
5110       gst_color_balance_list_channels (GST_COLOR_BALANCE (playbin->playsink));
5111 }
5112 
5113 static void
gst_play_bin3_colorbalance_set_value(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value)5114 gst_play_bin3_colorbalance_set_value (GstColorBalance * balance,
5115     GstColorBalanceChannel * channel, gint value)
5116 {
5117   GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
5118 
5119   gst_color_balance_set_value (GST_COLOR_BALANCE (playbin->playsink), channel,
5120       value);
5121 }
5122 
5123 static gint
gst_play_bin3_colorbalance_get_value(GstColorBalance * balance,GstColorBalanceChannel * channel)5124 gst_play_bin3_colorbalance_get_value (GstColorBalance * balance,
5125     GstColorBalanceChannel * channel)
5126 {
5127   GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
5128 
5129   return gst_color_balance_get_value (GST_COLOR_BALANCE (playbin->playsink),
5130       channel);
5131 }
5132 
5133 static GstColorBalanceType
gst_play_bin3_colorbalance_get_balance_type(GstColorBalance * balance)5134 gst_play_bin3_colorbalance_get_balance_type (GstColorBalance * balance)
5135 {
5136   GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
5137 
5138   return
5139       gst_color_balance_get_balance_type (GST_COLOR_BALANCE
5140       (playbin->playsink));
5141 }
5142 
5143 static void
gst_play_bin3_colorbalance_init(gpointer g_iface,gpointer g_iface_data)5144 gst_play_bin3_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
5145 {
5146   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
5147 
5148   iface->list_channels = gst_play_bin3_colorbalance_list_channels;
5149   iface->set_value = gst_play_bin3_colorbalance_set_value;
5150   iface->get_value = gst_play_bin3_colorbalance_get_value;
5151   iface->get_balance_type = gst_play_bin3_colorbalance_get_balance_type;
5152 }
5153 
5154 gboolean
gst_play_bin3_plugin_init(GstPlugin * plugin,gboolean as_playbin)5155 gst_play_bin3_plugin_init (GstPlugin * plugin, gboolean as_playbin)
5156 {
5157   GST_DEBUG_CATEGORY_INIT (gst_play_bin3_debug, "playbin3", 0, "play bin");
5158 
5159   if (as_playbin)
5160     return gst_element_register (plugin, "playbin", GST_RANK_NONE,
5161         GST_TYPE_PLAY_BIN);
5162 
5163   return gst_element_register (plugin, "playbin3", GST_RANK_NONE,
5164       GST_TYPE_PLAY_BIN);
5165 }
5166