seek: Add support for setting a subtitle URI
[platform/upstream/gstreamer.git] / gst / playback / gstplaybin2.c
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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-playbin2
23  *
24  * Playbin2 provides a stand-alone everything-in-one abstraction for an
25  * audio and/or video player.
26  *
27  * playbin2 is considered stable now. It is the prefered playback API now,
28  * and replaces the old #playbin element, which is no longer supported.
29  *
30  * It can handle both audio and video files and features
31  * <itemizedlist>
32  * <listitem>
33  * automatic file type recognition and based on that automatic
34  * selection and usage of the right audio/video/subtitle demuxers/decoders
35  * </listitem>
36  * <listitem>
37  * visualisations for audio files
38  * </listitem>
39  * <listitem>
40  * subtitle support for video files. Subtitles can be store in external
41  * files.
42  * </listitem>
43  * <listitem>
44  * stream selection between different video/audio/subtitles streams
45  * </listitem>
46  * <listitem>
47  * meta info (tag) extraction
48  * </listitem>
49  * <listitem>
50  * easy access to the last video frame
51  * </listitem>
52  * <listitem>
53  * buffering when playing streams over a network
54  * </listitem>
55  * <listitem>
56  * volume control with mute option
57  * </listitem>
58  * </itemizedlist>
59  *
60  * <refsect2>
61  * <title>Usage</title>
62  * <para>
63  * A playbin2 element can be created just like any other element using
64  * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin2:uri
65  * property. This must be an absolute URI, relative file paths are not allowed.
66  * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
67  *
68  * Playbin is a #GstPipeline. It will notify the application of everything
69  * that's happening (errors, end of stream, tags found, state changes, etc.)
70  * by posting messages on its #GstBus. The application needs to watch the
71  * bus.
72  *
73  * Playback can be initiated by setting the element to PLAYING state using
74  * gst_element_set_state(). Note that the state change will take place in
75  * the background in a separate thread, when the function returns playback
76  * is probably not happening yet and any errors might not have occured yet.
77  * Applications using playbin should ideally be written to deal with things
78  * completely asynchroneous.
79  *
80  * When playback has finished (an EOS message has been received on the bus)
81  * or an error has occured (an ERROR message has been received on the bus) or
82  * the user wants to play a different track, playbin should be set back to
83  * READY or NULL state, then the #GstPlayBin2:uri property should be set to the
84  * new location and then playbin be set to PLAYING state again.
85  *
86  * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
87  * on the playbin element. Again, the seek will not be executed
88  * instantaneously, but will be done in a background thread. When the seek
89  * call returns the seek will most likely still be in process. An application
90  * may wait for the seek to finish (or fail) using gst_element_get_state() with
91  * -1 as the timeout, but this will block the user interface and is not
92  * recommended at all.
93  *
94  * Applications may query the current position and duration of the stream
95  * via gst_element_query_position() and gst_element_query_duration() and
96  * setting the format passed to GST_FORMAT_TIME. If the query was successful,
97  * the duration or position will have been returned in units of nanoseconds.
98  * </para>
99  * </refsect2>
100  * <refsect2>
101  * <title>Advanced Usage: specifying the audio and video sink</title>
102  * <para>
103  * By default, if no audio sink or video sink has been specified via the
104  * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property, playbin 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 playbin using the
113  * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property.
114  *
115  * GNOME-based applications, for example, will usually want to create
116  * gconfaudiosink and gconfvideosink elements and make playbin 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 playbin,
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  * </para>
133  * </refsect2>
134  * <refsect2>
135  * <title>Retrieving Tags and Other Meta Data</title>
136  * <para>
137  * Most of the common meta data (artist, title, etc.) can be retrieved by
138  * watching for TAG messages on the pipeline's bus (see above).
139  *
140  * Other more specific meta information like width/height/framerate of video
141  * streams or samplerate/number of channels of audio streams can be obtained
142  * from the negotiated caps on the sink pads of the sinks.
143  * </para>
144  * </refsect2>
145  * <refsect2>
146  * <title>Buffering</title>
147  * Playbin handles buffering automatically for the most part, but applications
148  * need to handle parts of the buffering process as well. Whenever playbin is
149  * buffering, it will post BUFFERING messages on the bus with a percentage
150  * value that shows the progress of the buffering process. Applications need
151  * to set playbin to PLAYING or PAUSED state in response to these messages.
152  * They may also want to convey the buffering progress to the user in some
153  * way. Here is how to extract the percentage information from the message
154  * (requires GStreamer >= 0.10.11):
155  * |[
156  * switch (GST_MESSAGE_TYPE (msg)) {
157  *   case GST_MESSAGE_BUFFERING: {
158  *     gint percent = 0;
159  *     gst_message_parse_buffering (msg, &amp;percent);
160  *     g_print ("Buffering (%%u percent done)", percent);
161  *     break;
162  *   }
163  *   ...
164  * }
165  * ]|
166  * Note that applications should keep/set the pipeline in the PAUSED state when
167  * a BUFFERING message is received with a buffer percent value < 100 and set
168  * the pipeline back to PLAYING state when a BUFFERING message with a value
169  * of 100 percent is received (if PLAYING is the desired state, that is).
170  * </refsect2>
171  * <refsect2>
172  * <title>Embedding the video window in your application</title>
173  * By default, playbin (or rather the video sinks used) will create their own
174  * window. Applications will usually want to force output to a window of their
175  * own, however. This can be done using the #GstXOverlay interface, which most
176  * video sinks implement. See the documentation there for more details.
177  * </refsect2>
178  * <refsect2>
179  * <title>Specifying which CD/DVD device to use</title>
180  * The device to use for CDs/DVDs needs to be set on the source element
181  * playbin creates before it is opened. The most generic way of doing this
182  * is to connect to playbin's "source-setup" (or "notify::source") signal,
183  * which will be emitted by playbin2 when it has created the source element
184  * for a particular URI. In the signal callback you can check if the source
185  * element has a "device" property and set it appropriately. In some cases
186  * the device can also be set as part of the URI, but it depends on the
187  * elements involved if this will work or not. For example, for DVD menu
188  * playback, the following syntax might work (if the resindvd plugin is used):
189  * dvd://[/path/to/device]
190  * </refsect2>
191  * <refsect2>
192  * <title>Handling redirects</title>
193  * <para>
194  * Some elements may post 'redirect' messages on the bus to tell the
195  * application to open another location. These are element messages containing
196  * a structure named 'redirect' along with a 'new-location' field of string
197  * type. The new location may be a relative or an absolute URI. Examples
198  * for such redirects can be found in many quicktime movie trailers.
199  * </para>
200  * </refsect2>
201  * <refsect2>
202  * <title>Examples</title>
203  * |[
204  * gst-launch -v playbin2 uri=file:///path/to/somefile.avi
205  * ]| This will play back the given AVI video file, given that the video and
206  * audio decoders required to decode the content are installed. Since no
207  * special audio sink or video sink is supplied (not possible via gst-launch),
208  * playbin will try to find a suitable audio and video sink automatically
209  * using the autoaudiosink and autovideosink elements.
210  * |[
211  * gst-launch -v playbin2 uri=cdda://4
212  * ]| This will play back track 4 on an audio CD in your disc drive (assuming
213  * the drive is detected automatically by the plugin).
214  * |[
215  * gst-launch -v playbin2 uri=dvd://
216  * ]| This will play back the DVD in your disc drive (assuming
217  * the drive is detected automatically by the plugin).
218  * </refsect2>
219  */
220
221 #ifdef HAVE_CONFIG_H
222 #include "config.h"
223 #endif
224
225 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
226  * with newer GLib versions (>= 2.31.0) */
227 #define GLIB_DISABLE_DEPRECATION_WARNINGS
228
229 #include <string.h>
230 #include <gst/gst.h>
231
232 #include <gst/gst-i18n-plugin.h>
233 #include <gst/pbutils/pbutils.h>
234 #include <gst/interfaces/streamvolume.h>
235 #include <gst/interfaces/xoverlay.h>
236 #include <gst/interfaces/navigation.h>
237 #include <gst/interfaces/colorbalance.h>
238
239 #include "gstplay-enum.h"
240 #include "gstplay-marshal.h"
241 #include "gstplayback.h"
242 #include "gstplaysink.h"
243 #include "gstsubtitleoverlay.h"
244
245 #include "gst/glib-compat-private.h"
246
247 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
248 #define GST_CAT_DEFAULT gst_play_bin_debug
249
250 #define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
251 #define GST_PLAY_BIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
252 #define GST_PLAY_BIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
253 #define GST_IS_PLAY_BIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
254 #define GST_IS_PLAY_BIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
255
256 #define VOLUME_MAX_DOUBLE 10.0
257
258 typedef struct _GstPlayBin GstPlayBin;
259 typedef struct _GstPlayBinClass GstPlayBinClass;
260 typedef struct _GstSourceGroup GstSourceGroup;
261 typedef struct _GstSourceSelect GstSourceSelect;
262
263 typedef GstCaps *(*SourceSelectGetMediaCapsFunc) (void);
264
265 /* has the info for a selector and provides the link to the sink */
266 struct _GstSourceSelect
267 {
268   const gchar *media_list[8];   /* the media types for the selector */
269   SourceSelectGetMediaCapsFunc get_media_caps;  /* more complex caps for the selector */
270   GstPlaySinkType type;         /* the sink pad type of the selector */
271
272   GstElement *selector;         /* the selector */
273   GPtrArray *channels;
274   GstPad *srcpad;               /* the source pad of the selector */
275   GstPad *sinkpad;              /* the sinkpad of the sink when the selector
276                                  * is linked
277                                  */
278   GstEvent *sinkpad_delayed_event;
279   gulong sinkpad_data_probe;
280 };
281
282 #define GST_SOURCE_GROUP_GET_LOCK(group) (((GstSourceGroup*)(group))->lock)
283 #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
284 #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
285
286 enum
287 {
288   PLAYBIN_STREAM_AUDIO = 0,
289   PLAYBIN_STREAM_VIDEO,
290   PLAYBIN_STREAM_TEXT,
291   PLAYBIN_STREAM_LAST
292 };
293
294 /* a structure to hold the objects for decoding a uri and the subtitle uri
295  */
296 struct _GstSourceGroup
297 {
298   GstPlayBin *playbin;
299
300   GMutex *lock;
301
302   gboolean valid;               /* the group has valid info to start playback */
303   gboolean active;              /* the group is active */
304
305   /* properties */
306   gchar *uri;
307   gchar *suburi;
308   GValueArray *streaminfo;
309   GstElement *source;
310
311   GPtrArray *video_channels;    /* links to selector pads */
312   GPtrArray *audio_channels;    /* links to selector pads */
313   GPtrArray *text_channels;     /* links to selector pads */
314
315   GstElement *audio_sink;       /* autoplugged audio and video sinks */
316   GstElement *video_sink;
317
318   /* uridecodebins for uri and subtitle uri */
319   GstElement *uridecodebin;
320   GstElement *suburidecodebin;
321   gint pending;
322   gboolean sub_pending;
323
324   gulong pad_added_id;
325   gulong pad_removed_id;
326   gulong no_more_pads_id;
327   gulong notify_source_id;
328   gulong drained_id;
329   gulong autoplug_factories_id;
330   gulong autoplug_select_id;
331   gulong autoplug_continue_id;
332
333   gulong sub_pad_added_id;
334   gulong sub_pad_removed_id;
335   gulong sub_no_more_pads_id;
336   gulong sub_autoplug_continue_id;
337
338   GMutex *stream_changed_pending_lock;
339   GList *stream_changed_pending;
340
341   /* selectors for different streams */
342   GstSourceSelect selector[PLAYBIN_STREAM_LAST];
343 };
344
345 #define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
346 #define GST_PLAY_BIN_LOCK(bin) (g_static_rec_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin)))
347 #define GST_PLAY_BIN_UNLOCK(bin) (g_static_rec_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin)))
348
349 /* lock to protect dynamic callbacks, like no-more-pads */
350 #define GST_PLAY_BIN_DYN_LOCK(bin)    g_mutex_lock ((bin)->dyn_lock)
351 #define GST_PLAY_BIN_DYN_UNLOCK(bin)  g_mutex_unlock ((bin)->dyn_lock)
352
353 /* lock for shutdown */
354 #define GST_PLAY_BIN_SHUTDOWN_LOCK(bin,label)           \
355 G_STMT_START {                                          \
356   if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown)))   \
357     goto label;                                         \
358   GST_PLAY_BIN_DYN_LOCK (bin);                          \
359   if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
360     GST_PLAY_BIN_DYN_UNLOCK (bin);                      \
361     goto label;                                         \
362   }                                                     \
363 } G_STMT_END
364
365 /* unlock for shutdown */
366 #define GST_PLAY_BIN_SHUTDOWN_UNLOCK(bin)         \
367   GST_PLAY_BIN_DYN_UNLOCK (bin);                  \
368
369 /**
370  * GstPlayBin2:
371  *
372  * playbin element structure
373  */
374 struct _GstPlayBin
375 {
376   GstPipeline parent;
377
378   GStaticRecMutex lock;         /* to protect group switching */
379
380   /* the groups, we use a double buffer to switch between current and next */
381   GstSourceGroup groups[2];     /* array with group info */
382   GstSourceGroup *curr_group;   /* pointer to the currently playing group */
383   GstSourceGroup *next_group;   /* pointer to the next group */
384
385   /* properties */
386   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
387   gint current_video;           /* the currently selected stream */
388   gint current_audio;           /* the currently selected stream */
389   gint current_text;            /* the currently selected stream */
390
391   guint64 buffer_duration;      /* When buffering, the max buffer duration (ns) */
392   guint buffer_size;            /* When buffering, the max buffer size (bytes) */
393
394   /* our play sink */
395   GstPlaySink *playsink;
396
397   /* the last activated source */
398   GstElement *source;
399
400   /* lock protecting dynamic adding/removing */
401   GMutex *dyn_lock;
402   /* if we are shutting down or not */
403   gint shutdown;
404
405   GMutex *elements_lock;
406   guint32 elements_cookie;
407   GList *elements;              /* factories we can use for selecting elements */
408
409   gboolean have_selector;       /* set to FALSE when we fail to create an
410                                  * input-selector, so that we only post a
411                                  * warning once */
412
413   GstElement *audio_sink;       /* configured audio sink, or NULL      */
414   GstElement *video_sink;       /* configured video sink, or NULL      */
415   GstElement *text_sink;        /* configured text sink, or NULL       */
416
417   struct
418   {
419     gboolean valid;
420     GstFormat format;
421     gint64 duration;
422   } duration[5];                /* cached durations */
423
424   guint64 ring_buffer_max_size; /* 0 means disabled */
425 };
426
427 struct _GstPlayBinClass
428 {
429   GstPipelineClass parent_class;
430
431   /* notify app that the current uri finished decoding and it is possible to
432    * queue a new one for gapless playback */
433   void (*about_to_finish) (GstPlayBin * playbin);
434
435   /* notify app that number of audio/video/text streams changed */
436   void (*video_changed) (GstPlayBin * playbin);
437   void (*audio_changed) (GstPlayBin * playbin);
438   void (*text_changed) (GstPlayBin * playbin);
439
440   /* notify app that the tags of audio/video/text streams changed */
441   void (*video_tags_changed) (GstPlayBin * playbin, gint stream);
442   void (*audio_tags_changed) (GstPlayBin * playbin, gint stream);
443   void (*text_tags_changed) (GstPlayBin * playbin, gint stream);
444
445   /* get audio/video/text tags for a stream */
446   GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
447   GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
448   GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
449
450   /* get the last video frame and convert it to the given caps */
451   GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
452
453   /* get audio/video/text pad for a stream */
454   GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
455   GstPad *(*get_audio_pad) (GstPlayBin * playbin, gint stream);
456   GstPad *(*get_text_pad) (GstPlayBin * playbin, gint stream);
457 };
458
459 /* props */
460 #define DEFAULT_URI               NULL
461 #define DEFAULT_SUBURI            NULL
462 #define DEFAULT_SOURCE            NULL
463 #define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
464                                   GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
465 #define DEFAULT_N_VIDEO           0
466 #define DEFAULT_CURRENT_VIDEO     -1
467 #define DEFAULT_N_AUDIO           0
468 #define DEFAULT_CURRENT_AUDIO     -1
469 #define DEFAULT_N_TEXT            0
470 #define DEFAULT_CURRENT_TEXT      -1
471 #define DEFAULT_SUBTITLE_ENCODING NULL
472 #define DEFAULT_AUDIO_SINK        NULL
473 #define DEFAULT_VIDEO_SINK        NULL
474 #define DEFAULT_VIS_PLUGIN        NULL
475 #define DEFAULT_TEXT_SINK         NULL
476 #define DEFAULT_VOLUME            1.0
477 #define DEFAULT_MUTE              FALSE
478 #define DEFAULT_FRAME             NULL
479 #define DEFAULT_FONT_DESC         NULL
480 #define DEFAULT_CONNECTION_SPEED  0
481 #define DEFAULT_BUFFER_DURATION   -1
482 #define DEFAULT_BUFFER_SIZE       -1
483 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
484
485 enum
486 {
487   PROP_0,
488   PROP_URI,
489   PROP_SUBURI,
490   PROP_SOURCE,
491   PROP_FLAGS,
492   PROP_N_VIDEO,
493   PROP_CURRENT_VIDEO,
494   PROP_N_AUDIO,
495   PROP_CURRENT_AUDIO,
496   PROP_N_TEXT,
497   PROP_CURRENT_TEXT,
498   PROP_SUBTITLE_ENCODING,
499   PROP_AUDIO_SINK,
500   PROP_VIDEO_SINK,
501   PROP_VIS_PLUGIN,
502   PROP_TEXT_SINK,
503   PROP_VOLUME,
504   PROP_MUTE,
505   PROP_FRAME,
506   PROP_FONT_DESC,
507   PROP_CONNECTION_SPEED,
508   PROP_BUFFER_SIZE,
509   PROP_BUFFER_DURATION,
510   PROP_AV_OFFSET,
511   PROP_RING_BUFFER_MAX_SIZE,
512   PROP_LAST
513 };
514
515 /* signals */
516 enum
517 {
518   SIGNAL_ABOUT_TO_FINISH,
519   SIGNAL_CONVERT_FRAME,
520   SIGNAL_VIDEO_CHANGED,
521   SIGNAL_AUDIO_CHANGED,
522   SIGNAL_TEXT_CHANGED,
523   SIGNAL_VIDEO_TAGS_CHANGED,
524   SIGNAL_AUDIO_TAGS_CHANGED,
525   SIGNAL_TEXT_TAGS_CHANGED,
526   SIGNAL_GET_VIDEO_TAGS,
527   SIGNAL_GET_AUDIO_TAGS,
528   SIGNAL_GET_TEXT_TAGS,
529   SIGNAL_GET_VIDEO_PAD,
530   SIGNAL_GET_AUDIO_PAD,
531   SIGNAL_GET_TEXT_PAD,
532   SIGNAL_SOURCE_SETUP,
533   LAST_SIGNAL
534 };
535
536 static void gst_play_bin_class_init (GstPlayBinClass * klass);
537 static void gst_play_bin_init (GstPlayBin * playbin);
538 static void gst_play_bin_finalize (GObject * object);
539
540 static void gst_play_bin_set_property (GObject * object, guint prop_id,
541     const GValue * value, GParamSpec * spec);
542 static void gst_play_bin_get_property (GObject * object, guint prop_id,
543     GValue * value, GParamSpec * spec);
544
545 static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
546     GstStateChange transition);
547
548 static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
549 static gboolean gst_play_bin_query (GstElement * element, GstQuery * query);
550
551 static GstTagList *gst_play_bin_get_video_tags (GstPlayBin * playbin,
552     gint stream);
553 static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
554     gint stream);
555 static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
556     gint stream);
557
558 static GstBuffer *gst_play_bin_convert_frame (GstPlayBin * playbin,
559     GstCaps * caps);
560
561 static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
562 static GstPad *gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream);
563 static GstPad *gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream);
564
565 static gboolean setup_next_source (GstPlayBin * playbin, GstState target);
566
567 static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
568 static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
569     GstSourceGroup * group);
570
571 static void gst_play_bin_suburidecodebin_block (GstElement * suburidecodebin,
572     gboolean block);
573 static void gst_play_bin_suburidecodebin_seek_to_start (GstElement *
574     suburidecodebin);
575
576 static GstElementClass *parent_class;
577
578 static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
579
580 #define REMOVE_SIGNAL(obj,id)            \
581 if (id) {                                \
582   g_signal_handler_disconnect (obj, id); \
583   id = 0;                                \
584 }
585
586 static void gst_play_bin_implements_interface_init (gpointer g_iface,
587     gpointer g_iface_data);
588 static void gst_play_bin_xoverlay_init (gpointer g_iface,
589     gpointer g_iface_data);
590 static void gst_play_bin_navigation_init (gpointer g_iface,
591     gpointer g_iface_data);
592 static void gst_play_bin_colorbalance_init (gpointer g_iface,
593     gpointer g_iface_data);
594
595 static GType
596 gst_play_bin_get_type (void)
597 {
598   static GType gst_play_bin_type = 0;
599
600   if (!gst_play_bin_type) {
601     static const GTypeInfo gst_play_bin_info = {
602       sizeof (GstPlayBinClass),
603       NULL,
604       NULL,
605       (GClassInitFunc) gst_play_bin_class_init,
606       NULL,
607       NULL,
608       sizeof (GstPlayBin),
609       0,
610       (GInstanceInitFunc) gst_play_bin_init,
611       NULL
612     };
613     static const GInterfaceInfo impl_info = {
614       gst_play_bin_implements_interface_init,
615       NULL, NULL
616     };
617     static const GInterfaceInfo svol_info = {
618       NULL, NULL, NULL
619     };
620     static const GInterfaceInfo xov_info = {
621       gst_play_bin_xoverlay_init,
622       NULL, NULL
623     };
624     static const GInterfaceInfo nav_info = {
625       gst_play_bin_navigation_init,
626       NULL, NULL
627     };
628     static const GInterfaceInfo col_info = {
629       gst_play_bin_colorbalance_init,
630       NULL, NULL
631     };
632
633     gst_play_bin_type = g_type_register_static (GST_TYPE_PIPELINE,
634         "GstPlayBin2", &gst_play_bin_info, 0);
635
636     g_type_add_interface_static (gst_play_bin_type,
637         GST_TYPE_IMPLEMENTS_INTERFACE, &impl_info);
638     g_type_add_interface_static (gst_play_bin_type, GST_TYPE_STREAM_VOLUME,
639         &svol_info);
640     g_type_add_interface_static (gst_play_bin_type, GST_TYPE_X_OVERLAY,
641         &xov_info);
642     g_type_add_interface_static (gst_play_bin_type, GST_TYPE_NAVIGATION,
643         &nav_info);
644     g_type_add_interface_static (gst_play_bin_type, GST_TYPE_COLOR_BALANCE,
645         &col_info);
646   }
647
648   return gst_play_bin_type;
649 }
650
651 static void
652 gst_play_bin_class_init (GstPlayBinClass * klass)
653 {
654   GObjectClass *gobject_klass;
655   GstElementClass *gstelement_klass;
656   GstBinClass *gstbin_klass;
657
658   gobject_klass = (GObjectClass *) klass;
659   gstelement_klass = (GstElementClass *) klass;
660   gstbin_klass = (GstBinClass *) klass;
661
662   parent_class = g_type_class_peek_parent (klass);
663
664   gobject_klass->set_property = gst_play_bin_set_property;
665   gobject_klass->get_property = gst_play_bin_get_property;
666
667   gobject_klass->finalize = gst_play_bin_finalize;
668
669   /**
670    * GstPlayBin2:uri
671    *
672    * Set the next URI that playbin will play. This property can be set from the
673    * about-to-finish signal to queue the next media file.
674    */
675   g_object_class_install_property (gobject_klass, PROP_URI,
676       g_param_spec_string ("uri", "URI", "URI of the media to play",
677           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678
679   /**
680    * GstPlayBin2:suburi
681    *
682    * Set the next subtitle URI that playbin will play. This property can be
683    * set from the about-to-finish signal to queue the next subtitle media file.
684    */
685   g_object_class_install_property (gobject_klass, PROP_SUBURI,
686       g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
687           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
688
689   g_object_class_install_property (gobject_klass, PROP_SOURCE,
690       g_param_spec_object ("source", "Source", "Source element",
691           GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
692
693   /**
694    * GstPlayBin2:flags
695    *
696    * Control the behaviour of playbin.
697    */
698   g_object_class_install_property (gobject_klass, PROP_FLAGS,
699       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
700           GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
701           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
702
703   /**
704    * GstPlayBin2:n-video
705    *
706    * Get the total number of available video streams.
707    */
708   g_object_class_install_property (gobject_klass, PROP_N_VIDEO,
709       g_param_spec_int ("n-video", "Number Video",
710           "Total number of video streams", 0, G_MAXINT, 0,
711           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
712   /**
713    * GstPlayBin2:current-video
714    *
715    * Get or set the currently playing video stream. By default the first video
716    * stream with data is played.
717    */
718   g_object_class_install_property (gobject_klass, PROP_CURRENT_VIDEO,
719       g_param_spec_int ("current-video", "Current Video",
720           "Currently playing video stream (-1 = auto)",
721           -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
722   /**
723    * GstPlayBin2:n-audio
724    *
725    * Get the total number of available audio streams.
726    */
727   g_object_class_install_property (gobject_klass, PROP_N_AUDIO,
728       g_param_spec_int ("n-audio", "Number Audio",
729           "Total number of audio streams", 0, G_MAXINT, 0,
730           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
731   /**
732    * GstPlayBin2:current-audio
733    *
734    * Get or set the currently playing audio stream. By default the first audio
735    * stream with data is played.
736    */
737   g_object_class_install_property (gobject_klass, PROP_CURRENT_AUDIO,
738       g_param_spec_int ("current-audio", "Current audio",
739           "Currently playing audio stream (-1 = auto)",
740           -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
741   /**
742    * GstPlayBin2:n-text
743    *
744    * Get the total number of available subtitle streams.
745    */
746   g_object_class_install_property (gobject_klass, PROP_N_TEXT,
747       g_param_spec_int ("n-text", "Number Text",
748           "Total number of text streams", 0, G_MAXINT, 0,
749           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
750   /**
751    * GstPlayBin2:current-text:
752    *
753    * Get or set the currently playing subtitle stream. By default the first
754    * subtitle stream with data is played.
755    */
756   g_object_class_install_property (gobject_klass, PROP_CURRENT_TEXT,
757       g_param_spec_int ("current-text", "Current Text",
758           "Currently playing text stream (-1 = auto)",
759           -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
760
761   g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
762       g_param_spec_string ("subtitle-encoding", "subtitle encoding",
763           "Encoding to assume if input subtitles are not in UTF-8 encoding. "
764           "If not set, the GST_SUBTITLE_ENCODING environment variable will "
765           "be checked for an encoding to use. If that is not set either, "
766           "ISO-8859-15 will be assumed.", NULL,
767           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
768
769   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
770       g_param_spec_object ("video-sink", "Video Sink",
771           "the video output element to use (NULL = default sink)",
772           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
773   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
774       g_param_spec_object ("audio-sink", "Audio Sink",
775           "the audio output element to use (NULL = default sink)",
776           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
777   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
778       g_param_spec_object ("vis-plugin", "Vis plugin",
779           "the visualization element to use (NULL = default)",
780           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
781   g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
782       g_param_spec_object ("text-sink", "Text plugin",
783           "the text output element to use (NULL = default textoverlay)",
784           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
785
786   /**
787    * GstPlayBin2:volume:
788    *
789    * Get or set the current audio stream volume. 1.0 means 100%,
790    * 0.0 means mute. This uses a linear volume scale.
791    *
792    */
793   g_object_class_install_property (gobject_klass, PROP_VOLUME,
794       g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
795           0.0, VOLUME_MAX_DOUBLE, 1.0,
796           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
797   g_object_class_install_property (gobject_klass, PROP_MUTE,
798       g_param_spec_boolean ("mute", "Mute",
799           "Mute the audio channel without changing the volume", FALSE,
800           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
801
802   /**
803    * GstPlayBin2:frame:
804    * @playbin: a #GstPlayBin2
805    *
806    * Get the currently rendered or prerolled frame in the video sink.
807    * The #GstCaps on the buffer will describe the format of the buffer.
808    */
809   g_object_class_install_property (gobject_klass, PROP_FRAME,
810       gst_param_spec_mini_object ("frame", "Frame",
811           "The last frame (NULL = no video available)",
812           GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
813   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
814       g_param_spec_string ("subtitle-font-desc",
815           "Subtitle font description",
816           "Pango font description of font "
817           "to be used for subtitle rendering", NULL,
818           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
819
820   g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
821       g_param_spec_uint ("connection-speed", "Connection Speed",
822           "Network connection speed in kbps (0 = unknown)",
823           0, G_MAXUINT / 1000, DEFAULT_CONNECTION_SPEED,
824           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
825
826   g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
827       g_param_spec_int ("buffer-size", "Buffer size (bytes)",
828           "Buffer size when buffering network streams",
829           -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
830           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
831   g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
832       g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
833           "Buffer duration when buffering network streams",
834           -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
835           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
836   /**
837    * GstPlayBin2:av-offset:
838    *
839    * Control the synchronisation offset between the audio and video streams.
840    * Positive values make the audio ahead of the video and negative values make
841    * the audio go behind the video.
842    *
843    * Since: 0.10.30
844    */
845   g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
846       g_param_spec_int64 ("av-offset", "AV Offset",
847           "The synchronisation offset between audio and video in nanoseconds",
848           G_MININT64, G_MAXINT64, 0,
849           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
850
851   /**
852    * GstQueue2:ring-buffer-max-size
853    *
854    * The maximum size of the ring buffer in bytes. If set to 0, the ring
855    * buffer is disabled. Default 0.
856    *
857    * Since: 0.10.31
858    */
859   g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
860       g_param_spec_uint64 ("ring-buffer-max-size",
861           "Max. ring buffer size (bytes)",
862           "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
863           0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
864           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
865
866   /**
867    * GstPlayBin2::about-to-finish
868    * @playbin: a #GstPlayBin2
869    *
870    * This signal is emitted when the current uri is about to finish. You can
871    * set the uri and suburi to make sure that playback continues.
872    *
873    * This signal is emitted from the context of a GStreamer streaming thread.
874    */
875   gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
876       g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
877       G_SIGNAL_RUN_LAST,
878       G_STRUCT_OFFSET (GstPlayBinClass, about_to_finish), NULL, NULL,
879       gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
880
881   /**
882    * GstPlayBin2::video-changed
883    * @playbin: a #GstPlayBin2
884    *
885    * This signal is emitted whenever the number or order of the video
886    * streams has changed. The application will most likely want to select
887    * a new video stream.
888    *
889    * This signal is usually emitted from the context of a GStreamer streaming
890    * thread. You can use gst_message_new_application() and
891    * gst_element_post_message() to notify your application's main thread.
892    */
893   /* FIXME 0.11: turn video-changed signal into message? */
894   gst_play_bin_signals[SIGNAL_VIDEO_CHANGED] =
895       g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass),
896       G_SIGNAL_RUN_LAST,
897       G_STRUCT_OFFSET (GstPlayBinClass, video_changed), NULL, NULL,
898       gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
899   /**
900    * GstPlayBin2::audio-changed
901    * @playbin: a #GstPlayBin2
902    *
903    * This signal is emitted whenever the number or order of the audio
904    * streams has changed. The application will most likely want to select
905    * a new audio stream.
906    *
907    * This signal may be emitted from the context of a GStreamer streaming thread.
908    * You can use gst_message_new_application() and gst_element_post_message()
909    * to notify your application's main thread.
910    */
911   /* FIXME 0.11: turn audio-changed signal into message? */
912   gst_play_bin_signals[SIGNAL_AUDIO_CHANGED] =
913       g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass),
914       G_SIGNAL_RUN_LAST,
915       G_STRUCT_OFFSET (GstPlayBinClass, audio_changed), NULL, NULL,
916       gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
917   /**
918    * GstPlayBin2::text-changed
919    * @playbin: a #GstPlayBin2
920    *
921    * This signal is emitted whenever the number or order of the text
922    * streams has changed. The application will most likely want to select
923    * a new text stream.
924    *
925    * This signal may be emitted from the context of a GStreamer streaming thread.
926    * You can use gst_message_new_application() and gst_element_post_message()
927    * to notify your application's main thread.
928    */
929   /* FIXME 0.11: turn text-changed signal into message? */
930   gst_play_bin_signals[SIGNAL_TEXT_CHANGED] =
931       g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
932       G_SIGNAL_RUN_LAST,
933       G_STRUCT_OFFSET (GstPlayBinClass, text_changed), NULL, NULL,
934       gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
935
936   /**
937    * GstPlayBin2::video-tags-changed
938    * @playbin: a #GstPlayBin2
939    * @stream: stream index with changed tags
940    *
941    * This signal is emitted whenever the tags of a video stream have changed.
942    * The application will most likely want to get the new tags.
943    *
944    * This signal may be emitted from the context of a GStreamer streaming thread.
945    * You can use gst_message_new_application() and gst_element_post_message()
946    * to notify your application's main thread.
947    *
948    * Since: 0.10.24
949    */
950   gst_play_bin_signals[SIGNAL_VIDEO_TAGS_CHANGED] =
951       g_signal_new ("video-tags-changed", G_TYPE_FROM_CLASS (klass),
952       G_SIGNAL_RUN_LAST,
953       G_STRUCT_OFFSET (GstPlayBinClass, video_tags_changed), NULL, NULL,
954       gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
955
956   /**
957    * GstPlayBin2::audio-tags-changed
958    * @playbin: a #GstPlayBin2
959    * @stream: stream index with changed tags
960    *
961    * This signal is emitted whenever the tags of an audio stream have changed.
962    * The application will most likely want to get the new tags.
963    *
964    * This signal may be emitted from the context of a GStreamer streaming thread.
965    * You can use gst_message_new_application() and gst_element_post_message()
966    * to notify your application's main thread.
967    *
968    * Since: 0.10.24
969    */
970   gst_play_bin_signals[SIGNAL_AUDIO_TAGS_CHANGED] =
971       g_signal_new ("audio-tags-changed", G_TYPE_FROM_CLASS (klass),
972       G_SIGNAL_RUN_LAST,
973       G_STRUCT_OFFSET (GstPlayBinClass, audio_tags_changed), NULL, NULL,
974       gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
975
976   /**
977    * GstPlayBin2::text-tags-changed
978    * @playbin: a #GstPlayBin2
979    * @stream: stream index with changed tags
980    *
981    * This signal is emitted whenever the tags of a text stream have changed.
982    * The application will most likely want to get the new tags.
983    *
984    * This signal may be emitted from the context of a GStreamer streaming thread.
985    * You can use gst_message_new_application() and gst_element_post_message()
986    * to notify your application's main thread.
987    *
988    * Since: 0.10.24
989    */
990   gst_play_bin_signals[SIGNAL_TEXT_TAGS_CHANGED] =
991       g_signal_new ("text-tags-changed", G_TYPE_FROM_CLASS (klass),
992       G_SIGNAL_RUN_LAST,
993       G_STRUCT_OFFSET (GstPlayBinClass, text_tags_changed), NULL, NULL,
994       gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
995
996   /**
997    * GstPlayBin2::source-setup:
998    * @playbin: a #GstPlayBin2
999    * @source: source element
1000    *
1001    * This signal is emitted after the source element has been created, so
1002    * it can be configured by setting additional properties (e.g. set a
1003    * proxy server for an http source, or set the device and read speed for
1004    * an audio cd source). This is functionally equivalent to connecting to
1005    * the notify::source signal, but more convenient.
1006    *
1007    * This signal is usually emitted from the context of a GStreamer streaming
1008    * thread.
1009    *
1010    * Since: 0.10.33
1011    */
1012   gst_play_bin_signals[SIGNAL_SOURCE_SETUP] =
1013       g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
1014       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1015       gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1016
1017   /**
1018    * GstPlayBin2::get-video-tags
1019    * @playbin: a #GstPlayBin2
1020    * @stream: a video stream number
1021    *
1022    * Action signal to retrieve the tags of a specific video stream number.
1023    * This information can be used to select a stream.
1024    *
1025    * Returns: a GstTagList with tags or NULL when the stream number does not
1026    * exist.
1027    */
1028   gst_play_bin_signals[SIGNAL_GET_VIDEO_TAGS] =
1029       g_signal_new ("get-video-tags", G_TYPE_FROM_CLASS (klass),
1030       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1031       G_STRUCT_OFFSET (GstPlayBinClass, get_video_tags), NULL, NULL,
1032       gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1033   /**
1034    * GstPlayBin2::get-audio-tags
1035    * @playbin: a #GstPlayBin2
1036    * @stream: an audio stream number
1037    *
1038    * Action signal to retrieve the tags of a specific audio stream number.
1039    * This information can be used to select a stream.
1040    *
1041    * Returns: a GstTagList with tags or NULL when the stream number does not
1042    * exist.
1043    */
1044   gst_play_bin_signals[SIGNAL_GET_AUDIO_TAGS] =
1045       g_signal_new ("get-audio-tags", G_TYPE_FROM_CLASS (klass),
1046       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1047       G_STRUCT_OFFSET (GstPlayBinClass, get_audio_tags), NULL, NULL,
1048       gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1049   /**
1050    * GstPlayBin2::get-text-tags
1051    * @playbin: a #GstPlayBin2
1052    * @stream: a text stream number
1053    *
1054    * Action signal to retrieve the tags of a specific text stream number.
1055    * This information can be used to select a stream.
1056    *
1057    * Returns: a GstTagList with tags or NULL when the stream number does not
1058    * exist.
1059    */
1060   gst_play_bin_signals[SIGNAL_GET_TEXT_TAGS] =
1061       g_signal_new ("get-text-tags", G_TYPE_FROM_CLASS (klass),
1062       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1063       G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
1064       gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1065   /**
1066    * GstPlayBin2::convert-frame
1067    * @playbin: a #GstPlayBin2
1068    * @caps: the target format of the frame
1069    *
1070    * Action signal to retrieve the currently playing video frame in the format
1071    * specified by @caps.
1072    * If @caps is %NULL, no conversion will be performed and this function is
1073    * equivalent to the #GstPlayBin::frame property.
1074    *
1075    * Returns: a #GstBuffer of the current video frame converted to #caps.
1076    * The caps on the buffer will describe the final layout of the buffer data.
1077    * %NULL is returned when no current buffer can be retrieved or when the
1078    * conversion failed.
1079    */
1080   gst_play_bin_signals[SIGNAL_CONVERT_FRAME] =
1081       g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
1082       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1083       G_STRUCT_OFFSET (GstPlayBinClass, convert_frame), NULL, NULL,
1084       gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
1085
1086   /**
1087    * GstPlayBin2::get-video-pad
1088    * @playbin: a #GstPlayBin2
1089    * @stream: a video stream number
1090    *
1091    * Action signal to retrieve the stream-selector sinkpad for a specific
1092    * video stream.
1093    * This pad can be used for notifications of caps changes, stream-specific
1094    * queries, etc.
1095    *
1096    * Returns: a #GstPad, or NULL when the stream number does not exist.
1097    */
1098   gst_play_bin_signals[SIGNAL_GET_VIDEO_PAD] =
1099       g_signal_new ("get-video-pad", G_TYPE_FROM_CLASS (klass),
1100       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1101       G_STRUCT_OFFSET (GstPlayBinClass, get_video_pad), NULL, NULL,
1102       gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);
1103   /**
1104    * GstPlayBin2::get-audio-pad
1105    * @playbin: a #GstPlayBin2
1106    * @stream: an audio stream number
1107    *
1108    * Action signal to retrieve the stream-selector sinkpad for a specific
1109    * audio stream.
1110    * This pad can be used for notifications of caps changes, stream-specific
1111    * queries, etc.
1112    *
1113    * Returns: a #GstPad, or NULL when the stream number does not exist.
1114    */
1115   gst_play_bin_signals[SIGNAL_GET_AUDIO_PAD] =
1116       g_signal_new ("get-audio-pad", G_TYPE_FROM_CLASS (klass),
1117       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1118       G_STRUCT_OFFSET (GstPlayBinClass, get_audio_pad), NULL, NULL,
1119       gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);
1120   /**
1121    * GstPlayBin2::get-text-pad
1122    * @playbin: a #GstPlayBin2
1123    * @stream: a text stream number
1124    *
1125    * Action signal to retrieve the stream-selector sinkpad for a specific
1126    * text stream.
1127    * This pad can be used for notifications of caps changes, stream-specific
1128    * queries, etc.
1129    *
1130    * Returns: a #GstPad, or NULL when the stream number does not exist.
1131    */
1132   gst_play_bin_signals[SIGNAL_GET_TEXT_PAD] =
1133       g_signal_new ("get-text-pad", G_TYPE_FROM_CLASS (klass),
1134       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1135       G_STRUCT_OFFSET (GstPlayBinClass, get_text_pad), NULL, NULL,
1136       gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);
1137
1138   klass->get_video_tags = gst_play_bin_get_video_tags;
1139   klass->get_audio_tags = gst_play_bin_get_audio_tags;
1140   klass->get_text_tags = gst_play_bin_get_text_tags;
1141
1142   klass->convert_frame = gst_play_bin_convert_frame;
1143
1144   klass->get_video_pad = gst_play_bin_get_video_pad;
1145   klass->get_audio_pad = gst_play_bin_get_audio_pad;
1146   klass->get_text_pad = gst_play_bin_get_text_pad;
1147
1148   gst_element_class_set_details_simple (gstelement_klass,
1149       "Player Bin 2", "Generic/Bin/Player",
1150       "Autoplug and play media from an uri",
1151       "Wim Taymans <wim.taymans@gmail.com>");
1152
1153   gstelement_klass->change_state =
1154       GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
1155   gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
1156
1157   gstbin_klass->handle_message =
1158       GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
1159 }
1160
1161 static void
1162 init_group (GstPlayBin * playbin, GstSourceGroup * group)
1163 {
1164   int n;
1165
1166   /* store the array for the different channels */
1167   group->video_channels = g_ptr_array_new ();
1168   group->audio_channels = g_ptr_array_new ();
1169   group->text_channels = g_ptr_array_new ();
1170   group->lock = g_mutex_new ();
1171   /* init selectors. The selector is found by finding the first prefix that
1172    * matches the media. */
1173   group->playbin = playbin;
1174   /* If you add any items to these lists, check that media_list[] is defined
1175    * above to be large enough to hold MAX(items)+1, so as to accommodate a
1176    * NULL terminator (set when the memory is zeroed on allocation) */
1177   group->selector[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
1178   group->selector[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
1179   group->selector[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
1180   group->selector[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
1181   group->selector[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
1182   group->selector[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
1183   group->selector[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
1184   group->selector[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
1185   group->selector[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
1186   group->selector[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
1187   group->selector[PLAYBIN_STREAM_TEXT].media_list[4] = "video/x-dvd-subpicture";
1188   group->selector[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
1189   group->selector[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
1190   group->selector[PLAYBIN_STREAM_TEXT].get_media_caps =
1191       gst_subtitle_overlay_create_factory_caps;
1192   group->selector[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
1193   group->selector[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
1194
1195   for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
1196     GstSourceSelect *select = &group->selector[n];
1197     select->sinkpad_delayed_event = NULL;
1198     select->sinkpad_data_probe = 0;
1199   }
1200 }
1201
1202 static void
1203 free_group (GstPlayBin * playbin, GstSourceGroup * group)
1204 {
1205   int n;
1206
1207   for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
1208     GstSourceSelect *select = &group->selector[n];
1209     if (select->sinkpad && select->sinkpad_data_probe)
1210       gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe);
1211     if (select->sinkpad_delayed_event)
1212       gst_event_unref (select->sinkpad_delayed_event);
1213   }
1214
1215   g_free (group->uri);
1216   g_free (group->suburi);
1217   g_ptr_array_free (group->video_channels, TRUE);
1218   g_ptr_array_free (group->audio_channels, TRUE);
1219   g_ptr_array_free (group->text_channels, TRUE);
1220
1221   g_mutex_free (group->lock);
1222   if (group->audio_sink) {
1223     if (group->audio_sink != playbin->audio_sink)
1224       gst_element_set_state (group->audio_sink, GST_STATE_NULL);
1225     gst_object_unref (group->audio_sink);
1226   }
1227   group->audio_sink = NULL;
1228   if (group->video_sink) {
1229     if (group->video_sink != playbin->video_sink)
1230       gst_element_set_state (group->video_sink, GST_STATE_NULL);
1231     gst_object_unref (group->video_sink);
1232   }
1233   group->video_sink = NULL;
1234
1235   g_list_free (group->stream_changed_pending);
1236   group->stream_changed_pending = NULL;
1237
1238   if (group->stream_changed_pending_lock)
1239     g_mutex_free (group->stream_changed_pending_lock);
1240   group->stream_changed_pending_lock = NULL;
1241 }
1242
1243 static void
1244 notify_volume_cb (GObject * selector, GParamSpec * pspec, GstPlayBin * playbin)
1245 {
1246   g_object_notify (G_OBJECT (playbin), "volume");
1247 }
1248
1249 static void
1250 notify_mute_cb (GObject * selector, GParamSpec * pspec, GstPlayBin * playbin)
1251 {
1252   g_object_notify (G_OBJECT (playbin), "mute");
1253 }
1254
1255 static void
1256 colorbalance_value_changed_cb (GstColorBalance * balance,
1257     GstColorBalanceChannel * channel, gint value, GstPlayBin * playbin)
1258 {
1259   gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
1260 }
1261
1262 /* Must be called with elements lock! */
1263 static void
1264 gst_play_bin_update_elements_list (GstPlayBin * playbin)
1265 {
1266   GList *res, *tmp;
1267
1268   if (!playbin->elements ||
1269       playbin->elements_cookie !=
1270       gst_default_registry_get_feature_list_cookie ()) {
1271     if (playbin->elements)
1272       gst_plugin_feature_list_free (playbin->elements);
1273     res =
1274         gst_element_factory_list_get_elements
1275         (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1276     tmp =
1277         gst_element_factory_list_get_elements
1278         (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
1279     playbin->elements = g_list_concat (res, tmp);
1280     playbin->elements =
1281         g_list_sort (playbin->elements, gst_plugin_feature_rank_compare_func);
1282     playbin->elements_cookie = gst_default_registry_get_feature_list_cookie ();
1283   }
1284 }
1285
1286 static void
1287 gst_play_bin_init (GstPlayBin * playbin)
1288 {
1289   g_static_rec_mutex_init (&playbin->lock);
1290   playbin->dyn_lock = g_mutex_new ();
1291
1292   /* assume we can create a selector */
1293   playbin->have_selector = TRUE;
1294
1295   /* init groups */
1296   playbin->curr_group = &playbin->groups[0];
1297   playbin->next_group = &playbin->groups[1];
1298   init_group (playbin, &playbin->groups[0]);
1299   init_group (playbin, &playbin->groups[1]);
1300
1301   /* first filter out the interesting element factories */
1302   playbin->elements_lock = g_mutex_new ();
1303
1304   /* add sink */
1305   playbin->playsink =
1306       g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", NULL);
1307   gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
1308   gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1309   /* Connect to notify::volume and notify::mute signals for proxying */
1310   g_signal_connect (playbin->playsink, "notify::volume",
1311       G_CALLBACK (notify_volume_cb), playbin);
1312   g_signal_connect (playbin->playsink, "notify::mute",
1313       G_CALLBACK (notify_mute_cb), playbin);
1314   g_signal_connect (playbin->playsink, "value-changed",
1315       G_CALLBACK (colorbalance_value_changed_cb), playbin);
1316
1317   playbin->current_video = DEFAULT_CURRENT_VIDEO;
1318   playbin->current_audio = DEFAULT_CURRENT_AUDIO;
1319   playbin->current_text = DEFAULT_CURRENT_TEXT;
1320
1321   playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
1322   playbin->buffer_size = DEFAULT_BUFFER_SIZE;
1323   playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
1324 }
1325
1326 static void
1327 gst_play_bin_finalize (GObject * object)
1328 {
1329   GstPlayBin *playbin;
1330
1331   playbin = GST_PLAY_BIN (object);
1332
1333   free_group (playbin, &playbin->groups[0]);
1334   free_group (playbin, &playbin->groups[1]);
1335
1336   if (playbin->source)
1337     gst_object_unref (playbin->source);
1338   if (playbin->video_sink) {
1339     gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
1340     gst_object_unref (playbin->video_sink);
1341   }
1342   if (playbin->audio_sink) {
1343     gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
1344     gst_object_unref (playbin->audio_sink);
1345   }
1346   if (playbin->text_sink) {
1347     gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
1348     gst_object_unref (playbin->text_sink);
1349   }
1350
1351   if (playbin->elements)
1352     gst_plugin_feature_list_free (playbin->elements);
1353
1354   g_static_rec_mutex_free (&playbin->lock);
1355   g_mutex_free (playbin->dyn_lock);
1356   g_mutex_free (playbin->elements_lock);
1357
1358   G_OBJECT_CLASS (parent_class)->finalize (object);
1359 }
1360
1361 static gboolean
1362 gst_playbin_uri_is_valid (GstPlayBin * playbin, const gchar * uri)
1363 {
1364   const gchar *c;
1365
1366   GST_LOG_OBJECT (playbin, "checking uri '%s'", uri);
1367
1368   /* this just checks the protocol */
1369   if (!gst_uri_is_valid (uri))
1370     return FALSE;
1371
1372   for (c = uri; *c != '\0'; ++c) {
1373     if (!g_ascii_isprint (*c))
1374       goto invalid;
1375     if (*c == ' ')
1376       goto invalid;
1377   }
1378
1379   return TRUE;
1380
1381 invalid:
1382   {
1383     GST_WARNING_OBJECT (playbin, "uri '%s' not valid, character #%u",
1384         uri, (guint) ((guintptr) c - (guintptr) uri));
1385     return FALSE;
1386   }
1387 }
1388
1389 static void
1390 gst_play_bin_set_uri (GstPlayBin * playbin, const gchar * uri)
1391 {
1392   GstSourceGroup *group;
1393
1394   if (uri == NULL) {
1395     g_warning ("cannot set NULL uri");
1396     return;
1397   }
1398
1399   if (!gst_playbin_uri_is_valid (playbin, uri)) {
1400     if (g_str_has_prefix (uri, "file:")) {
1401       GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
1402           "sure to escape spaces and non-ASCII characters properly and specify "
1403           "an absolute path. Use gst_filename_to_uri() to convert filenames "
1404           "to URIs", uri);
1405     } else {
1406       /* GST_ERROR_OBJECT (playbin, "malformed URI '%s'", uri); */
1407     }
1408   }
1409
1410   GST_PLAY_BIN_LOCK (playbin);
1411   group = playbin->next_group;
1412
1413   GST_SOURCE_GROUP_LOCK (group);
1414   /* store the uri in the next group we will play */
1415   g_free (group->uri);
1416   group->uri = g_strdup (uri);
1417   group->valid = TRUE;
1418   GST_SOURCE_GROUP_UNLOCK (group);
1419
1420   GST_DEBUG ("set new uri to %s", uri);
1421   GST_PLAY_BIN_UNLOCK (playbin);
1422 }
1423
1424 static void
1425 gst_play_bin_set_suburi (GstPlayBin * playbin, const gchar * suburi)
1426 {
1427   GstSourceGroup *group;
1428
1429   GST_PLAY_BIN_LOCK (playbin);
1430   group = playbin->next_group;
1431
1432   GST_SOURCE_GROUP_LOCK (group);
1433   g_free (group->suburi);
1434   group->suburi = g_strdup (suburi);
1435   GST_SOURCE_GROUP_UNLOCK (group);
1436
1437   GST_DEBUG ("setting new .sub uri to %s", suburi);
1438
1439   GST_PLAY_BIN_UNLOCK (playbin);
1440 }
1441
1442 static void
1443 gst_play_bin_set_flags (GstPlayBin * playbin, GstPlayFlags flags)
1444 {
1445   gst_play_sink_set_flags (playbin->playsink, flags);
1446   gst_play_sink_reconfigure (playbin->playsink);
1447 }
1448
1449 static GstPlayFlags
1450 gst_play_bin_get_flags (GstPlayBin * playbin)
1451 {
1452   GstPlayFlags flags;
1453
1454   flags = gst_play_sink_get_flags (playbin->playsink);
1455
1456   return flags;
1457 }
1458
1459 /* get the currently playing group or if nothing is playing, the next
1460  * group. Must be called with the PLAY_BIN_LOCK. */
1461 static GstSourceGroup *
1462 get_group (GstPlayBin * playbin)
1463 {
1464   GstSourceGroup *result;
1465
1466   if (!(result = playbin->curr_group))
1467     result = playbin->next_group;
1468
1469   return result;
1470 }
1471
1472 static GstPad *
1473 gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream)
1474 {
1475   GstPad *sinkpad = NULL;
1476   GstSourceGroup *group;
1477
1478   GST_PLAY_BIN_LOCK (playbin);
1479   group = get_group (playbin);
1480   if (stream < group->video_channels->len) {
1481     sinkpad = g_ptr_array_index (group->video_channels, stream);
1482     gst_object_ref (sinkpad);
1483   }
1484   GST_PLAY_BIN_UNLOCK (playbin);
1485
1486   return sinkpad;
1487 }
1488
1489 static GstPad *
1490 gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream)
1491 {
1492   GstPad *sinkpad = NULL;
1493   GstSourceGroup *group;
1494
1495   GST_PLAY_BIN_LOCK (playbin);
1496   group = get_group (playbin);
1497   if (stream < group->audio_channels->len) {
1498     sinkpad = g_ptr_array_index (group->audio_channels, stream);
1499     gst_object_ref (sinkpad);
1500   }
1501   GST_PLAY_BIN_UNLOCK (playbin);
1502
1503   return sinkpad;
1504 }
1505
1506 static GstPad *
1507 gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream)
1508 {
1509   GstPad *sinkpad = NULL;
1510   GstSourceGroup *group;
1511
1512   GST_PLAY_BIN_LOCK (playbin);
1513   group = get_group (playbin);
1514   if (stream < group->text_channels->len) {
1515     sinkpad = g_ptr_array_index (group->text_channels, stream);
1516     gst_object_ref (sinkpad);
1517   }
1518   GST_PLAY_BIN_UNLOCK (playbin);
1519
1520   return sinkpad;
1521 }
1522
1523
1524 static GstTagList *
1525 get_tags (GstPlayBin * playbin, GPtrArray * channels, gint stream)
1526 {
1527   GstTagList *result;
1528   GstPad *sinkpad;
1529
1530   if (!channels || stream >= channels->len)
1531     return NULL;
1532
1533   sinkpad = g_ptr_array_index (channels, stream);
1534   g_object_get (sinkpad, "tags", &result, NULL);
1535
1536   return result;
1537 }
1538
1539 static GstTagList *
1540 gst_play_bin_get_video_tags (GstPlayBin * playbin, gint stream)
1541 {
1542   GstTagList *result;
1543   GstSourceGroup *group;
1544
1545   GST_PLAY_BIN_LOCK (playbin);
1546   group = get_group (playbin);
1547   result = get_tags (playbin, group->video_channels, stream);
1548   GST_PLAY_BIN_UNLOCK (playbin);
1549
1550   return result;
1551 }
1552
1553 static GstTagList *
1554 gst_play_bin_get_audio_tags (GstPlayBin * playbin, gint stream)
1555 {
1556   GstTagList *result;
1557   GstSourceGroup *group;
1558
1559   GST_PLAY_BIN_LOCK (playbin);
1560   group = get_group (playbin);
1561   result = get_tags (playbin, group->audio_channels, stream);
1562   GST_PLAY_BIN_UNLOCK (playbin);
1563
1564   return result;
1565 }
1566
1567 static GstTagList *
1568 gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
1569 {
1570   GstTagList *result;
1571   GstSourceGroup *group;
1572
1573   GST_PLAY_BIN_LOCK (playbin);
1574   group = get_group (playbin);
1575   result = get_tags (playbin, group->text_channels, stream);
1576   GST_PLAY_BIN_UNLOCK (playbin);
1577
1578   return result;
1579 }
1580
1581 static GstBuffer *
1582 gst_play_bin_convert_frame (GstPlayBin * playbin, GstCaps * caps)
1583 {
1584   return gst_play_sink_convert_frame (playbin->playsink, caps);
1585 }
1586
1587 /* Returns current stream number, or -1 if none has been selected yet */
1588 static int
1589 get_current_stream_number (GstPlayBin * playbin, GPtrArray * channels)
1590 {
1591   /* Internal API cleanup would make this easier... */
1592   int i;
1593   GstPad *pad, *current;
1594   GstObject *selector = NULL;
1595   int ret = -1;
1596
1597   for (i = 0; i < channels->len; i++) {
1598     pad = g_ptr_array_index (channels, i);
1599     if ((selector = gst_pad_get_parent (pad))) {
1600       g_object_get (selector, "active-pad", &current, NULL);
1601       gst_object_unref (selector);
1602
1603       if (pad == current) {
1604         gst_object_unref (current);
1605         ret = i;
1606         break;
1607       }
1608
1609       if (current)
1610         gst_object_unref (current);
1611     }
1612   }
1613
1614   return ret;
1615 }
1616
1617 static gboolean
1618 gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
1619 {
1620   GstSourceGroup *group;
1621   GPtrArray *channels;
1622   GstPad *sinkpad;
1623
1624   GST_PLAY_BIN_LOCK (playbin);
1625
1626   GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
1627       playbin->current_video, stream);
1628
1629   group = get_group (playbin);
1630   if (!(channels = group->video_channels))
1631     goto no_channels;
1632
1633   if (stream == -1 || channels->len <= stream) {
1634     sinkpad = NULL;
1635   } else {
1636     /* take channel from selected stream */
1637     sinkpad = g_ptr_array_index (channels, stream);
1638   }
1639
1640   if (sinkpad)
1641     gst_object_ref (sinkpad);
1642   GST_PLAY_BIN_UNLOCK (playbin);
1643
1644   if (sinkpad) {
1645     GstObject *selector;
1646
1647     if ((selector = gst_pad_get_parent (sinkpad))) {
1648       /* activate the selected pad */
1649       g_object_set (selector, "active-pad", sinkpad, NULL);
1650       gst_object_unref (selector);
1651     }
1652     gst_object_unref (sinkpad);
1653   }
1654   return TRUE;
1655
1656 no_channels:
1657   {
1658     GST_PLAY_BIN_UNLOCK (playbin);
1659     GST_DEBUG_OBJECT (playbin, "can't switch video, we have no channels");
1660     return FALSE;
1661   }
1662 }
1663
1664 static gboolean
1665 gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
1666 {
1667   GstSourceGroup *group;
1668   GPtrArray *channels;
1669   GstPad *sinkpad;
1670
1671   GST_PLAY_BIN_LOCK (playbin);
1672
1673   GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
1674       playbin->current_audio, stream);
1675
1676   group = get_group (playbin);
1677   if (!(channels = group->audio_channels))
1678     goto no_channels;
1679
1680   if (stream == -1 || channels->len <= stream) {
1681     sinkpad = NULL;
1682   } else {
1683     /* take channel from selected stream */
1684     sinkpad = g_ptr_array_index (channels, stream);
1685   }
1686
1687   if (sinkpad)
1688     gst_object_ref (sinkpad);
1689   GST_PLAY_BIN_UNLOCK (playbin);
1690
1691   if (sinkpad) {
1692     GstObject *selector;
1693
1694     if ((selector = gst_pad_get_parent (sinkpad))) {
1695       /* activate the selected pad */
1696       g_object_set (selector, "active-pad", sinkpad, NULL);
1697       gst_object_unref (selector);
1698     }
1699     gst_object_unref (sinkpad);
1700   }
1701   return TRUE;
1702
1703 no_channels:
1704   {
1705     GST_PLAY_BIN_UNLOCK (playbin);
1706     GST_DEBUG_OBJECT (playbin, "can't switch audio, we have no channels");
1707     return FALSE;
1708   }
1709 }
1710
1711 static void
1712 _suburidecodebin_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
1713 {
1714   GST_DEBUG_OBJECT (pad, "Pad blocked: %d", blocked);
1715 }
1716
1717 static void
1718 gst_play_bin_suburidecodebin_seek_to_start (GstElement * suburidecodebin)
1719 {
1720   GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
1721   GstPad *sinkpad;
1722
1723   if (it && gst_iterator_next (it, (gpointer) & sinkpad) == GST_ITERATOR_OK
1724       && sinkpad) {
1725     GstEvent *event;
1726
1727     event =
1728         gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE,
1729         GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
1730     if (!gst_pad_send_event (sinkpad, event)) {
1731       event =
1732           gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
1733           GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
1734       if (!gst_pad_send_event (sinkpad, event))
1735         GST_DEBUG_OBJECT (suburidecodebin, "Seeking to the beginning failed!");
1736     }
1737
1738     gst_object_unref (sinkpad);
1739   }
1740
1741   if (it)
1742     gst_iterator_free (it);
1743 }
1744
1745 static void
1746 gst_play_bin_suburidecodebin_block (GstElement * suburidecodebin,
1747     gboolean block)
1748 {
1749   GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
1750   gboolean done = FALSE;
1751
1752   GST_DEBUG_OBJECT (suburidecodebin, "Blocking suburidecodebin: %d", block);
1753
1754   if (!it)
1755     return;
1756   while (!done) {
1757     GstPad *sinkpad;
1758
1759     switch (gst_iterator_next (it, (gpointer) & sinkpad)) {
1760       case GST_ITERATOR_OK:
1761         gst_pad_set_blocked_async (sinkpad, block, _suburidecodebin_blocked_cb,
1762             NULL);
1763         gst_object_unref (sinkpad);
1764         break;
1765       case GST_ITERATOR_DONE:
1766         done = TRUE;
1767         break;
1768       case GST_ITERATOR_RESYNC:
1769         gst_iterator_resync (it);
1770         break;
1771       case GST_ITERATOR_ERROR:
1772         done = TRUE;
1773         break;
1774     }
1775   }
1776   gst_iterator_free (it);
1777 }
1778
1779 static gboolean
1780 gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
1781 {
1782   GstSourceGroup *group;
1783   GPtrArray *channels;
1784   GstPad *sinkpad;
1785
1786   GST_PLAY_BIN_LOCK (playbin);
1787
1788   GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
1789       playbin->current_text, stream);
1790
1791   group = get_group (playbin);
1792   if (!(channels = group->text_channels))
1793     goto no_channels;
1794
1795   if (stream == -1 || channels->len <= stream) {
1796     sinkpad = NULL;
1797   } else {
1798     /* take channel from selected stream */
1799     sinkpad = g_ptr_array_index (channels, stream);
1800   }
1801
1802   if (sinkpad)
1803     gst_object_ref (sinkpad);
1804   GST_PLAY_BIN_UNLOCK (playbin);
1805
1806   if (sinkpad) {
1807     GstObject *selector;
1808
1809     if ((selector = gst_pad_get_parent (sinkpad))) {
1810       GstPad *old_sinkpad;
1811
1812       g_object_get (selector, "active-pad", &old_sinkpad, NULL);
1813
1814       if (old_sinkpad != sinkpad) {
1815         gboolean need_unblock, need_block, need_seek;
1816         GstPad *src, *peer = NULL, *oldpeer = NULL;
1817         GstElement *parent_element = NULL, *old_parent_element = NULL;
1818
1819         /* Now check if we need to seek the suburidecodebin to the beginning
1820          * or if we need to block all suburidecodebin sinkpads or if we need
1821          * to unblock all suburidecodebin sinkpads
1822          */
1823         if (sinkpad)
1824           peer = gst_pad_get_peer (sinkpad);
1825         if (old_sinkpad)
1826           oldpeer = gst_pad_get_peer (old_sinkpad);
1827
1828         if (peer)
1829           parent_element = gst_pad_get_parent_element (peer);
1830         if (oldpeer)
1831           old_parent_element = gst_pad_get_parent_element (oldpeer);
1832
1833         need_block = (old_parent_element == group->suburidecodebin
1834             && parent_element != old_parent_element);
1835         need_unblock = (parent_element == group->suburidecodebin
1836             && parent_element != old_parent_element);
1837         need_seek = (parent_element == group->suburidecodebin);
1838
1839         if (peer)
1840           gst_object_unref (peer);
1841         if (oldpeer)
1842           gst_object_unref (oldpeer);
1843         if (parent_element)
1844           gst_object_unref (parent_element);
1845         if (old_parent_element)
1846           gst_object_unref (old_parent_element);
1847
1848         /* Block all suburidecodebin sinkpads */
1849         if (need_block)
1850           gst_play_bin_suburidecodebin_block (group->suburidecodebin, TRUE);
1851
1852         /* activate the selected pad */
1853         g_object_set (selector, "active-pad", sinkpad, NULL);
1854
1855         src = gst_element_get_static_pad (GST_ELEMENT_CAST (selector), "src");
1856         peer = gst_pad_get_peer (src);
1857         if (peer) {
1858           GstStructure *s;
1859           GstEvent *event;
1860           /* Flush the subtitle renderer to remove any
1861            * currently displayed subtitles. This event will
1862            * never travel outside subtitleoverlay!
1863            */
1864           s = gst_structure_empty_new ("subtitleoverlay-flush-subtitle");
1865           event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
1866           gst_pad_send_event (peer, event);
1867           gst_object_unref (peer);
1868         }
1869         gst_object_unref (src);
1870
1871         /* Unblock pads if necessary */
1872         if (need_unblock)
1873           gst_play_bin_suburidecodebin_block (group->suburidecodebin, FALSE);
1874
1875         /* seek to the beginning */
1876         if (need_seek)
1877           gst_play_bin_suburidecodebin_seek_to_start (group->suburidecodebin);
1878       }
1879       gst_object_unref (selector);
1880
1881       if (old_sinkpad)
1882         gst_object_unref (old_sinkpad);
1883     }
1884     gst_object_unref (sinkpad);
1885   }
1886   return TRUE;
1887
1888 no_channels:
1889   {
1890     GST_PLAY_BIN_UNLOCK (playbin);
1891     return FALSE;
1892   }
1893 }
1894
1895 static void
1896 gst_play_bin_set_sink (GstPlayBin * playbin, GstElement ** elem,
1897     const gchar * dbg, GstElement * sink)
1898 {
1899   GST_INFO_OBJECT (playbin, "Setting %s sink to %" GST_PTR_FORMAT, dbg, sink);
1900
1901   GST_PLAY_BIN_LOCK (playbin);
1902   if (*elem != sink) {
1903     GstElement *old;
1904
1905     old = *elem;
1906     if (sink)
1907       gst_object_ref_sink (sink);
1908
1909     *elem = sink;
1910     if (old)
1911       gst_object_unref (old);
1912   }
1913   GST_LOG_OBJECT (playbin, "%s sink now %" GST_PTR_FORMAT, dbg, *elem);
1914   GST_PLAY_BIN_UNLOCK (playbin);
1915 }
1916
1917 static void
1918 gst_play_bin_set_encoding (GstPlayBin * playbin, const gchar * encoding)
1919 {
1920   GstElement *elem;
1921
1922   GST_PLAY_BIN_LOCK (playbin);
1923
1924   /* set subtitles on all current and next decodebins. */
1925   if ((elem = playbin->groups[0].uridecodebin))
1926     g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
1927   if ((elem = playbin->groups[0].suburidecodebin))
1928     g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
1929   if ((elem = playbin->groups[1].uridecodebin))
1930     g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
1931   if ((elem = playbin->groups[1].suburidecodebin))
1932     g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
1933
1934   gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
1935   GST_PLAY_BIN_UNLOCK (playbin);
1936 }
1937
1938 static void
1939 gst_play_bin_set_property (GObject * object, guint prop_id,
1940     const GValue * value, GParamSpec * pspec)
1941 {
1942   GstPlayBin *playbin = GST_PLAY_BIN (object);
1943
1944   switch (prop_id) {
1945     case PROP_URI:
1946       gst_play_bin_set_uri (playbin, g_value_get_string (value));
1947       break;
1948     case PROP_SUBURI:
1949       gst_play_bin_set_suburi (playbin, g_value_get_string (value));
1950       break;
1951     case PROP_FLAGS:
1952       gst_play_bin_set_flags (playbin, g_value_get_flags (value));
1953       break;
1954     case PROP_CURRENT_VIDEO:
1955       gst_play_bin_set_current_video_stream (playbin, g_value_get_int (value));
1956       break;
1957     case PROP_CURRENT_AUDIO:
1958       gst_play_bin_set_current_audio_stream (playbin, g_value_get_int (value));
1959       break;
1960     case PROP_CURRENT_TEXT:
1961       gst_play_bin_set_current_text_stream (playbin, g_value_get_int (value));
1962       break;
1963     case PROP_SUBTITLE_ENCODING:
1964       gst_play_bin_set_encoding (playbin, g_value_get_string (value));
1965       break;
1966     case PROP_VIDEO_SINK:
1967       gst_play_bin_set_sink (playbin, &playbin->video_sink, "video",
1968           g_value_get_object (value));
1969       break;
1970     case PROP_AUDIO_SINK:
1971       gst_play_bin_set_sink (playbin, &playbin->audio_sink, "audio",
1972           g_value_get_object (value));
1973       break;
1974     case PROP_VIS_PLUGIN:
1975       gst_play_sink_set_vis_plugin (playbin->playsink,
1976           g_value_get_object (value));
1977       break;
1978     case PROP_TEXT_SINK:
1979       gst_play_bin_set_sink (playbin, &playbin->text_sink, "text",
1980           g_value_get_object (value));
1981       break;
1982     case PROP_VOLUME:
1983       gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
1984       break;
1985     case PROP_MUTE:
1986       gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
1987       break;
1988     case PROP_FONT_DESC:
1989       gst_play_sink_set_font_desc (playbin->playsink,
1990           g_value_get_string (value));
1991       break;
1992     case PROP_CONNECTION_SPEED:
1993       GST_PLAY_BIN_LOCK (playbin);
1994       playbin->connection_speed = g_value_get_uint (value) * 1000;
1995       GST_PLAY_BIN_UNLOCK (playbin);
1996       break;
1997     case PROP_BUFFER_SIZE:
1998       playbin->buffer_size = g_value_get_int (value);
1999       break;
2000     case PROP_BUFFER_DURATION:
2001       playbin->buffer_duration = g_value_get_int64 (value);
2002       break;
2003     case PROP_AV_OFFSET:
2004       gst_play_sink_set_av_offset (playbin->playsink,
2005           g_value_get_int64 (value));
2006       break;
2007     case PROP_RING_BUFFER_MAX_SIZE:
2008       playbin->ring_buffer_max_size = g_value_get_uint64 (value);
2009       break;
2010     default:
2011       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2012       break;
2013   }
2014 }
2015
2016 static GstElement *
2017 gst_play_bin_get_current_sink (GstPlayBin * playbin, GstElement ** elem,
2018     const gchar * dbg, GstPlaySinkType type)
2019 {
2020   GstElement *sink = gst_play_sink_get_sink (playbin->playsink, type);
2021
2022   GST_LOG_OBJECT (playbin, "play_sink_get_sink() returned %s sink %"
2023       GST_PTR_FORMAT ", the originally set %s sink is %" GST_PTR_FORMAT,
2024       dbg, sink, dbg, *elem);
2025
2026   if (sink == NULL) {
2027     GST_PLAY_BIN_LOCK (playbin);
2028     if ((sink = *elem))
2029       gst_object_ref (sink);
2030     GST_PLAY_BIN_UNLOCK (playbin);
2031   }
2032
2033   return sink;
2034 }
2035
2036 static void
2037 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
2038     GParamSpec * pspec)
2039 {
2040   GstPlayBin *playbin = GST_PLAY_BIN (object);
2041
2042   switch (prop_id) {
2043     case PROP_URI:
2044     {
2045       GstSourceGroup *group;
2046
2047       GST_PLAY_BIN_LOCK (playbin);
2048       group = get_group (playbin);
2049       g_value_set_string (value, group->uri);
2050       GST_PLAY_BIN_UNLOCK (playbin);
2051       break;
2052     }
2053     case PROP_SUBURI:
2054     {
2055       GstSourceGroup *group;
2056
2057       GST_PLAY_BIN_LOCK (playbin);
2058       group = get_group (playbin);
2059       g_value_set_string (value, group->suburi);
2060       GST_PLAY_BIN_UNLOCK (playbin);
2061       break;
2062     }
2063     case PROP_SOURCE:
2064     {
2065       GST_OBJECT_LOCK (playbin);
2066       g_value_set_object (value, playbin->source);
2067       GST_OBJECT_UNLOCK (playbin);
2068       break;
2069     }
2070     case PROP_FLAGS:
2071       g_value_set_flags (value, gst_play_bin_get_flags (playbin));
2072       break;
2073     case PROP_N_VIDEO:
2074     {
2075       GstSourceGroup *group;
2076       gint n_video;
2077
2078       GST_PLAY_BIN_LOCK (playbin);
2079       group = get_group (playbin);
2080       n_video = (group->video_channels ? group->video_channels->len : 0);
2081       g_value_set_int (value, n_video);
2082       GST_PLAY_BIN_UNLOCK (playbin);
2083       break;
2084     }
2085     case PROP_CURRENT_VIDEO:
2086       GST_PLAY_BIN_LOCK (playbin);
2087       g_value_set_int (value, playbin->current_video);
2088       GST_PLAY_BIN_UNLOCK (playbin);
2089       break;
2090     case PROP_N_AUDIO:
2091     {
2092       GstSourceGroup *group;
2093       gint n_audio;
2094
2095       GST_PLAY_BIN_LOCK (playbin);
2096       group = get_group (playbin);
2097       n_audio = (group->audio_channels ? group->audio_channels->len : 0);
2098       g_value_set_int (value, n_audio);
2099       GST_PLAY_BIN_UNLOCK (playbin);
2100       break;
2101     }
2102     case PROP_CURRENT_AUDIO:
2103       GST_PLAY_BIN_LOCK (playbin);
2104       g_value_set_int (value, playbin->current_audio);
2105       GST_PLAY_BIN_UNLOCK (playbin);
2106       break;
2107     case PROP_N_TEXT:
2108     {
2109       GstSourceGroup *group;
2110       gint n_text;
2111
2112       GST_PLAY_BIN_LOCK (playbin);
2113       group = get_group (playbin);
2114       n_text = (group->text_channels ? group->text_channels->len : 0);
2115       g_value_set_int (value, n_text);
2116       GST_PLAY_BIN_UNLOCK (playbin);
2117       break;
2118     }
2119     case PROP_CURRENT_TEXT:
2120       GST_PLAY_BIN_LOCK (playbin);
2121       g_value_set_int (value, playbin->current_text);
2122       GST_PLAY_BIN_UNLOCK (playbin);
2123       break;
2124     case PROP_SUBTITLE_ENCODING:
2125       GST_PLAY_BIN_LOCK (playbin);
2126       g_value_take_string (value,
2127           gst_play_sink_get_subtitle_encoding (playbin->playsink));
2128       GST_PLAY_BIN_UNLOCK (playbin);
2129       break;
2130     case PROP_VIDEO_SINK:
2131       g_value_take_object (value,
2132           gst_play_bin_get_current_sink (playbin, &playbin->video_sink,
2133               "video", GST_PLAY_SINK_TYPE_VIDEO));
2134       break;
2135     case PROP_AUDIO_SINK:
2136       g_value_take_object (value,
2137           gst_play_bin_get_current_sink (playbin, &playbin->audio_sink,
2138               "audio", GST_PLAY_SINK_TYPE_AUDIO));
2139       break;
2140     case PROP_VIS_PLUGIN:
2141       g_value_take_object (value,
2142           gst_play_sink_get_vis_plugin (playbin->playsink));
2143       break;
2144     case PROP_TEXT_SINK:
2145       g_value_take_object (value,
2146           gst_play_bin_get_current_sink (playbin, &playbin->text_sink,
2147               "text", GST_PLAY_SINK_TYPE_TEXT));
2148       break;
2149     case PROP_VOLUME:
2150       g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
2151       break;
2152     case PROP_MUTE:
2153       g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
2154       break;
2155     case PROP_FRAME:
2156       gst_value_take_buffer (value,
2157           gst_play_sink_get_last_frame (playbin->playsink));
2158       break;
2159     case PROP_FONT_DESC:
2160       g_value_take_string (value,
2161           gst_play_sink_get_font_desc (playbin->playsink));
2162       break;
2163     case PROP_CONNECTION_SPEED:
2164       GST_PLAY_BIN_LOCK (playbin);
2165       g_value_set_uint (value, playbin->connection_speed / 1000);
2166       GST_PLAY_BIN_UNLOCK (playbin);
2167       break;
2168     case PROP_BUFFER_SIZE:
2169       GST_OBJECT_LOCK (playbin);
2170       g_value_set_int (value, playbin->buffer_size);
2171       GST_OBJECT_UNLOCK (playbin);
2172       break;
2173     case PROP_BUFFER_DURATION:
2174       GST_OBJECT_LOCK (playbin);
2175       g_value_set_int64 (value, playbin->buffer_duration);
2176       GST_OBJECT_UNLOCK (playbin);
2177       break;
2178     case PROP_AV_OFFSET:
2179       g_value_set_int64 (value,
2180           gst_play_sink_get_av_offset (playbin->playsink));
2181       break;
2182     case PROP_RING_BUFFER_MAX_SIZE:
2183       g_value_set_uint64 (value, playbin->ring_buffer_max_size);
2184       break;
2185     default:
2186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2187       break;
2188   }
2189 }
2190
2191 static void
2192 gst_play_bin_update_cached_duration_from_query (GstPlayBin * playbin,
2193     gboolean valid, GstQuery * query)
2194 {
2195   GstFormat fmt;
2196   gint64 duration;
2197   gint i;
2198
2199   GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
2200   gst_query_parse_duration (query, &fmt, &duration);
2201
2202   for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2203     if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
2204       playbin->duration[i].valid = valid;
2205       playbin->duration[i].format = fmt;
2206       playbin->duration[i].duration = valid ? duration : -1;
2207       break;
2208     }
2209   }
2210 }
2211
2212 static void
2213 gst_play_bin_update_cached_duration (GstPlayBin * playbin)
2214 {
2215   const GstFormat formats[] =
2216       { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
2217   gboolean ret;
2218   GstQuery *query;
2219   gint i;
2220
2221   GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
2222   for (i = 0; i < G_N_ELEMENTS (formats); i++) {
2223     query = gst_query_new_duration (formats[i]);
2224     ret =
2225         GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
2226         query);
2227     gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
2228     gst_query_unref (query);
2229   }
2230 }
2231
2232 static gboolean
2233 gst_play_bin_query (GstElement * element, GstQuery * query)
2234 {
2235   GstPlayBin *playbin = GST_PLAY_BIN (element);
2236   gboolean ret;
2237
2238   /* During a group switch we shouldn't allow duration queries
2239    * because it's not clear if the old or new group's duration
2240    * is returned and if the sinks are already playing new data
2241    * or old data. See bug #585969
2242    *
2243    * While we're at it, also don't do any other queries during
2244    * a group switch or any other event that causes topology changes
2245    * by taking the playbin lock in any case.
2246    */
2247   GST_PLAY_BIN_LOCK (playbin);
2248
2249   if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
2250     GstSourceGroup *group = playbin->curr_group;
2251     gboolean pending;
2252
2253     GST_SOURCE_GROUP_LOCK (group);
2254     if (group->stream_changed_pending_lock) {
2255       g_mutex_lock (group->stream_changed_pending_lock);
2256       pending = group->pending || group->stream_changed_pending;
2257       g_mutex_unlock (group->stream_changed_pending_lock);
2258     } else {
2259       pending = group->pending;
2260     }
2261     if (pending) {
2262       GstFormat fmt;
2263       gint i;
2264
2265       ret = FALSE;
2266       gst_query_parse_duration (query, &fmt, NULL);
2267       for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2268         if (fmt == playbin->duration[i].format) {
2269           ret = playbin->duration[i].valid;
2270           gst_query_set_duration (query, fmt,
2271               (ret ? playbin->duration[i].duration : -1));
2272           break;
2273         }
2274       }
2275       /* if nothing cached yet, we might as well request duration,
2276        * such as during initial startup */
2277       if (ret) {
2278         GST_DEBUG_OBJECT (playbin,
2279             "Taking cached duration because of pending group switch: %d", ret);
2280         GST_SOURCE_GROUP_UNLOCK (group);
2281         GST_PLAY_BIN_UNLOCK (playbin);
2282         return ret;
2283       }
2284     }
2285     GST_SOURCE_GROUP_UNLOCK (group);
2286   }
2287
2288   ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
2289
2290   if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
2291     gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
2292   GST_PLAY_BIN_UNLOCK (playbin);
2293
2294   return ret;
2295 }
2296
2297 /* mime types we are not handling on purpose right now, don't post a
2298  * missing-plugin message for these */
2299 static const gchar *blacklisted_mimes[] = {
2300   NULL
2301 };
2302
2303 static void
2304 gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
2305 {
2306   GstPlayBin *playbin = GST_PLAY_BIN (bin);
2307   GstSourceGroup *group;
2308
2309   if (gst_is_missing_plugin_message (msg)) {
2310     gchar *detail;
2311     guint i;
2312
2313     detail = gst_missing_plugin_message_get_installer_detail (msg);
2314     for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
2315       if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
2316         GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
2317         gst_message_unref (msg);
2318         g_free (detail);
2319         return;
2320       }
2321     }
2322     g_free (detail);
2323   } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ELEMENT) {
2324     const GstStructure *s = gst_message_get_structure (msg);
2325
2326     /* Drop all stream-changed messages except the last one */
2327     if (strcmp ("playbin2-stream-changed", gst_structure_get_name (s)) == 0) {
2328       guint32 seqnum = gst_message_get_seqnum (msg);
2329       GList *l, *l_prev;
2330
2331       group = playbin->curr_group;
2332       g_mutex_lock (group->stream_changed_pending_lock);
2333       for (l = group->stream_changed_pending; l;) {
2334         guint32 l_seqnum = GPOINTER_TO_UINT (l->data);
2335
2336         if (l_seqnum == seqnum) {
2337           l_prev = l;
2338           l = l->next;
2339           group->stream_changed_pending =
2340               g_list_delete_link (group->stream_changed_pending, l_prev);
2341           if (group->stream_changed_pending) {
2342             gst_message_unref (msg);
2343             msg = NULL;
2344             break;
2345           }
2346         } else {
2347           l = l->next;
2348         }
2349       }
2350       g_mutex_unlock (group->stream_changed_pending_lock);
2351     }
2352   } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_START ||
2353       GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE) {
2354     GstObject *src = GST_OBJECT_CAST (msg->src);
2355
2356     /* Ignore async state changes from the uridecodebin children,
2357      * see bug #602000. */
2358     group = playbin->curr_group;
2359     if (src && (group = playbin->curr_group) &&
2360         ((group->uridecodebin && src == GST_OBJECT_CAST (group->uridecodebin))
2361             || (group->suburidecodebin
2362                 && src == GST_OBJECT_CAST (group->suburidecodebin)))) {
2363       GST_DEBUG_OBJECT (playbin,
2364           "Ignoring async state change of uridecodebin: %s",
2365           GST_OBJECT_NAME (src));
2366       gst_message_unref (msg);
2367       msg = NULL;
2368     }
2369   } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
2370     /* If we get an error of the subtitle uridecodebin transform
2371      * them into warnings and disable the subtitles */
2372     group = playbin->curr_group;
2373     if (group && group->suburidecodebin) {
2374       if (G_UNLIKELY (gst_object_has_ancestor (msg->src, GST_OBJECT_CAST
2375                   (group->suburidecodebin)))) {
2376         GError *err;
2377         gchar *debug = NULL;
2378         GstMessage *new_msg;
2379         GstIterator *it;
2380         gboolean done = FALSE;
2381
2382         gst_message_parse_error (msg, &err, &debug);
2383         new_msg = gst_message_new_warning (msg->src, err, debug);
2384
2385         gst_message_unref (msg);
2386         g_error_free (err);
2387         g_free (debug);
2388         msg = new_msg;
2389
2390         REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
2391         REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
2392         REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
2393         REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
2394
2395         it = gst_element_iterate_src_pads (group->suburidecodebin);
2396         while (it && !done) {
2397           GstPad *p = NULL;
2398           GstIteratorResult res;
2399
2400           res = gst_iterator_next (it, (gpointer) & p);
2401
2402           switch (res) {
2403             case GST_ITERATOR_DONE:
2404               done = TRUE;
2405               break;
2406             case GST_ITERATOR_OK:
2407               pad_removed_cb (NULL, p, group);
2408               gst_object_unref (p);
2409               break;
2410
2411             case GST_ITERATOR_RESYNC:
2412               gst_iterator_resync (it);
2413               break;
2414             case GST_ITERATOR_ERROR:
2415               done = TRUE;
2416               break;
2417           }
2418         }
2419         if (it)
2420           gst_iterator_free (it);
2421
2422         gst_object_ref (group->suburidecodebin);
2423         gst_bin_remove (bin, group->suburidecodebin);
2424         gst_element_set_locked_state (group->suburidecodebin, FALSE);
2425
2426         if (group->sub_pending) {
2427           group->sub_pending = FALSE;
2428           no_more_pads_cb (NULL, group);
2429         }
2430       }
2431     }
2432   }
2433
2434   if (msg)
2435     GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
2436 }
2437
2438 static void
2439 selector_active_pad_changed (GObject * selector, GParamSpec * pspec,
2440     GstPlayBin * playbin)
2441 {
2442   const gchar *property;
2443   GstSourceGroup *group;
2444   GstSourceSelect *select = NULL;
2445   int i;
2446
2447   GST_PLAY_BIN_LOCK (playbin);
2448   group = get_group (playbin);
2449
2450   for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2451     if (selector == G_OBJECT (group->selector[i].selector)) {
2452       select = &group->selector[i];
2453     }
2454   }
2455
2456   /* We got a pad-change after our group got switched out; no need to notify */
2457   if (!select) {
2458     GST_PLAY_BIN_UNLOCK (playbin);
2459     return;
2460   }
2461
2462   switch (select->type) {
2463     case GST_PLAY_SINK_TYPE_VIDEO:
2464     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2465       property = "current-video";
2466       playbin->current_video = get_current_stream_number (playbin,
2467           group->video_channels);
2468       break;
2469     case GST_PLAY_SINK_TYPE_AUDIO:
2470     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2471       property = "current-audio";
2472       playbin->current_audio = get_current_stream_number (playbin,
2473           group->audio_channels);
2474       break;
2475     case GST_PLAY_SINK_TYPE_TEXT:
2476       property = "current-text";
2477       playbin->current_text = get_current_stream_number (playbin,
2478           group->text_channels);
2479       break;
2480     default:
2481       property = NULL;
2482   }
2483   GST_PLAY_BIN_UNLOCK (playbin);
2484
2485   if (property)
2486     g_object_notify (G_OBJECT (playbin), property);
2487 }
2488
2489 static void
2490 selector_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
2491 {
2492   /* no nothing */
2493   GST_DEBUG_OBJECT (pad, "blocked callback, blocked: %d", blocked);
2494 }
2495
2496 /* this callback sends a delayed event once the pad becomes unblocked */
2497 static gboolean
2498 stream_changed_data_probe (GstPad * pad, GstMiniObject * object, gpointer data)
2499 {
2500   GstSourceSelect *select = (GstSourceSelect *) data;
2501   GstEvent *e;
2502
2503   /* we need do this just once, so cleanup first */
2504   gst_pad_remove_data_probe (pad, select->sinkpad_data_probe);
2505   select->sinkpad_data_probe = 0;
2506   e = select->sinkpad_delayed_event;
2507   select->sinkpad_delayed_event = NULL;
2508
2509   /* really, this should not happen */
2510   if (!e) {
2511     GST_WARNING ("Data probed called, but no delayed event");
2512     return TRUE;
2513   }
2514
2515   if (GST_IS_EVENT (object)
2516       && GST_EVENT_TYPE (GST_EVENT_CAST (object)) == GST_EVENT_NEWSEGMENT) {
2517     /* push the event first, then send the delayed one */
2518     gst_event_ref (GST_EVENT_CAST (object));
2519     gst_pad_send_event (pad, GST_EVENT_CAST (object));
2520     gst_pad_send_event (pad, e);
2521     return FALSE;
2522   } else {
2523     /* send delayed event, then allow the caller to go on */
2524     gst_pad_send_event (pad, e);
2525     return TRUE;
2526   }
2527 }
2528
2529 /* helper function to lookup stuff in lists */
2530 static gboolean
2531 array_has_value (const gchar * values[], const gchar * value, gboolean exact)
2532 {
2533   gint i;
2534
2535   for (i = 0; values[i]; i++) {
2536     if (exact && !strcmp (value, values[i]))
2537       return TRUE;
2538     if (!exact && g_str_has_prefix (value, values[i]))
2539       return TRUE;
2540   }
2541   return FALSE;
2542 }
2543
2544 typedef struct
2545 {
2546   GstPlayBin *playbin;
2547   gint stream_id;
2548   GstPlaySinkType type;
2549 } NotifyTagsData;
2550
2551 static void
2552 notify_tags_cb (GObject * object, GParamSpec * pspec, gpointer user_data)
2553 {
2554   NotifyTagsData *ntdata = (NotifyTagsData *) user_data;
2555   gint signal;
2556
2557   GST_DEBUG_OBJECT (ntdata->playbin, "Tags on pad %" GST_PTR_FORMAT
2558       " with stream id %d and type %d have changed",
2559       object, ntdata->stream_id, ntdata->type);
2560
2561   switch (ntdata->type) {
2562     case GST_PLAY_SINK_TYPE_VIDEO:
2563     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2564       signal = SIGNAL_VIDEO_TAGS_CHANGED;
2565       break;
2566     case GST_PLAY_SINK_TYPE_AUDIO:
2567     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2568       signal = SIGNAL_AUDIO_TAGS_CHANGED;
2569       break;
2570     case GST_PLAY_SINK_TYPE_TEXT:
2571       signal = SIGNAL_TEXT_TAGS_CHANGED;
2572       break;
2573     default:
2574       signal = -1;
2575       break;
2576   }
2577
2578   if (signal >= 0)
2579     g_signal_emit (G_OBJECT (ntdata->playbin), gst_play_bin_signals[signal], 0,
2580         ntdata->stream_id);
2581 }
2582
2583 /* this function is called when a new pad is added to decodebin. We check the
2584  * type of the pad and add it to the selector element of the group.
2585  */
2586 static void
2587 pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
2588 {
2589   GstPlayBin *playbin;
2590   GstCaps *caps;
2591   const GstStructure *s;
2592   const gchar *name;
2593   GstPad *sinkpad;
2594   GstPadLinkReturn res;
2595   GstSourceSelect *select = NULL;
2596   gint i, pass;
2597   gboolean changed = FALSE;
2598
2599   playbin = group->playbin;
2600
2601   caps = gst_pad_get_caps_reffed (pad);
2602   s = gst_caps_get_structure (caps, 0);
2603   name = gst_structure_get_name (s);
2604
2605   GST_DEBUG_OBJECT (playbin,
2606       "pad %s:%s with caps %" GST_PTR_FORMAT " added in group %p",
2607       GST_DEBUG_PAD_NAME (pad), caps, group);
2608
2609   /* major type of the pad, this determines the selector to use,
2610      try exact match first so we don't prematurely match video/
2611      for video/x-dvd-subpicture */
2612   for (pass = 0; !select && pass < 2; pass++) {
2613     for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2614       if (array_has_value (group->selector[i].media_list, name, pass == 0)) {
2615         select = &group->selector[i];
2616         break;
2617       } else if (group->selector[i].get_media_caps) {
2618         GstCaps *media_caps = group->selector[i].get_media_caps ();
2619
2620         if (media_caps && gst_caps_can_intersect (media_caps, caps)) {
2621           select = &group->selector[i];
2622           gst_caps_unref (media_caps);
2623           break;
2624         }
2625         gst_caps_unref (media_caps);
2626       }
2627     }
2628   }
2629   /* no selector found for the media type, don't bother linking it to a
2630    * selector. This will leave the pad unlinked and thus ignored. */
2631   if (select == NULL)
2632     goto unknown_type;
2633
2634   GST_SOURCE_GROUP_LOCK (group);
2635   if (select->selector == NULL && playbin->have_selector) {
2636     /* no selector, create one */
2637     GST_DEBUG_OBJECT (playbin, "creating new input selector");
2638     select->selector = gst_element_factory_make ("input-selector", NULL);
2639     if (select->selector == NULL) {
2640       /* post the missing selector message only once */
2641       playbin->have_selector = FALSE;
2642       gst_element_post_message (GST_ELEMENT_CAST (playbin),
2643           gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
2644               "input-selector"));
2645       GST_ELEMENT_WARNING (playbin, CORE, MISSING_PLUGIN,
2646           (_("Missing element '%s' - check your GStreamer installation."),
2647               "input-selector"), (NULL));
2648     } else {
2649       g_object_set (select->selector, "sync-streams", TRUE, NULL);
2650
2651       g_signal_connect (select->selector, "notify::active-pad",
2652           G_CALLBACK (selector_active_pad_changed), playbin);
2653
2654       GST_DEBUG_OBJECT (playbin, "adding new selector %p", select->selector);
2655       gst_bin_add (GST_BIN_CAST (playbin), select->selector);
2656       gst_element_set_state (select->selector, GST_STATE_PAUSED);
2657     }
2658   }
2659
2660   if (select->srcpad == NULL) {
2661     if (select->selector) {
2662       /* save source pad of the selector */
2663       select->srcpad = gst_element_get_static_pad (select->selector, "src");
2664     } else {
2665       /* no selector, use the pad as the source pad then */
2666       select->srcpad = gst_object_ref (pad);
2667     }
2668
2669     /* block the selector srcpad. It's possible that multiple decodebins start
2670      * pushing data into the selectors before we have a chance to collect all
2671      * streams and connect the sinks, resulting in not-linked errors. After we
2672      * configured the sinks we will unblock them all. */
2673     GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, select->srcpad);
2674     gst_pad_set_blocked_async (select->srcpad, TRUE, selector_blocked, NULL);
2675   }
2676
2677   /* get sinkpad for the new stream */
2678   if (select->selector) {
2679     if ((sinkpad = gst_element_get_request_pad (select->selector, "sink%d"))) {
2680       gulong notify_tags_handler = 0;
2681       NotifyTagsData *ntdata;
2682
2683       GST_DEBUG_OBJECT (playbin, "got pad %s:%s from selector",
2684           GST_DEBUG_PAD_NAME (sinkpad));
2685
2686       /* store the selector for the pad */
2687       g_object_set_data (G_OBJECT (sinkpad), "playbin2.select", select);
2688
2689       /* connect to the notify::tags signal for our
2690        * own *-tags-changed signals
2691        */
2692       ntdata = g_new0 (NotifyTagsData, 1);
2693       ntdata->playbin = playbin;
2694       ntdata->stream_id = select->channels->len;
2695       ntdata->type = select->type;
2696
2697       notify_tags_handler =
2698           g_signal_connect_data (G_OBJECT (sinkpad), "notify::tags",
2699           G_CALLBACK (notify_tags_cb), ntdata, (GClosureNotify) g_free,
2700           (GConnectFlags) 0);
2701       g_object_set_data (G_OBJECT (sinkpad), "playbin2.notify_tags_handler",
2702           (gpointer) (guintptr) notify_tags_handler);
2703
2704       /* store the pad in the array */
2705       GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
2706       g_ptr_array_add (select->channels, sinkpad);
2707
2708       res = gst_pad_link (pad, sinkpad);
2709       if (GST_PAD_LINK_FAILED (res))
2710         goto link_failed;
2711
2712       /* store selector pad so we can release it */
2713       g_object_set_data (G_OBJECT (pad), "playbin2.sinkpad", sinkpad);
2714
2715       changed = TRUE;
2716       GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to selector %p",
2717           GST_DEBUG_PAD_NAME (pad), select->selector);
2718     }
2719   } else {
2720     /* no selector, don't configure anything, we'll link the new pad directly to
2721      * the sink. */
2722     changed = FALSE;
2723     sinkpad = NULL;
2724   }
2725   GST_SOURCE_GROUP_UNLOCK (group);
2726
2727   if (changed) {
2728     int signal;
2729     gboolean always_ok = (decodebin == group->suburidecodebin);
2730
2731     switch (select->type) {
2732       case GST_PLAY_SINK_TYPE_VIDEO:
2733       case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2734         /* we want to return NOT_LINKED for unselected pads but only for pads
2735          * from the normal uridecodebin. This makes sure that subtitle streams
2736          * are not raced past audio/video from decodebin2's multiqueue.
2737          * For pads from suburidecodebin OK should always be returned, otherwise
2738          * it will most likely stop. */
2739         g_object_set (sinkpad, "always-ok", always_ok, NULL);
2740         signal = SIGNAL_VIDEO_CHANGED;
2741         break;
2742       case GST_PLAY_SINK_TYPE_AUDIO:
2743       case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2744         g_object_set (sinkpad, "always-ok", always_ok, NULL);
2745         signal = SIGNAL_AUDIO_CHANGED;
2746         break;
2747       case GST_PLAY_SINK_TYPE_TEXT:
2748         g_object_set (sinkpad, "always-ok", always_ok, NULL);
2749         signal = SIGNAL_TEXT_CHANGED;
2750         break;
2751       default:
2752         signal = -1;
2753     }
2754
2755     if (signal >= 0)
2756       g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0, NULL);
2757   }
2758
2759 done:
2760   gst_caps_unref (caps);
2761   return;
2762
2763   /* ERRORS */
2764 unknown_type:
2765   {
2766     GST_ERROR_OBJECT (playbin, "unknown type %s for pad %s:%s",
2767         name, GST_DEBUG_PAD_NAME (pad));
2768     goto done;
2769   }
2770 link_failed:
2771   {
2772     GST_ERROR_OBJECT (playbin,
2773         "failed to link pad %s:%s to selector, reason %d",
2774         GST_DEBUG_PAD_NAME (pad), res);
2775     GST_SOURCE_GROUP_UNLOCK (group);
2776     goto done;
2777   }
2778 }
2779
2780 /* called when a pad is removed from the uridecodebin. We unlink the pad from
2781  * the selector. This will make the selector select a new pad. */
2782 static void
2783 pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
2784 {
2785   GstPlayBin *playbin;
2786   GstPad *peer;
2787   GstElement *selector;
2788   GstSourceSelect *select;
2789
2790   playbin = group->playbin;
2791
2792   GST_DEBUG_OBJECT (playbin,
2793       "pad %s:%s removed from group %p", GST_DEBUG_PAD_NAME (pad), group);
2794
2795   GST_SOURCE_GROUP_LOCK (group);
2796   /* get the selector sinkpad */
2797   if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin2.sinkpad")))
2798     goto not_linked;
2799
2800   if ((select = g_object_get_data (G_OBJECT (peer), "playbin2.select"))) {
2801     gulong notify_tags_handler;
2802
2803     notify_tags_handler =
2804         (guintptr) g_object_get_data (G_OBJECT (peer),
2805         "playbin2.notify_tags_handler");
2806     if (notify_tags_handler != 0)
2807       g_signal_handler_disconnect (G_OBJECT (peer), notify_tags_handler);
2808     g_object_set_data (G_OBJECT (peer), "playbin2.notify_tags_handler", NULL);
2809
2810     /* remove the pad from the array */
2811     g_ptr_array_remove (select->channels, peer);
2812     GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer);
2813   }
2814
2815   /* unlink the pad now (can fail, the pad is unlinked before it's removed) */
2816   gst_pad_unlink (pad, peer);
2817
2818   /* get selector, this can be NULL when the element is removing the pads
2819    * because it's being disposed. */
2820   selector = GST_ELEMENT_CAST (gst_pad_get_parent (peer));
2821   if (!selector) {
2822     gst_object_unref (peer);
2823     goto no_selector;
2824   }
2825
2826   /* release the pad to the selector, this will make the selector choose a new
2827    * pad. */
2828   gst_element_release_request_pad (selector, peer);
2829   gst_object_unref (peer);
2830
2831   gst_object_unref (selector);
2832   GST_SOURCE_GROUP_UNLOCK (group);
2833
2834   return;
2835
2836   /* ERRORS */
2837 not_linked:
2838   {
2839     GST_DEBUG_OBJECT (playbin, "pad not linked");
2840     GST_SOURCE_GROUP_UNLOCK (group);
2841     return;
2842   }
2843 no_selector:
2844   {
2845     GST_DEBUG_OBJECT (playbin, "selector not found");
2846     GST_SOURCE_GROUP_UNLOCK (group);
2847     return;
2848   }
2849 }
2850
2851 /* we get called when all pads are available and we must connect the sinks to
2852  * them.
2853  * The main purpose of the code is to see if we have video/audio and subtitles
2854  * and pick the right pipelines to display them.
2855  *
2856  * The selectors installed on the group tell us about the presence of
2857  * audio/video and subtitle streams. This allows us to see if we need
2858  * visualisation, video or/and audio.
2859  */
2860 static void
2861 no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
2862 {
2863   GstPlayBin *playbin;
2864   GstPadLinkReturn res;
2865   gint i;
2866   gboolean configure;
2867
2868   playbin = group->playbin;
2869
2870   GST_DEBUG_OBJECT (playbin, "no more pads in group %p", group);
2871
2872   GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
2873
2874   GST_SOURCE_GROUP_LOCK (group);
2875   for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2876     GstSourceSelect *select = &group->selector[i];
2877
2878     /* check if the specific media type was detected and thus has a selector
2879      * created for it. If there is the media type, get a sinkpad from the sink
2880      * and link it. We only do this if we have not yet requested the sinkpad
2881      * before. */
2882     if (select->srcpad && select->sinkpad == NULL) {
2883       GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", select->type);
2884       select->sinkpad =
2885           gst_play_sink_request_pad (playbin->playsink, select->type);
2886
2887       res = gst_pad_link (select->srcpad, select->sinkpad);
2888       GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
2889           select->media_list[0], res);
2890       if (res != GST_PAD_LINK_OK) {
2891         GST_ELEMENT_ERROR (playbin, CORE, PAD,
2892             ("Internal playbin error."),
2893             ("Failed to link selector to sink. Error %d", res));
2894       }
2895     }
2896   }
2897   GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
2898       group->pending - 1);
2899
2900   if (group->pending > 0)
2901     group->pending--;
2902
2903   if (group->suburidecodebin == decodebin)
2904     group->sub_pending = FALSE;
2905
2906   if (group->pending == 0) {
2907     /* we are the last group to complete, we will configure the output and then
2908      * signal the other waiters. */
2909     GST_LOG_OBJECT (playbin, "last group complete");
2910     configure = TRUE;
2911   } else {
2912     GST_LOG_OBJECT (playbin, "have more pending groups");
2913     configure = FALSE;
2914   }
2915   GST_SOURCE_GROUP_UNLOCK (group);
2916
2917   if (configure) {
2918     /* if we have custom sinks, configure them now */
2919     GST_SOURCE_GROUP_LOCK (group);
2920
2921     if (group->audio_sink) {
2922       GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
2923           group->audio_sink);
2924       gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
2925           group->audio_sink);
2926     }
2927
2928     if (group->video_sink) {
2929       GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
2930           group->video_sink);
2931       gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
2932           group->video_sink);
2933     }
2934
2935     if (playbin->text_sink) {
2936       GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
2937           playbin->text_sink);
2938       gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
2939           playbin->text_sink);
2940     }
2941
2942     GST_SOURCE_GROUP_UNLOCK (group);
2943
2944     /* signal the other decodebins that they can continue now. */
2945     GST_SOURCE_GROUP_LOCK (group);
2946     /* unblock all selectors */
2947     for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2948       GstSourceSelect *select = &group->selector[i];
2949
2950       /* All streamsynchronizer streams should see stream-changed message,
2951        * to arrange for blocking unblocking. */
2952       if (select->sinkpad) {
2953         GstStructure *s;
2954         GstMessage *msg;
2955         GstEvent *event;
2956         guint32 seqnum;
2957
2958         s = gst_structure_new ("playbin2-stream-changed", "uri", G_TYPE_STRING,
2959             group->uri, NULL);
2960         if (group->suburi)
2961           gst_structure_set (s, "suburi", G_TYPE_STRING, group->suburi, NULL);
2962         msg = gst_message_new_element (GST_OBJECT_CAST (playbin), s);
2963         seqnum = gst_message_get_seqnum (msg);
2964         event = gst_event_new_sink_message (msg);
2965         g_mutex_lock (group->stream_changed_pending_lock);
2966         group->stream_changed_pending =
2967             g_list_prepend (group->stream_changed_pending,
2968             GUINT_TO_POINTER (seqnum));
2969
2970         /* remove any data probe we might have, and replace */
2971         if (select->sinkpad_delayed_event)
2972           gst_event_unref (select->sinkpad_delayed_event);
2973         select->sinkpad_delayed_event = event;
2974         if (select->sinkpad_data_probe)
2975           gst_pad_remove_data_probe (select->sinkpad,
2976               select->sinkpad_data_probe);
2977
2978         /* we go to the trouble of setting a probe on the pad to send
2979            the playbin2-stream-changed event as sending it here might
2980            find that the pad is blocked, so we'd block here, and the
2981            pad might not be linked yet. Additionally, sending it here
2982            apparently would be on the wrong thread */
2983         select->sinkpad_data_probe =
2984             gst_pad_add_data_probe (select->sinkpad,
2985             (GCallback) stream_changed_data_probe, (gpointer) select);
2986
2987         g_mutex_unlock (group->stream_changed_pending_lock);
2988         gst_message_unref (msg);
2989       }
2990
2991       if (select->srcpad) {
2992         GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
2993             select->srcpad);
2994         gst_pad_set_blocked_async (select->srcpad, FALSE, selector_blocked,
2995             NULL);
2996       }
2997     }
2998     GST_SOURCE_GROUP_UNLOCK (group);
2999   }
3000
3001   GST_PLAY_BIN_SHUTDOWN_UNLOCK (playbin);
3002
3003   return;
3004
3005 shutdown:
3006   {
3007     GST_DEBUG ("ignoring, we are shutting down");
3008     /* Request a flushing pad from playsink that we then link to the selector.
3009      * Then we unblock the selectors so that they stop with a WRONG_STATE
3010      * instead of a NOT_LINKED error.
3011      */
3012     GST_SOURCE_GROUP_LOCK (group);
3013     for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3014       GstSourceSelect *select = &group->selector[i];
3015
3016       if (select->srcpad) {
3017         if (select->sinkpad == NULL) {
3018           GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad");
3019           select->sinkpad =
3020               gst_play_sink_request_pad (playbin->playsink,
3021               GST_PLAY_SINK_TYPE_FLUSHING);
3022           res = gst_pad_link (select->srcpad, select->sinkpad);
3023           GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res);
3024         }
3025         GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
3026             select->srcpad);
3027         gst_pad_set_blocked_async (select->srcpad, FALSE, selector_blocked,
3028             NULL);
3029       }
3030     }
3031     GST_SOURCE_GROUP_UNLOCK (group);
3032     return;
3033   }
3034 }
3035
3036 static void
3037 drained_cb (GstElement * decodebin, GstSourceGroup * group)
3038 {
3039   GstPlayBin *playbin;
3040
3041   playbin = group->playbin;
3042
3043   GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
3044
3045   /* after this call, we should have a next group to activate or we EOS */
3046   g_signal_emit (G_OBJECT (playbin),
3047       gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
3048
3049   /* now activate the next group. If the app did not set a uri, this will
3050    * fail and we can do EOS */
3051   setup_next_source (playbin, GST_STATE_PAUSED);
3052 }
3053
3054 /* Like gst_element_factory_can_sink_any_caps() but doesn't
3055  * allow ANY caps on the sinkpad template */
3056 static gboolean
3057 _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
3058 {
3059   const GList *templs;
3060
3061   templs = gst_element_factory_get_static_pad_templates (factory);
3062
3063   while (templs) {
3064     GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data;
3065
3066     if (templ->direction == GST_PAD_SINK) {
3067       GstCaps *templcaps = gst_static_caps_get (&templ->static_caps);
3068
3069       if (!gst_caps_is_any (templcaps)
3070           && gst_caps_can_intersect (templcaps, caps)) {
3071         gst_caps_unref (templcaps);
3072         return TRUE;
3073       }
3074       gst_caps_unref (templcaps);
3075     }
3076     templs = g_list_next (templs);
3077   }
3078
3079   return FALSE;
3080 }
3081
3082 /* Called when we must provide a list of factories to plug to @pad with @caps.
3083  * We first check if we have a sink that can handle the format and if we do, we
3084  * return NULL, to expose the pad. If we have no sink (or the sink does not
3085  * work), we return the list of elements that can connect. */
3086 static GValueArray *
3087 autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
3088     GstCaps * caps, GstSourceGroup * group)
3089 {
3090   GstPlayBin *playbin;
3091   GList *mylist, *tmp;
3092   GValueArray *result;
3093
3094   playbin = group->playbin;
3095
3096   GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
3097       group, GST_DEBUG_PAD_NAME (pad), caps);
3098
3099   /* filter out the elements based on the caps. */
3100   g_mutex_lock (playbin->elements_lock);
3101   gst_play_bin_update_elements_list (playbin);
3102   mylist =
3103       gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
3104       FALSE);
3105   g_mutex_unlock (playbin->elements_lock);
3106
3107   GST_DEBUG_OBJECT (playbin, "found factories %p", mylist);
3108   GST_PLUGIN_FEATURE_LIST_DEBUG (mylist);
3109
3110   /* 2 additional elements for the already set audio/video sinks */
3111   result = g_value_array_new (g_list_length (mylist) + 2);
3112
3113   /* Check if we already have an audio/video sink and if this is the case
3114    * put it as the first element of the array */
3115   if (group->audio_sink) {
3116     GstElementFactory *factory = gst_element_get_factory (group->audio_sink);
3117
3118     if (factory && _factory_can_sink_caps (factory, caps)) {
3119       GValue val = { 0, };
3120
3121       g_value_init (&val, G_TYPE_OBJECT);
3122       g_value_set_object (&val, factory);
3123       result = g_value_array_append (result, &val);
3124       g_value_unset (&val);
3125     }
3126   }
3127
3128   if (group->video_sink) {
3129     GstElementFactory *factory = gst_element_get_factory (group->video_sink);
3130
3131     if (factory && _factory_can_sink_caps (factory, caps)) {
3132       GValue val = { 0, };
3133
3134       g_value_init (&val, G_TYPE_OBJECT);
3135       g_value_set_object (&val, factory);
3136       result = g_value_array_append (result, &val);
3137       g_value_unset (&val);
3138     }
3139   }
3140
3141   for (tmp = mylist; tmp; tmp = tmp->next) {
3142     GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
3143     GValue val = { 0, };
3144
3145     if (group->audio_sink && gst_element_factory_list_is_type (factory,
3146             GST_ELEMENT_FACTORY_TYPE_SINK |
3147             GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
3148       continue;
3149     }
3150     if (group->video_sink && gst_element_factory_list_is_type (factory,
3151             GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO
3152             | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
3153       continue;
3154     }
3155
3156     g_value_init (&val, G_TYPE_OBJECT);
3157     g_value_set_object (&val, factory);
3158     g_value_array_append (result, &val);
3159     g_value_unset (&val);
3160   }
3161   gst_plugin_feature_list_free (mylist);
3162
3163   return result;
3164 }
3165
3166 /* autoplug-continue decides, if a pad has raw caps that can be exposed
3167  * directly or if further decoding is necessary. We use this to expose
3168  * supported subtitles directly */
3169
3170 /* FIXME 0.11: Remove the checks for ANY caps, a sink should specify
3171  * explicitly the caps it supports and if it claims to support ANY
3172  * caps it really should support everything */
3173 static gboolean
3174 autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps,
3175     GstSourceGroup * group)
3176 {
3177   gboolean ret = TRUE;
3178   GstElement *sink;
3179   GstPad *sinkpad = NULL;
3180
3181   GST_PLAY_BIN_LOCK (group->playbin);
3182   GST_SOURCE_GROUP_LOCK (group);
3183
3184   if ((sink = group->playbin->text_sink))
3185     sinkpad = gst_element_get_static_pad (sink, "sink");
3186   if (sinkpad) {
3187     GstCaps *sinkcaps;
3188
3189     /* Ignore errors here, if a custom sink fails to go
3190      * to READY things are wrong and will error out later
3191      */
3192     if (GST_STATE (sink) < GST_STATE_READY)
3193       gst_element_set_state (sink, GST_STATE_READY);
3194
3195     sinkcaps = gst_pad_get_caps_reffed (sinkpad);
3196     if (!gst_caps_is_any (sinkcaps))
3197       ret = !gst_pad_accept_caps (sinkpad, caps);
3198     gst_caps_unref (sinkcaps);
3199     gst_object_unref (sinkpad);
3200   } else {
3201     GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
3202     ret = !gst_caps_is_subset (caps, subcaps);
3203     gst_caps_unref (subcaps);
3204   }
3205   /* If autoplugging can stop don't do additional checks */
3206   if (!ret)
3207     goto done;
3208
3209   /* If this is from the subtitle uridecodebin we don't need to
3210    * check the audio and video sink */
3211   if (group->suburidecodebin
3212       && gst_object_has_ancestor (GST_OBJECT_CAST (element),
3213           GST_OBJECT_CAST (group->suburidecodebin)))
3214     goto done;
3215
3216   if ((sink = group->audio_sink)) {
3217     sinkpad = gst_element_get_static_pad (sink, "sink");
3218     if (sinkpad) {
3219       GstCaps *sinkcaps;
3220
3221       /* Ignore errors here, if a custom sink fails to go
3222        * to READY things are wrong and will error out later
3223        */
3224       if (GST_STATE (sink) < GST_STATE_READY)
3225         gst_element_set_state (sink, GST_STATE_READY);
3226
3227       sinkcaps = gst_pad_get_caps_reffed (sinkpad);
3228       if (!gst_caps_is_any (sinkcaps))
3229         ret = !gst_pad_accept_caps (sinkpad, caps);
3230       gst_caps_unref (sinkcaps);
3231       gst_object_unref (sinkpad);
3232     }
3233   }
3234   if (!ret)
3235     goto done;
3236
3237   if ((sink = group->video_sink)) {
3238     sinkpad = gst_element_get_static_pad (sink, "sink");
3239     if (sinkpad) {
3240       GstCaps *sinkcaps;
3241
3242       /* Ignore errors here, if a custom sink fails to go
3243        * to READY things are wrong and will error out later
3244        */
3245       if (GST_STATE (sink) < GST_STATE_READY)
3246         gst_element_set_state (sink, GST_STATE_READY);
3247
3248       sinkcaps = gst_pad_get_caps_reffed (sinkpad);
3249       if (!gst_caps_is_any (sinkcaps))
3250         ret = !gst_pad_accept_caps (sinkpad, caps);
3251       gst_caps_unref (sinkcaps);
3252       gst_object_unref (sinkpad);
3253     }
3254   }
3255
3256 done:
3257   GST_SOURCE_GROUP_UNLOCK (group);
3258   GST_PLAY_BIN_UNLOCK (group->playbin);
3259
3260   GST_DEBUG_OBJECT (group->playbin,
3261       "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d",
3262       group, GST_DEBUG_PAD_NAME (pad), caps, ret);
3263
3264   return ret;
3265 }
3266
3267 static gboolean
3268 sink_accepts_caps (GstElement * sink, GstCaps * caps)
3269 {
3270   GstPad *sinkpad;
3271
3272   /* ... activate it ... We do this before adding it to the bin so that we
3273    * don't accidentally make it post error messages that will stop
3274    * everything. */
3275   if (GST_STATE (sink) < GST_STATE_READY &&
3276       gst_element_set_state (sink,
3277           GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) {
3278     return FALSE;
3279   }
3280
3281   if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
3282     /* Got the sink pad, now let's see if the element actually does accept the
3283      * caps that we have */
3284     if (!gst_pad_accept_caps (sinkpad, caps)) {
3285       gst_object_unref (sinkpad);
3286       return FALSE;
3287     }
3288     gst_object_unref (sinkpad);
3289   }
3290
3291   return TRUE;
3292 }
3293
3294 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw-int; "
3295     "audio/x-raw-float");
3296 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw-rgb; "
3297     "video/x-raw-yuv; " "video/x-raw-gray");
3298
3299 /* We are asked to select an element. See if the next element to check
3300  * is a sink. If this is the case, we see if the sink works by setting it to
3301  * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
3302  * expose the raw pad so that we can setup the mixers. */
3303 static GstAutoplugSelectResult
3304 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
3305     GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
3306 {
3307   GstPlayBin *playbin;
3308   GstElement *element;
3309   const gchar *klass;
3310   GstPlaySinkType type;
3311   GstElement **sinkp;
3312
3313   playbin = group->playbin;
3314
3315   GST_DEBUG_OBJECT (playbin, "select group %p for %s:%s, %" GST_PTR_FORMAT,
3316       group, GST_DEBUG_PAD_NAME (pad), caps);
3317
3318   GST_DEBUG_OBJECT (playbin, "checking factory %s",
3319       GST_PLUGIN_FEATURE_NAME (factory));
3320
3321   /* if it's not a sink, we make sure the element is compatible with
3322    * the fixed sink */
3323   if (!gst_element_factory_list_is_type (factory,
3324           GST_ELEMENT_FACTORY_TYPE_SINK)) {
3325     gboolean isvideodec = gst_element_factory_list_is_type (factory,
3326         GST_ELEMENT_FACTORY_TYPE_DECODER |
3327         GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3328         GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
3329     gboolean isaudiodec = gst_element_factory_list_is_type (factory,
3330         GST_ELEMENT_FACTORY_TYPE_DECODER |
3331         GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
3332
3333     /* If it is a decoder and we have a fixed sink for the media
3334      * type it outputs, check that the decoder is compatible with this sink */
3335     if ((isvideodec && group->video_sink) || (isaudiodec && group->audio_sink)) {
3336       gboolean compatible = TRUE;
3337       GstPad *sinkpad;
3338       GstCaps *caps;
3339       GstElement *sink;
3340
3341       if (isaudiodec)
3342         sink = group->audio_sink;
3343       else
3344         sink = group->video_sink;
3345
3346       if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
3347         GstPlayFlags flags = gst_play_bin_get_flags (playbin);
3348         GstCaps *raw_caps =
3349             (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
3350             gst_static_caps_get (&raw_video_caps);
3351
3352         caps = gst_pad_get_caps_reffed (sinkpad);
3353
3354         /* If the sink supports raw audio/video, we first check
3355          * if the decoder could output any raw audio/video format
3356          * and assume it is compatible with the sink then. We don't
3357          * do a complete compatibility check here if converters
3358          * are plugged between the decoder and the sink because
3359          * the converters will convert between raw formats and
3360          * even if the decoder format is not supported by the decoder
3361          * a converter will convert it.
3362          *
3363          * We assume here that the converters can convert between
3364          * any raw format.
3365          */
3366         if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
3367                 && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
3368                 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
3369                 && gst_caps_can_intersect (caps, raw_caps))) {
3370           compatible = gst_element_factory_can_src_any_caps (factory, raw_caps)
3371               || gst_element_factory_can_src_any_caps (factory, caps);
3372         } else {
3373           compatible = gst_element_factory_can_src_any_caps (factory, caps);
3374         }
3375
3376         gst_object_unref (sinkpad);
3377         gst_caps_unref (caps);
3378       }
3379
3380       if (compatible)
3381         return GST_AUTOPLUG_SELECT_TRY;
3382
3383       GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
3384           GST_PLUGIN_FEATURE_NAME (factory));
3385
3386       return GST_AUTOPLUG_SELECT_SKIP;
3387     } else
3388       return GST_AUTOPLUG_SELECT_TRY;
3389   }
3390
3391   /* it's a sink, see if an instance of it actually works */
3392   GST_DEBUG_OBJECT (playbin, "we found a sink");
3393
3394   klass = gst_element_factory_get_klass (factory);
3395
3396   /* figure out the klass */
3397   if (strstr (klass, "Audio")) {
3398     GST_DEBUG_OBJECT (playbin, "we found an audio sink");
3399     type = GST_PLAY_SINK_TYPE_AUDIO;
3400     sinkp = &group->audio_sink;
3401   } else if (strstr (klass, "Video")) {
3402     GST_DEBUG_OBJECT (playbin, "we found a video sink");
3403     type = GST_PLAY_SINK_TYPE_VIDEO;
3404     sinkp = &group->video_sink;
3405   } else {
3406     /* unknown klass, skip this element */
3407     GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
3408     return GST_AUTOPLUG_SELECT_SKIP;
3409   }
3410
3411   /* if we are asked to do visualisations and it's an audio sink, skip the
3412    * element. We can only do visualisations with raw sinks */
3413   if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
3414     if (type == GST_PLAY_SINK_TYPE_AUDIO) {
3415       GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
3416       return GST_AUTOPLUG_SELECT_SKIP;
3417     }
3418   }
3419
3420   /* now see if we already have a sink element */
3421   GST_SOURCE_GROUP_LOCK (group);
3422   if (*sinkp) {
3423     GstElement *sink = gst_object_ref (*sinkp);
3424
3425     if (sink_accepts_caps (sink, caps)) {
3426       GST_DEBUG_OBJECT (playbin,
3427           "Existing sink '%s' accepts caps: %" GST_PTR_FORMAT,
3428           GST_ELEMENT_NAME (sink), caps);
3429       gst_object_unref (sink);
3430       GST_SOURCE_GROUP_UNLOCK (group);
3431       return GST_AUTOPLUG_SELECT_EXPOSE;
3432     } else {
3433       GST_DEBUG_OBJECT (playbin,
3434           "Existing sink '%s' does not accept caps: %" GST_PTR_FORMAT,
3435           GST_ELEMENT_NAME (sink), caps);
3436       gst_object_unref (sink);
3437       GST_SOURCE_GROUP_UNLOCK (group);
3438       return GST_AUTOPLUG_SELECT_SKIP;
3439     }
3440   }
3441   GST_DEBUG_OBJECT (playbin, "we have no pending sink, try to create one");
3442
3443   if ((element = gst_element_factory_create (factory, NULL)) == NULL) {
3444     GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
3445         gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
3446     GST_SOURCE_GROUP_UNLOCK (group);
3447     return GST_AUTOPLUG_SELECT_SKIP;
3448   }
3449
3450   /* Check if the selected sink actually supports the
3451    * caps and can be set to READY*/
3452   if (!sink_accepts_caps (element, caps)) {
3453     gst_element_set_state (element, GST_STATE_NULL);
3454     gst_object_unref (element);
3455     GST_SOURCE_GROUP_UNLOCK (group);
3456     return GST_AUTOPLUG_SELECT_SKIP;
3457   }
3458
3459   /* remember the sink in the group now, the element is floating, we take
3460    * ownership now 
3461    *
3462    * store the sink in the group, we will configure it later when we
3463    * reconfigure the sink */
3464   GST_DEBUG_OBJECT (playbin, "remember sink");
3465   gst_object_ref_sink (element);
3466   *sinkp = element;
3467   GST_SOURCE_GROUP_UNLOCK (group);
3468
3469   /* tell decodebin to expose the pad because we are going to use this
3470    * sink */
3471   GST_DEBUG_OBJECT (playbin, "we found a working sink, expose pad");
3472
3473   return GST_AUTOPLUG_SELECT_EXPOSE;
3474 }
3475
3476 static void
3477 notify_source_cb (GstElement * uridecodebin, GParamSpec * pspec,
3478     GstSourceGroup * group)
3479 {
3480   GstPlayBin *playbin;
3481   GstElement *source;
3482
3483   playbin = group->playbin;
3484
3485   g_object_get (group->uridecodebin, "source", &source, NULL);
3486
3487   GST_OBJECT_LOCK (playbin);
3488   if (playbin->source)
3489     gst_object_unref (playbin->source);
3490   playbin->source = source;
3491   GST_OBJECT_UNLOCK (playbin);
3492
3493   g_object_notify (G_OBJECT (playbin), "source");
3494
3495   g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_SOURCE_SETUP],
3496       0, playbin->source);
3497 }
3498
3499 /* must be called with the group lock */
3500 static gboolean
3501 group_set_locked_state_unlocked (GstPlayBin * playbin, GstSourceGroup * group,
3502     gboolean locked)
3503 {
3504   GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
3505
3506   if (group->uridecodebin)
3507     gst_element_set_locked_state (group->uridecodebin, locked);
3508   if (group->suburidecodebin)
3509     gst_element_set_locked_state (group->suburidecodebin, locked);
3510
3511   return TRUE;
3512 }
3513
3514 /* must be called with PLAY_BIN_LOCK */
3515 static gboolean
3516 activate_group (GstPlayBin * playbin, GstSourceGroup * group, GstState target)
3517 {
3518   GstElement *uridecodebin;
3519   GstElement *suburidecodebin = NULL;
3520   GstPlayFlags flags;
3521
3522   g_return_val_if_fail (group->valid, FALSE);
3523   g_return_val_if_fail (!group->active, FALSE);
3524
3525   GST_DEBUG_OBJECT (playbin, "activating group %p", group);
3526
3527   GST_SOURCE_GROUP_LOCK (group);
3528
3529   /* First set up the custom sources */
3530   if (playbin->audio_sink)
3531     group->audio_sink = gst_object_ref (playbin->audio_sink);
3532   if (playbin->video_sink)
3533     group->video_sink = gst_object_ref (playbin->video_sink);
3534
3535   g_list_free (group->stream_changed_pending);
3536   group->stream_changed_pending = NULL;
3537   if (!group->stream_changed_pending_lock)
3538     group->stream_changed_pending_lock = g_mutex_new ();
3539
3540   if (group->uridecodebin) {
3541     GST_DEBUG_OBJECT (playbin, "reusing existing uridecodebin");
3542     uridecodebin = group->uridecodebin;
3543     gst_element_set_state (uridecodebin, GST_STATE_READY);
3544     gst_bin_add (GST_BIN_CAST (playbin), gst_object_ref (uridecodebin));
3545   } else {
3546     GST_DEBUG_OBJECT (playbin, "making new uridecodebin");
3547     uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
3548     if (!uridecodebin)
3549       goto no_decodebin;
3550     gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
3551     group->uridecodebin = gst_object_ref (uridecodebin);
3552   }
3553
3554   flags = gst_play_sink_get_flags (playbin->playsink);
3555
3556   g_object_set (uridecodebin,
3557       /* configure connection speed */
3558       "connection-speed", playbin->connection_speed / 1000,
3559       /* configure uri */
3560       "uri", group->uri,
3561       /* configure download buffering */
3562       "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
3563       /* configure buffering of demuxed/parsed data */
3564       "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
3565       /* configure buffering parameters */
3566       "buffer-duration", playbin->buffer_duration,
3567       "buffer-size", playbin->buffer_size,
3568       "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
3569
3570   /* connect pads and other things */
3571   group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",
3572       G_CALLBACK (pad_added_cb), group);
3573   group->pad_removed_id = g_signal_connect (uridecodebin, "pad-removed",
3574       G_CALLBACK (pad_removed_cb), group);
3575   group->no_more_pads_id = g_signal_connect (uridecodebin, "no-more-pads",
3576       G_CALLBACK (no_more_pads_cb), group);
3577   group->notify_source_id = g_signal_connect (uridecodebin, "notify::source",
3578       G_CALLBACK (notify_source_cb), group);
3579
3580   /* we have 1 pending no-more-pads */
3581   group->pending = 1;
3582
3583   /* is called when the uridecodebin is out of data and we can switch to the
3584    * next uri */
3585   group->drained_id =
3586       g_signal_connect (uridecodebin, "drained", G_CALLBACK (drained_cb),
3587       group);
3588
3589   /* will be called when a new media type is found. We return a list of decoders
3590    * including sinks for decodebin to try */
3591   group->autoplug_factories_id =
3592       g_signal_connect (uridecodebin, "autoplug-factories",
3593       G_CALLBACK (autoplug_factories_cb), group);
3594   group->autoplug_select_id =
3595       g_signal_connect (uridecodebin, "autoplug-select",
3596       G_CALLBACK (autoplug_select_cb), group);
3597   group->autoplug_continue_id =
3598       g_signal_connect (uridecodebin, "autoplug-continue",
3599       G_CALLBACK (autoplug_continue_cb), group);
3600
3601   if (group->suburi) {
3602     /* subtitles */
3603     if (group->suburidecodebin) {
3604       GST_DEBUG_OBJECT (playbin, "reusing existing suburidecodebin");
3605       suburidecodebin = group->suburidecodebin;
3606       gst_element_set_state (suburidecodebin, GST_STATE_READY);
3607       gst_bin_add (GST_BIN_CAST (playbin), gst_object_ref (suburidecodebin));
3608     } else {
3609       GST_DEBUG_OBJECT (playbin, "making new suburidecodebin");
3610       suburidecodebin = gst_element_factory_make ("uridecodebin", NULL);
3611       if (!suburidecodebin)
3612         goto no_decodebin;
3613
3614       gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
3615       group->suburidecodebin = gst_object_ref (suburidecodebin);
3616     }
3617
3618     g_object_set (suburidecodebin,
3619         /* configure connection speed */
3620         "connection-speed", playbin->connection_speed,
3621         /* configure uri */
3622         "uri", group->suburi, NULL);
3623
3624     /* connect pads and other things */
3625     group->sub_pad_added_id = g_signal_connect (suburidecodebin, "pad-added",
3626         G_CALLBACK (pad_added_cb), group);
3627     group->sub_pad_removed_id = g_signal_connect (suburidecodebin,
3628         "pad-removed", G_CALLBACK (pad_removed_cb), group);
3629     group->sub_no_more_pads_id = g_signal_connect (suburidecodebin,
3630         "no-more-pads", G_CALLBACK (no_more_pads_cb), group);
3631
3632     group->sub_autoplug_continue_id =
3633         g_signal_connect (suburidecodebin, "autoplug-continue",
3634         G_CALLBACK (autoplug_continue_cb), group);
3635
3636     /* we have 2 pending no-more-pads */
3637     group->pending = 2;
3638     group->sub_pending = TRUE;
3639   } else {
3640     group->sub_pending = FALSE;
3641   }
3642
3643   /* release the group lock before setting the state of the decodebins, they
3644    * might fire signals in this thread that we need to handle with the
3645    * group_lock taken. */
3646   GST_SOURCE_GROUP_UNLOCK (group);
3647
3648   if (suburidecodebin) {
3649     if (gst_element_set_state (suburidecodebin,
3650             target) == GST_STATE_CHANGE_FAILURE) {
3651       GST_DEBUG_OBJECT (playbin,
3652           "failed state change of subtitle uridecodebin");
3653       GST_SOURCE_GROUP_LOCK (group);
3654
3655       REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
3656       REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
3657       REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
3658       REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
3659       /* Might already be removed because of an error message */
3660       if (GST_OBJECT_PARENT (suburidecodebin) == GST_OBJECT_CAST (playbin))
3661         gst_bin_remove (GST_BIN_CAST (playbin), suburidecodebin);
3662       if (group->sub_pending) {
3663         group->pending--;
3664         group->sub_pending = FALSE;
3665       }
3666       gst_element_set_state (suburidecodebin, GST_STATE_READY);
3667       GST_SOURCE_GROUP_UNLOCK (group);
3668     }
3669   }
3670   if (gst_element_set_state (uridecodebin, target) == GST_STATE_CHANGE_FAILURE)
3671     goto uridecodebin_failure;
3672
3673   GST_SOURCE_GROUP_LOCK (group);
3674   /* alow state changes of the playbin2 affect the group elements now */
3675   group_set_locked_state_unlocked (playbin, group, FALSE);
3676   group->active = TRUE;
3677   GST_SOURCE_GROUP_UNLOCK (group);
3678
3679   return TRUE;
3680
3681   /* ERRORS */
3682 no_decodebin:
3683   {
3684     GstMessage *msg;
3685
3686     /* delete any custom sinks we might have */
3687     if (group->audio_sink) {
3688       /* If this is a automatically created sink set it to NULL */
3689       if (group->audio_sink != playbin->audio_sink)
3690         gst_element_set_state (group->audio_sink, GST_STATE_NULL);
3691       gst_object_unref (group->audio_sink);
3692     }
3693     group->audio_sink = NULL;
3694     if (group->video_sink) {
3695       /* If this is a automatically created sink set it to NULL */
3696       if (group->video_sink != playbin->video_sink)
3697         gst_element_set_state (group->video_sink, GST_STATE_NULL);
3698       gst_object_unref (group->video_sink);
3699     }
3700     group->video_sink = NULL;
3701
3702     GST_SOURCE_GROUP_UNLOCK (group);
3703     msg =
3704         gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
3705         "uridecodebin");
3706     gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
3707
3708     GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
3709         (_("Could not create \"uridecodebin\" element.")), (NULL));
3710     return FALSE;
3711   }
3712 uridecodebin_failure:
3713   {
3714     /* delete any custom sinks we might have */
3715     if (group->audio_sink) {
3716       /* If this is a automatically created sink set it to NULL */
3717       if (group->audio_sink != playbin->audio_sink)
3718         gst_element_set_state (group->audio_sink, GST_STATE_NULL);
3719       gst_object_unref (group->audio_sink);
3720     }
3721     group->audio_sink = NULL;
3722     if (group->video_sink) {
3723       /* If this is a automatically created sink set it to NULL */
3724       if (group->video_sink != playbin->video_sink)
3725         gst_element_set_state (group->video_sink, GST_STATE_NULL);
3726       gst_object_unref (group->video_sink);
3727     }
3728     group->video_sink = NULL;
3729
3730     GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
3731     return FALSE;
3732   }
3733 }
3734
3735 /* unlink a group of uridecodebins from the sink.
3736  * must be called with PLAY_BIN_LOCK */
3737 static gboolean
3738 deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
3739 {
3740   gint i;
3741
3742   g_return_val_if_fail (group->valid, FALSE);
3743   g_return_val_if_fail (group->active, FALSE);
3744
3745   GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
3746
3747   GST_SOURCE_GROUP_LOCK (group);
3748   group->active = FALSE;
3749   for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3750     GstSourceSelect *select = &group->selector[i];
3751
3752     GST_DEBUG_OBJECT (playbin, "unlinking selector %s", select->media_list[0]);
3753
3754     if (select->srcpad) {
3755       if (select->sinkpad) {
3756         GST_LOG_OBJECT (playbin, "unlinking from sink");
3757         gst_pad_unlink (select->srcpad, select->sinkpad);
3758
3759         /* release back */
3760         GST_LOG_OBJECT (playbin, "release sink pad");
3761         gst_play_sink_release_pad (playbin->playsink, select->sinkpad);
3762         select->sinkpad = NULL;
3763       }
3764
3765       gst_object_unref (select->srcpad);
3766       select->srcpad = NULL;
3767     }
3768
3769     if (select->selector) {
3770       gint n;
3771
3772       /* release and unref requests pad from the selector */
3773       for (n = 0; n < select->channels->len; n++) {
3774         GstPad *sinkpad = g_ptr_array_index (select->channels, n);
3775
3776         gst_element_release_request_pad (select->selector, sinkpad);
3777         gst_object_unref (sinkpad);
3778       }
3779       g_ptr_array_set_size (select->channels, 0);
3780
3781       gst_element_set_state (select->selector, GST_STATE_NULL);
3782       gst_bin_remove (GST_BIN_CAST (playbin), select->selector);
3783       select->selector = NULL;
3784     }
3785   }
3786   /* delete any custom sinks we might have */
3787   if (group->audio_sink) {
3788     /* If this is a automatically created sink set it to NULL */
3789     if (group->audio_sink != playbin->audio_sink)
3790       gst_element_set_state (group->audio_sink, GST_STATE_NULL);
3791     gst_object_unref (group->audio_sink);
3792   }
3793   group->audio_sink = NULL;
3794   if (group->video_sink) {
3795     /* If this is a automatically created sink set it to NULL */
3796     if (group->video_sink != playbin->video_sink)
3797       gst_element_set_state (group->video_sink, GST_STATE_NULL);
3798     gst_object_unref (group->video_sink);
3799   }
3800   group->video_sink = NULL;
3801
3802   if (group->uridecodebin) {
3803     REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
3804     REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
3805     REMOVE_SIGNAL (group->uridecodebin, group->no_more_pads_id);
3806     REMOVE_SIGNAL (group->uridecodebin, group->notify_source_id);
3807     REMOVE_SIGNAL (group->uridecodebin, group->drained_id);
3808     REMOVE_SIGNAL (group->uridecodebin, group->autoplug_factories_id);
3809     REMOVE_SIGNAL (group->uridecodebin, group->autoplug_select_id);
3810     REMOVE_SIGNAL (group->uridecodebin, group->autoplug_continue_id);
3811     gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
3812   }
3813
3814   if (group->suburidecodebin) {
3815     REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
3816     REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
3817     REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
3818     REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
3819
3820     /* Might already be removed because of errors */
3821     if (GST_OBJECT_PARENT (group->suburidecodebin) == GST_OBJECT_CAST (playbin))
3822       gst_bin_remove (GST_BIN_CAST (playbin), group->suburidecodebin);
3823   }
3824
3825   GST_SOURCE_GROUP_UNLOCK (group);
3826
3827   return TRUE;
3828 }
3829
3830 /* setup the next group to play, this assumes the next_group is valid and
3831  * configured. It swaps out the current_group and activates the valid
3832  * next_group. */
3833 static gboolean
3834 setup_next_source (GstPlayBin * playbin, GstState target)
3835 {
3836   GstSourceGroup *new_group, *old_group;
3837
3838   GST_DEBUG_OBJECT (playbin, "setup sources");
3839
3840   /* see if there is a next group */
3841   GST_PLAY_BIN_LOCK (playbin);
3842   new_group = playbin->next_group;
3843   if (!new_group || !new_group->valid)
3844     goto no_next_group;
3845
3846   /* first unlink the current source, if any */
3847   old_group = playbin->curr_group;
3848   if (old_group && old_group->valid && old_group->active) {
3849     gst_play_bin_update_cached_duration (playbin);
3850     /* unlink our pads with the sink */
3851     deactivate_group (playbin, old_group);
3852     old_group->valid = FALSE;
3853   }
3854
3855   /* swap old and new */
3856   playbin->curr_group = new_group;
3857   playbin->next_group = old_group;
3858
3859   /* activate the new group */
3860   if (!activate_group (playbin, new_group, target))
3861     goto activate_failed;
3862
3863   GST_PLAY_BIN_UNLOCK (playbin);
3864
3865   return TRUE;
3866
3867   /* ERRORS */
3868 no_next_group:
3869   {
3870     GST_DEBUG_OBJECT (playbin, "no next group");
3871     if (target == GST_STATE_READY && new_group && new_group->uri == NULL)
3872       GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL));
3873     GST_PLAY_BIN_UNLOCK (playbin);
3874     return FALSE;
3875   }
3876 activate_failed:
3877   {
3878     GST_DEBUG_OBJECT (playbin, "activate failed");
3879     GST_PLAY_BIN_UNLOCK (playbin);
3880     return FALSE;
3881   }
3882 }
3883
3884 /* The group that is currently playing is copied again to the
3885  * next_group so that it will start playing the next time.
3886  */
3887 static gboolean
3888 save_current_group (GstPlayBin * playbin)
3889 {
3890   GstSourceGroup *curr_group;
3891
3892   GST_DEBUG_OBJECT (playbin, "save current group");
3893
3894   /* see if there is a current group */
3895   GST_PLAY_BIN_LOCK (playbin);
3896   curr_group = playbin->curr_group;
3897   if (curr_group && curr_group->valid && curr_group->active) {
3898     /* unlink our pads with the sink */
3899     deactivate_group (playbin, curr_group);
3900   }
3901   /* swap old and new */
3902   playbin->curr_group = playbin->next_group;
3903   playbin->next_group = curr_group;
3904   GST_PLAY_BIN_UNLOCK (playbin);
3905
3906   return TRUE;
3907 }
3908
3909 /* clear the locked state from all groups. This function is called before a
3910  * state change to NULL is performed on them. */
3911 static gboolean
3912 groups_set_locked_state (GstPlayBin * playbin, gboolean locked)
3913 {
3914   GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
3915       locked);
3916
3917   GST_PLAY_BIN_LOCK (playbin);
3918   GST_SOURCE_GROUP_LOCK (playbin->curr_group);
3919   group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
3920   GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
3921   GST_SOURCE_GROUP_LOCK (playbin->next_group);
3922   group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
3923   GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
3924   GST_PLAY_BIN_UNLOCK (playbin);
3925
3926   return TRUE;
3927 }
3928
3929 static GstStateChangeReturn
3930 gst_play_bin_change_state (GstElement * element, GstStateChange transition)
3931 {
3932   GstStateChangeReturn ret;
3933   GstPlayBin *playbin;
3934   gboolean do_save = FALSE;
3935
3936   playbin = GST_PLAY_BIN (element);
3937
3938   switch (transition) {
3939     case GST_STATE_CHANGE_NULL_TO_READY:
3940       memset (&playbin->duration, 0, sizeof (playbin->duration));
3941       break;
3942     case GST_STATE_CHANGE_READY_TO_PAUSED:
3943       GST_LOG_OBJECT (playbin, "clearing shutdown flag");
3944       memset (&playbin->duration, 0, sizeof (playbin->duration));
3945       g_atomic_int_set (&playbin->shutdown, 0);
3946
3947       if (!setup_next_source (playbin, GST_STATE_READY)) {
3948         ret = GST_STATE_CHANGE_FAILURE;
3949         goto failure;
3950       }
3951       break;
3952     case GST_STATE_CHANGE_PAUSED_TO_READY:
3953     async_down:
3954       /* FIXME unlock our waiting groups */
3955       GST_LOG_OBJECT (playbin, "setting shutdown flag");
3956       g_atomic_int_set (&playbin->shutdown, 1);
3957       memset (&playbin->duration, 0, sizeof (playbin->duration));
3958
3959       /* wait for all callbacks to end by taking the lock.
3960        * No dynamic (critical) new callbacks will
3961        * be able to happen as we set the shutdown flag. */
3962       GST_PLAY_BIN_DYN_LOCK (playbin);
3963       GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
3964       GST_PLAY_BIN_DYN_UNLOCK (playbin);
3965       if (!do_save)
3966         break;
3967     case GST_STATE_CHANGE_READY_TO_NULL:
3968       /* we go async to PAUSED, so if that fails, we never make it to PAUSED
3969        * an no state change PAUSED to READY passes here,
3970        * though it is a nice-to-have ... */
3971       if (!g_atomic_int_get (&playbin->shutdown)) {
3972         do_save = TRUE;
3973         goto async_down;
3974       }
3975       memset (&playbin->duration, 0, sizeof (playbin->duration));
3976
3977       /* unlock so that all groups go to NULL */
3978       groups_set_locked_state (playbin, FALSE);
3979       break;
3980     default:
3981       break;
3982   }
3983
3984   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3985   if (ret == GST_STATE_CHANGE_FAILURE)
3986     goto failure;
3987
3988   switch (transition) {
3989     case GST_STATE_CHANGE_READY_TO_PAUSED:
3990       break;
3991     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3992       /* FIXME Release audio device when we implement that */
3993       break;
3994     case GST_STATE_CHANGE_PAUSED_TO_READY:
3995       save_current_group (playbin);
3996       break;
3997     case GST_STATE_CHANGE_READY_TO_NULL:
3998     {
3999       guint i;
4000
4001       /* also do missed state change down to READY */
4002       if (do_save)
4003         save_current_group (playbin);
4004       /* Deactive the groups, set the uridecodebins to NULL
4005        * and unref them.
4006        */
4007       for (i = 0; i < 2; i++) {
4008         if (playbin->groups[i].active && playbin->groups[i].valid) {
4009           deactivate_group (playbin, &playbin->groups[i]);
4010           playbin->groups[i].valid = FALSE;
4011         }
4012
4013         if (playbin->groups[i].uridecodebin) {
4014           gst_element_set_state (playbin->groups[i].uridecodebin,
4015               GST_STATE_NULL);
4016           gst_object_unref (playbin->groups[i].uridecodebin);
4017           playbin->groups[i].uridecodebin = NULL;
4018         }
4019
4020         if (playbin->groups[i].suburidecodebin) {
4021           gst_element_set_state (playbin->groups[i].suburidecodebin,
4022               GST_STATE_NULL);
4023           gst_object_unref (playbin->groups[i].suburidecodebin);
4024           playbin->groups[i].suburidecodebin = NULL;
4025         }
4026       }
4027
4028       /* Set our sinks back to NULL, they might not be child of playbin */
4029       if (playbin->audio_sink)
4030         gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
4031       if (playbin->video_sink)
4032         gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
4033       if (playbin->text_sink)
4034         gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
4035
4036       /* make sure the groups don't perform a state change anymore until we
4037        * enable them again */
4038       groups_set_locked_state (playbin, TRUE);
4039       break;
4040     }
4041     default:
4042       break;
4043   }
4044
4045   return ret;
4046
4047   /* ERRORS */
4048 failure:
4049   {
4050     if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
4051       GstSourceGroup *curr_group;
4052
4053       curr_group = playbin->curr_group;
4054       if (curr_group && curr_group->active && curr_group->valid) {
4055         /* unlink our pads with the sink */
4056         deactivate_group (playbin, curr_group);
4057         curr_group->valid = FALSE;
4058       }
4059
4060       /* Swap current and next group back */
4061       playbin->curr_group = playbin->next_group;
4062       playbin->next_group = curr_group;
4063     }
4064     return ret;
4065   }
4066 }
4067
4068 static void
4069 gst_play_bin_xoverlay_expose (GstXOverlay * overlay)
4070 {
4071   GstPlayBin *playbin = GST_PLAY_BIN (overlay);
4072
4073   gst_x_overlay_expose (GST_X_OVERLAY (playbin->playsink));
4074 }
4075
4076 static void
4077 gst_play_bin_xoverlay_handle_events (GstXOverlay * overlay,
4078     gboolean handle_events)
4079 {
4080   GstPlayBin *playbin = GST_PLAY_BIN (overlay);
4081
4082   gst_x_overlay_handle_events (GST_X_OVERLAY (playbin->playsink),
4083       handle_events);
4084 }
4085
4086 static void
4087 gst_play_bin_xoverlay_set_render_rectangle (GstXOverlay * overlay, gint x,
4088     gint y, gint width, gint height)
4089 {
4090   GstPlayBin *playbin = GST_PLAY_BIN (overlay);
4091
4092   gst_x_overlay_set_render_rectangle (GST_X_OVERLAY (playbin->playsink), x, y,
4093       width, height);
4094 }
4095
4096 static void
4097 gst_play_bin_xoverlay_set_window_handle (GstXOverlay * overlay, guintptr handle)
4098 {
4099   GstPlayBin *playbin = GST_PLAY_BIN (overlay);
4100
4101   gst_x_overlay_set_window_handle (GST_X_OVERLAY (playbin->playsink), handle);
4102 }
4103
4104 static void
4105 gst_play_bin_xoverlay_init (gpointer g_iface, gpointer g_iface_data)
4106 {
4107   GstXOverlayClass *iface = (GstXOverlayClass *) g_iface;
4108   iface->expose = gst_play_bin_xoverlay_expose;
4109   iface->handle_events = gst_play_bin_xoverlay_handle_events;
4110   iface->set_render_rectangle = gst_play_bin_xoverlay_set_render_rectangle;
4111   iface->set_window_handle = gst_play_bin_xoverlay_set_window_handle;
4112 }
4113
4114 static gboolean
4115 gst_play_bin_implements_interface_supported (GstImplementsInterface * iface,
4116     GType type)
4117 {
4118   if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_STREAM_VOLUME ||
4119       type == GST_TYPE_NAVIGATION || type == GST_TYPE_COLOR_BALANCE)
4120     return TRUE;
4121   else
4122     return FALSE;
4123 }
4124
4125 static void
4126 gst_play_bin_implements_interface_init (gpointer g_iface, gpointer g_iface_data)
4127 {
4128   GstImplementsInterfaceClass *iface = (GstImplementsInterfaceClass *) g_iface;
4129   iface->supported = gst_play_bin_implements_interface_supported;
4130 }
4131
4132 static void
4133 gst_play_bin_navigation_send_event (GstNavigation * navigation,
4134     GstStructure * structure)
4135 {
4136   GstPlayBin *playbin = GST_PLAY_BIN (navigation);
4137
4138   gst_navigation_send_event (GST_NAVIGATION (playbin->playsink), structure);
4139 }
4140
4141 static void
4142 gst_play_bin_navigation_init (gpointer g_iface, gpointer g_iface_data)
4143 {
4144   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4145
4146   iface->send_event = gst_play_bin_navigation_send_event;
4147 }
4148
4149 static const GList *
4150 gst_play_bin_colorbalance_list_channels (GstColorBalance * balance)
4151 {
4152   GstPlayBin *playbin = GST_PLAY_BIN (balance);
4153
4154   return
4155       gst_color_balance_list_channels (GST_COLOR_BALANCE (playbin->playsink));
4156 }
4157
4158 static void
4159 gst_play_bin_colorbalance_set_value (GstColorBalance * balance,
4160     GstColorBalanceChannel * channel, gint value)
4161 {
4162   GstPlayBin *playbin = GST_PLAY_BIN (balance);
4163
4164   gst_color_balance_set_value (GST_COLOR_BALANCE (playbin->playsink), channel,
4165       value);
4166 }
4167
4168 static gint
4169 gst_play_bin_colorbalance_get_value (GstColorBalance * balance,
4170     GstColorBalanceChannel * channel)
4171 {
4172   GstPlayBin *playbin = GST_PLAY_BIN (balance);
4173
4174   return gst_color_balance_get_value (GST_COLOR_BALANCE (playbin->playsink),
4175       channel);
4176 }
4177
4178 static GstColorBalanceType
4179 gst_play_bin_colorbalance_get_balance_type (GstColorBalance * balance)
4180 {
4181   GstPlayBin *playbin = GST_PLAY_BIN (balance);
4182
4183   return
4184       gst_color_balance_get_balance_type (GST_COLOR_BALANCE
4185       (playbin->playsink));
4186 }
4187
4188 static void
4189 gst_play_bin_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4190 {
4191   GstColorBalanceClass *iface = (GstColorBalanceClass *) g_iface;
4192
4193   iface->list_channels = gst_play_bin_colorbalance_list_channels;
4194   iface->set_value = gst_play_bin_colorbalance_set_value;
4195   iface->get_value = gst_play_bin_colorbalance_get_value;
4196   iface->get_balance_type = gst_play_bin_colorbalance_get_balance_type;
4197 }
4198
4199 gboolean
4200 gst_play_bin2_plugin_init (GstPlugin * plugin)
4201 {
4202   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin2", 0, "play bin");
4203
4204   return gst_element_register (plugin, "playbin2", GST_RANK_NONE,
4205       GST_TYPE_PLAY_BIN);
4206 }