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