2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * Copyright (C) <2013> Collabora Ltd.
5 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 * SECTION:element-playbin
26 * Playbin provides a stand-alone everything-in-one abstraction for an
27 * audio and/or video player.
29 * Playbin can handle both audio and video files and features
32 * automatic file type recognition and based on that automatic
33 * selection and usage of the right audio/video/subtitle demuxers/decoders
36 * visualisations for audio files
39 * subtitle support for video files. Subtitles can be store in external
43 * stream selection between different video/audio/subtitles streams
46 * meta info (tag) extraction
49 * easy access to the last video sample
52 * buffering when playing streams over a network
55 * volume control with mute option
60 * <title>Usage</title>
62 * A playbin element can be created just like any other element using
63 * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin:uri
64 * property. This must be an absolute URI, relative file paths are not allowed.
65 * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
67 * Playbin is a #GstPipeline. It will notify the application of everything
68 * that's happening (errors, end of stream, tags found, state changes, etc.)
69 * by posting messages on its #GstBus. The application needs to watch the
72 * Playback can be initiated by setting the element to PLAYING state using
73 * gst_element_set_state(). Note that the state change will take place in
74 * the background in a separate thread, when the function returns playback
75 * is probably not happening yet and any errors might not have occured yet.
76 * Applications using playbin should ideally be written to deal with things
77 * completely asynchroneous.
79 * When playback has finished (an EOS message has been received on the bus)
80 * or an error has occured (an ERROR message has been received on the bus) or
81 * the user wants to play a different track, playbin should be set back to
82 * READY or NULL state, then the #GstPlayBin:uri property should be set to the
83 * new location and then playbin be set to PLAYING state again.
85 * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
86 * on the playbin element. Again, the seek will not be executed
87 * instantaneously, but will be done in a background thread. When the seek
88 * call returns the seek will most likely still be in process. An application
89 * may wait for the seek to finish (or fail) using gst_element_get_state() with
90 * -1 as the timeout, but this will block the user interface and is not
93 * Applications may query the current position and duration of the stream
94 * via gst_element_query_position() and gst_element_query_duration() and
95 * setting the format passed to GST_FORMAT_TIME. If the query was successful,
96 * the duration or position will have been returned in units of nanoseconds.
100 * <title>Advanced Usage: specifying the audio and video sink</title>
102 * By default, if no audio sink or video sink has been specified via the
103 * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property, playbin will use the autoaudiosink
104 * and autovideosink elements to find the first-best available output method.
105 * This should work in most cases, but is not always desirable. Often either
106 * the user or application might want to specify more explicitly what to use
107 * for audio and video output.
109 * If the application wants more control over how audio or video should be
110 * output, it may create the audio/video sink elements itself (for example
111 * using gst_element_factory_make()) and provide them to playbin using the
112 * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property.
114 * GNOME-based applications, for example, will usually want to create
115 * gconfaudiosink and gconfvideosink elements and make playbin use those,
116 * so that output happens to whatever the user has configured in the GNOME
117 * Multimedia System Selector configuration dialog.
119 * The sink elements do not necessarily need to be ready-made sinks. It is
120 * possible to create container elements that look like a sink to playbin,
121 * but in reality contain a number of custom elements linked together. This
122 * can be achieved by creating a #GstBin and putting elements in there and
123 * linking them, and then creating a sink #GstGhostPad for the bin and pointing
124 * it to the sink pad of the first element within the bin. This can be used
125 * for a number of purposes, for example to force output to a particular
126 * format or to modify or observe the data before it is output.
128 * It is also possible to 'suppress' audio and/or video output by using
129 * 'fakesink' elements (or capture it from there using the fakesink element's
130 * "handoff" signal, which, nota bene, is fired from the streaming thread!).
134 * <title>Retrieving Tags and Other Meta Data</title>
136 * Most of the common meta data (artist, title, etc.) can be retrieved by
137 * watching for TAG messages on the pipeline's bus (see above).
139 * Other more specific meta information like width/height/framerate of video
140 * streams or samplerate/number of channels of audio streams can be obtained
141 * from the negotiated caps on the sink pads of the sinks.
145 * <title>Buffering</title>
146 * Playbin handles buffering automatically for the most part, but applications
147 * need to handle parts of the buffering process as well. Whenever playbin is
148 * buffering, it will post BUFFERING messages on the bus with a percentage
149 * value that shows the progress of the buffering process. Applications need
150 * to set playbin to PLAYING or PAUSED state in response to these messages.
151 * They may also want to convey the buffering progress to the user in some
152 * way. Here is how to extract the percentage information from the message:
154 * switch (GST_MESSAGE_TYPE (msg)) {
155 * case GST_MESSAGE_BUFFERING: {
157 * gst_message_parse_buffering (msg, &percent);
158 * g_print ("Buffering (%%u percent done)", percent);
164 * Note that applications should keep/set the pipeline in the PAUSED state when
165 * a BUFFERING message is received with a buffer percent value < 100 and set
166 * the pipeline back to PLAYING state when a BUFFERING message with a value
167 * of 100 percent is received (if PLAYING is the desired state, that is).
170 * <title>Embedding the video window in your application</title>
171 * By default, playbin (or rather the video sinks used) will create their own
172 * window. Applications will usually want to force output to a window of their
173 * own, however. This can be done using the #GstVideoOverlay interface, which most
174 * video sinks implement. See the documentation there for more details.
177 * <title>Specifying which CD/DVD device to use</title>
178 * The device to use for CDs/DVDs needs to be set on the source element
179 * playbin creates before it is opened. The most generic way of doing this
180 * is to connect to playbin's "source-setup" (or "notify::source") signal,
181 * which will be emitted by playbin when it has created the source element
182 * for a particular URI. In the signal callback you can check if the source
183 * element has a "device" property and set it appropriately. In some cases
184 * the device can also be set as part of the URI, but it depends on the
185 * elements involved if this will work or not. For example, for DVD menu
186 * playback, the following syntax might work (if the resindvd plugin is used):
187 * dvd://[/path/to/device]
190 * <title>Handling redirects</title>
192 * Some elements may post 'redirect' messages on the bus to tell the
193 * application to open another location. These are element messages containing
194 * a structure named 'redirect' along with a 'new-location' field of string
195 * type. The new location may be a relative or an absolute URI. Examples
196 * for such redirects can be found in many quicktime movie trailers.
200 * <title>Examples</title>
202 * gst-launch -v playbin uri=file:///path/to/somefile.avi
203 * ]| This will play back the given AVI video file, given that the video and
204 * audio decoders required to decode the content are installed. Since no
205 * special audio sink or video sink is supplied (not possible via gst-launch),
206 * playbin will try to find a suitable audio and video sink automatically
207 * using the autoaudiosink and autovideosink elements.
209 * gst-launch -v playbin uri=cdda://4
210 * ]| This will play back track 4 on an audio CD in your disc drive (assuming
211 * the drive is detected automatically by the plugin).
213 * gst-launch -v playbin uri=dvd://
214 * ]| This will play back the DVD in your disc drive (assuming
215 * the drive is detected automatically by the plugin).
219 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
220 * with newer GLib versions (>= 2.31.0) */
221 #define GLIB_DISABLE_DEPRECATION_WARNINGS
230 #include <gst/gst-i18n-plugin.h>
231 #include <gst/pbutils/pbutils.h>
232 #include <gst/audio/streamvolume.h>
233 #include <gst/video/videooverlay.h>
234 #include <gst/video/navigation.h>
235 #include <gst/video/colorbalance.h>
236 #include "gstplay-enum.h"
237 #include "gstplayback.h"
238 #include "gstplaysink.h"
239 #include "gstsubtitleoverlay.h"
241 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
242 #define GST_CAT_DEFAULT gst_play_bin_debug
244 #define GST_TYPE_PLAY_BIN (gst_play_bin_get_type())
245 #define GST_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
246 #define GST_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
247 #define GST_IS_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
248 #define GST_IS_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
250 #define VOLUME_MAX_DOUBLE 10.0
252 typedef struct _GstPlayBin GstPlayBin;
253 typedef struct _GstPlayBinClass GstPlayBinClass;
254 typedef struct _GstSourceGroup GstSourceGroup;
255 typedef struct _GstSourceCombine GstSourceCombine;
257 typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void);
259 /* has the info for a combiner and provides the link to the sink */
260 struct _GstSourceCombine
262 const gchar *media_list[8]; /* the media types for the combiner */
263 SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */
264 GstPlaySinkType type; /* the sink pad type of the combiner */
266 GstElement *combiner; /* the combiner */
268 GstPad *srcpad; /* the source pad of the combiner */
269 GstPad *sinkpad; /* the sinkpad of the sink when the combiner
274 gboolean has_active_pad; /* stream combiner has the "active-pad" property */
276 gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */
277 gboolean has_tags; /* stream combiner's sink pads have the "tags" property */
280 #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
281 #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
282 #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
286 PLAYBIN_STREAM_AUDIO = 0,
287 PLAYBIN_STREAM_VIDEO,
292 static void avelements_free (gpointer data);
293 static GSequence *avelements_create (GstPlayBin * playbin,
294 gboolean isaudioelement);
296 /* The GstAudioVideoElement structure holding the audio/video decoder
297 * and the audio/video sink factories together with field indicating
298 * the number of common caps features */
301 GstElementFactory *dec; /* audio:video decoder */
302 GstElementFactory *sink; /* audio:video sink */
303 guint n_comm_cf; /* number of common caps features */
306 /* a structure to hold the objects for decoding a uri and the subtitle uri
308 struct _GstSourceGroup
314 gboolean valid; /* the group has valid info to start playback */
315 gboolean active; /* the group is active */
320 GValueArray *streaminfo;
323 GPtrArray *video_channels; /* links to combiner pads */
324 GPtrArray *audio_channels; /* links to combiner pads */
325 GPtrArray *text_channels; /* links to combiner pads */
327 /* Sinks for this group. These are initialized with
328 * the configure or currently used sink, otherwise
329 * left as NULL and playbin tries to automatically
332 GstElement *audio_sink;
333 GstElement *video_sink;
334 GstElement *text_sink;
336 /* uridecodebins for uri and subtitle uri */
337 GstElement *uridecodebin;
338 GstElement *suburidecodebin;
340 gboolean sub_pending;
342 gboolean have_group_id;
346 gulong pad_removed_id;
347 gulong no_more_pads_id;
348 gulong notify_source_id;
350 gulong autoplug_factories_id;
351 gulong autoplug_select_id;
352 gulong autoplug_continue_id;
353 gulong autoplug_query_id;
355 gulong sub_pad_added_id;
356 gulong sub_pad_removed_id;
357 gulong sub_no_more_pads_id;
358 gulong sub_autoplug_continue_id;
359 gulong sub_autoplug_query_id;
363 GMutex stream_changed_pending_lock;
364 gboolean stream_changed_pending;
366 /* to prevent that suburidecodebin seek flushes disrupt playback */
367 GMutex suburi_flushes_to_drop_lock;
368 GSList *suburi_flushes_to_drop;
370 /* combiners for different streams */
371 GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
374 #define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
375 #define GST_PLAY_BIN_LOCK(bin) (g_rec_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin)))
376 #define GST_PLAY_BIN_UNLOCK(bin) (g_rec_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin)))
378 /* lock to protect dynamic callbacks, like no-more-pads */
379 #define GST_PLAY_BIN_DYN_LOCK(bin) g_mutex_lock (&(bin)->dyn_lock)
380 #define GST_PLAY_BIN_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock)
382 /* lock for shutdown */
383 #define GST_PLAY_BIN_SHUTDOWN_LOCK(bin,label) \
385 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \
387 GST_PLAY_BIN_DYN_LOCK (bin); \
388 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
389 GST_PLAY_BIN_DYN_UNLOCK (bin); \
394 /* unlock for shutdown */
395 #define GST_PLAY_BIN_SHUTDOWN_UNLOCK(bin) \
396 GST_PLAY_BIN_DYN_UNLOCK (bin); \
401 * playbin element structure
407 GRecMutex lock; /* to protect group switching */
409 /* the groups, we use a double buffer to switch between current and next */
410 GstSourceGroup groups[2]; /* array with group info */
411 GstSourceGroup *curr_group; /* pointer to the currently playing group */
412 GstSourceGroup *next_group; /* pointer to the next group */
415 guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */
416 gint current_video; /* the currently selected stream */
417 gint current_audio; /* the currently selected stream */
418 gint current_text; /* the currently selected stream */
420 guint64 buffer_duration; /* When buffering, the max buffer duration (ns) */
421 guint buffer_size; /* When buffering, the max buffer size (bytes) */
422 gboolean force_aspect_ratio;
425 GstPlaySink *playsink;
427 /* the last activated source */
430 /* lock protecting dynamic adding/removing */
432 /* if we are shutting down or not */
435 GMutex elements_lock;
436 guint32 elements_cookie;
437 GList *elements; /* factories we can use for selecting elements */
439 gboolean have_selector; /* set to FALSE when we fail to create an
440 * input-selector, so that we only post a
443 gboolean video_pending_flush_finish; /* whether we are pending to send a custom
444 * custom-video-flush-finish event
445 * on pad activation */
446 gboolean audio_pending_flush_finish; /* whether we are pending to send a custom
447 * custom-audio-flush-finish event
448 * on pad activation */
449 gboolean text_pending_flush_finish; /* whether we are pending to send a custom
450 * custom-subtitle-flush-finish event
451 * on pad activation */
453 GstElement *audio_sink; /* configured audio sink, or NULL */
454 GstElement *video_sink; /* configured video sink, or NULL */
455 GstElement *text_sink; /* configured text sink, or NULL */
457 GstElement *audio_stream_combiner; /* configured audio stream combiner, or NULL */
458 GstElement *video_stream_combiner; /* configured video stream combiner, or NULL */
459 GstElement *text_stream_combiner; /* configured text stream combiner, or NULL */
461 GSequence *aelements; /* a list of GstAVElements for audio stream */
462 GSequence *velements; /* a list of GstAVElements for video stream */
469 } duration[5]; /* cached durations */
471 guint64 ring_buffer_max_size; /* 0 means disabled */
476 struct _GstPlayBinClass
478 GstPipelineClass parent_class;
480 /* notify app that the current uri finished decoding and it is possible to
481 * queue a new one for gapless playback */
482 void (*about_to_finish) (GstPlayBin * playbin);
484 /* notify app that number of audio/video/text streams changed */
485 void (*video_changed) (GstPlayBin * playbin);
486 void (*audio_changed) (GstPlayBin * playbin);
487 void (*text_changed) (GstPlayBin * playbin);
489 /* notify app that the tags of audio/video/text streams changed */
490 void (*video_tags_changed) (GstPlayBin * playbin, gint stream);
491 void (*audio_tags_changed) (GstPlayBin * playbin, gint stream);
492 void (*text_tags_changed) (GstPlayBin * playbin, gint stream);
494 /* get audio/video/text tags for a stream */
495 GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
496 GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
497 GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
499 /* get the last video sample and convert it to the given caps */
500 GstSample *(*convert_sample) (GstPlayBin * playbin, GstCaps * caps);
502 /* get audio/video/text pad for a stream */
503 GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
504 GstPad *(*get_audio_pad) (GstPlayBin * playbin, gint stream);
505 GstPad *(*get_text_pad) (GstPlayBin * playbin, gint stream);
509 #define DEFAULT_URI NULL
510 #define DEFAULT_SUBURI NULL
511 #define DEFAULT_SOURCE NULL
512 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
513 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
514 GST_PLAY_FLAG_SOFT_COLORBALANCE
515 #define DEFAULT_N_VIDEO 0
516 #define DEFAULT_CURRENT_VIDEO -1
517 #define DEFAULT_N_AUDIO 0
518 #define DEFAULT_CURRENT_AUDIO -1
519 #define DEFAULT_N_TEXT 0
520 #define DEFAULT_CURRENT_TEXT -1
521 #define DEFAULT_SUBTITLE_ENCODING NULL
522 #define DEFAULT_AUDIO_SINK NULL
523 #define DEFAULT_VIDEO_SINK NULL
524 #define DEFAULT_VIS_PLUGIN NULL
525 #define DEFAULT_TEXT_SINK NULL
526 #define DEFAULT_VOLUME 1.0
527 #define DEFAULT_MUTE FALSE
528 #define DEFAULT_FRAME NULL
529 #define DEFAULT_FONT_DESC NULL
530 #define DEFAULT_CONNECTION_SPEED 0
531 #define DEFAULT_BUFFER_DURATION -1
532 #define DEFAULT_BUFFER_SIZE -1
533 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
550 PROP_SUBTITLE_ENCODING,
555 PROP_VIDEO_STREAM_COMBINER,
556 PROP_AUDIO_STREAM_COMBINER,
557 PROP_TEXT_STREAM_COMBINER,
562 PROP_CONNECTION_SPEED,
564 PROP_BUFFER_DURATION,
566 PROP_RING_BUFFER_MAX_SIZE,
567 PROP_FORCE_ASPECT_RATIO,
574 SIGNAL_ABOUT_TO_FINISH,
575 SIGNAL_CONVERT_SAMPLE,
576 SIGNAL_VIDEO_CHANGED,
577 SIGNAL_AUDIO_CHANGED,
579 SIGNAL_VIDEO_TAGS_CHANGED,
580 SIGNAL_AUDIO_TAGS_CHANGED,
581 SIGNAL_TEXT_TAGS_CHANGED,
582 SIGNAL_GET_VIDEO_TAGS,
583 SIGNAL_GET_AUDIO_TAGS,
584 SIGNAL_GET_TEXT_TAGS,
585 SIGNAL_GET_VIDEO_PAD,
586 SIGNAL_GET_AUDIO_PAD,
592 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw");
593 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw");
595 static void gst_play_bin_class_init (GstPlayBinClass * klass);
596 static void gst_play_bin_init (GstPlayBin * playbin);
597 static void gst_play_bin_finalize (GObject * object);
599 static void gst_play_bin_set_property (GObject * object, guint prop_id,
600 const GValue * value, GParamSpec * spec);
601 static void gst_play_bin_get_property (GObject * object, guint prop_id,
602 GValue * value, GParamSpec * spec);
604 static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
605 GstStateChange transition);
607 static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
608 static gboolean gst_play_bin_query (GstElement * element, GstQuery * query);
609 static void gst_play_bin_set_context (GstElement * element,
610 GstContext * context);
612 static GstTagList *gst_play_bin_get_video_tags (GstPlayBin * playbin,
614 static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
616 static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
619 static GstSample *gst_play_bin_convert_sample (GstPlayBin * playbin,
622 static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
623 static GstPad *gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream);
624 static GstPad *gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream);
626 static gboolean setup_next_source (GstPlayBin * playbin, GstState target);
628 static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
629 static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
630 GstSourceGroup * group);
632 static void gst_play_bin_suburidecodebin_block (GstSourceGroup * group,
633 GstElement * suburidecodebin, gboolean block);
634 static void gst_play_bin_suburidecodebin_seek_to_start (GstSourceGroup * group);
636 static GstElementClass *parent_class;
638 static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
640 #define REMOVE_SIGNAL(obj,id) \
642 g_signal_handler_disconnect (obj, id); \
646 static void gst_play_bin_overlay_init (gpointer g_iface, gpointer g_iface_data);
647 static void gst_play_bin_navigation_init (gpointer g_iface,
648 gpointer g_iface_data);
649 static void gst_play_bin_colorbalance_init (gpointer g_iface,
650 gpointer g_iface_data);
653 gst_play_bin_get_type (void)
655 static GType gst_play_bin_type = 0;
657 if (!gst_play_bin_type) {
658 static const GTypeInfo gst_play_bin_info = {
659 sizeof (GstPlayBinClass),
662 (GClassInitFunc) gst_play_bin_class_init,
667 (GInstanceInitFunc) gst_play_bin_init,
670 static const GInterfaceInfo svol_info = {
673 static const GInterfaceInfo ov_info = {
674 gst_play_bin_overlay_init,
677 static const GInterfaceInfo nav_info = {
678 gst_play_bin_navigation_init,
681 static const GInterfaceInfo col_info = {
682 gst_play_bin_colorbalance_init,
686 gst_play_bin_type = g_type_register_static (GST_TYPE_PIPELINE,
687 "GstPlayBin", &gst_play_bin_info, 0);
689 g_type_add_interface_static (gst_play_bin_type, GST_TYPE_STREAM_VOLUME,
691 g_type_add_interface_static (gst_play_bin_type, GST_TYPE_VIDEO_OVERLAY,
693 g_type_add_interface_static (gst_play_bin_type, GST_TYPE_NAVIGATION,
695 g_type_add_interface_static (gst_play_bin_type, GST_TYPE_COLOR_BALANCE,
699 return gst_play_bin_type;
703 gst_play_bin_class_init (GstPlayBinClass * klass)
705 GObjectClass *gobject_klass;
706 GstElementClass *gstelement_klass;
707 GstBinClass *gstbin_klass;
709 gobject_klass = (GObjectClass *) klass;
710 gstelement_klass = (GstElementClass *) klass;
711 gstbin_klass = (GstBinClass *) klass;
713 parent_class = g_type_class_peek_parent (klass);
715 gobject_klass->set_property = gst_play_bin_set_property;
716 gobject_klass->get_property = gst_play_bin_get_property;
718 gobject_klass->finalize = gst_play_bin_finalize;
723 * Set the next URI that playbin will play. This property can be set from the
724 * about-to-finish signal to queue the next media file.
726 g_object_class_install_property (gobject_klass, PROP_URI,
727 g_param_spec_string ("uri", "URI", "URI of the media to play",
728 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
731 * GstPlayBin:current-uri
733 * The currently playing uri.
735 g_object_class_install_property (gobject_klass, PROP_CURRENT_URI,
736 g_param_spec_string ("current-uri", "Current URI",
737 "The currently playing URI", NULL,
738 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
743 * Set the next subtitle URI that playbin will play. This property can be
744 * set from the about-to-finish signal to queue the next subtitle media file.
746 g_object_class_install_property (gobject_klass, PROP_SUBURI,
747 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
748 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
751 * GstPlayBin:current-suburi
753 * The currently playing subtitle uri.
755 g_object_class_install_property (gobject_klass, PROP_CURRENT_SUBURI,
756 g_param_spec_string ("current-suburi", "Current .sub-URI",
757 "The currently playing URI of a subtitle",
758 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
760 g_object_class_install_property (gobject_klass, PROP_SOURCE,
761 g_param_spec_object ("source", "Source", "Source element",
762 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
767 * Control the behaviour of playbin.
769 g_object_class_install_property (gobject_klass, PROP_FLAGS,
770 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
771 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
772 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
777 * Get the total number of available video streams.
779 g_object_class_install_property (gobject_klass, PROP_N_VIDEO,
780 g_param_spec_int ("n-video", "Number Video",
781 "Total number of video streams", 0, G_MAXINT, 0,
782 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
784 * GstPlayBin:current-video
786 * Get or set the currently playing video stream. By default the first video
787 * stream with data is played.
789 g_object_class_install_property (gobject_klass, PROP_CURRENT_VIDEO,
790 g_param_spec_int ("current-video", "Current Video",
791 "Currently playing video stream (-1 = auto)",
792 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
796 * Get the total number of available audio streams.
798 g_object_class_install_property (gobject_klass, PROP_N_AUDIO,
799 g_param_spec_int ("n-audio", "Number Audio",
800 "Total number of audio streams", 0, G_MAXINT, 0,
801 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
803 * GstPlayBin:current-audio
805 * Get or set the currently playing audio stream. By default the first audio
806 * stream with data is played.
808 g_object_class_install_property (gobject_klass, PROP_CURRENT_AUDIO,
809 g_param_spec_int ("current-audio", "Current audio",
810 "Currently playing audio stream (-1 = auto)",
811 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
815 * Get the total number of available subtitle streams.
817 g_object_class_install_property (gobject_klass, PROP_N_TEXT,
818 g_param_spec_int ("n-text", "Number Text",
819 "Total number of text streams", 0, G_MAXINT, 0,
820 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
822 * GstPlayBin:current-text:
824 * Get or set the currently playing subtitle stream. By default the first
825 * subtitle stream with data is played.
827 g_object_class_install_property (gobject_klass, PROP_CURRENT_TEXT,
828 g_param_spec_int ("current-text", "Current Text",
829 "Currently playing text stream (-1 = auto)",
830 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
832 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
833 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
834 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
835 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
836 "be checked for an encoding to use. If that is not set either, "
837 "ISO-8859-15 will be assumed.", NULL,
838 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
840 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
841 g_param_spec_object ("video-sink", "Video Sink",
842 "the video output element to use (NULL = default sink)",
843 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
844 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
845 g_param_spec_object ("audio-sink", "Audio Sink",
846 "the audio output element to use (NULL = default sink)",
847 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
848 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
849 g_param_spec_object ("vis-plugin", "Vis plugin",
850 "the visualization element to use (NULL = default)",
851 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
852 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
853 g_param_spec_object ("text-sink", "Text plugin",
854 "the text output element to use (NULL = default subtitleoverlay)",
855 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
857 * GstPlayBin:video-stream-combiner
859 * Get or set the current video stream combiner. By default, an input-selector
860 * is created and deleted as-needed.
862 g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
863 g_param_spec_object ("video-stream-combiner", "Video stream combiner",
864 "Current video stream combiner (NULL = input-selector)",
865 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
867 * GstPlayBin:audio-stream-combiner
869 * Get or set the current audio stream combiner. By default, an input-selector
870 * is created and deleted as-needed.
872 g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
873 g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
874 "Current audio stream combiner (NULL = input-selector)",
875 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
877 * GstPlayBin:text-stream-combiner
879 * Get or set the current text stream combiner. By default, an input-selector
880 * is created and deleted as-needed.
882 g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
883 g_param_spec_object ("text-stream-combiner", "Text stream combiner",
884 "Current text stream combiner (NULL = input-selector)",
885 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
890 * Get or set the current audio stream volume. 1.0 means 100%,
891 * 0.0 means mute. This uses a linear volume scale.
894 g_object_class_install_property (gobject_klass, PROP_VOLUME,
895 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
896 0.0, VOLUME_MAX_DOUBLE, 1.0,
897 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
898 g_object_class_install_property (gobject_klass, PROP_MUTE,
899 g_param_spec_boolean ("mute", "Mute",
900 "Mute the audio channel without changing the volume", FALSE,
901 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
905 * @playbin: a #GstPlayBin
907 * Get the currently rendered or prerolled sample in the video sink.
908 * The #GstCaps in the sample will describe the format of the buffer.
910 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
911 g_param_spec_boxed ("sample", "Sample",
912 "The last sample (NULL = no video available)",
913 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
915 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
916 g_param_spec_string ("subtitle-font-desc",
917 "Subtitle font description",
918 "Pango font description of font "
919 "to be used for subtitle rendering", NULL,
920 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
922 g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
923 g_param_spec_uint64 ("connection-speed", "Connection Speed",
924 "Network connection speed in kbps (0 = unknown)",
925 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
926 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
928 g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
929 g_param_spec_int ("buffer-size", "Buffer size (bytes)",
930 "Buffer size when buffering network streams",
931 -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
932 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
933 g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
934 g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
935 "Buffer duration when buffering network streams",
936 -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
937 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
939 * GstPlayBin:av-offset:
941 * Control the synchronisation offset between the audio and video streams.
942 * Positive values make the audio ahead of the video and negative values make
943 * the audio go behind the video.
945 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
946 g_param_spec_int64 ("av-offset", "AV Offset",
947 "The synchronisation offset between audio and video in nanoseconds",
948 G_MININT64, G_MAXINT64, 0,
949 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
952 * GstPlayBin:ring-buffer-max-size
954 * The maximum size of the ring buffer in bytes. If set to 0, the ring
955 * buffer is disabled. Default 0.
957 g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
958 g_param_spec_uint64 ("ring-buffer-max-size",
959 "Max. ring buffer size (bytes)",
960 "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
961 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
962 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
965 * GstPlayBin::force-aspect-ratio:
967 * Requests the video sink to enforce the video display aspect ratio.
969 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
970 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
971 "When enabled, scaling will respect original aspect ratio", TRUE,
972 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
975 * GstPlayBin::about-to-finish
976 * @playbin: a #GstPlayBin
978 * This signal is emitted when the current uri is about to finish. You can
979 * set the uri and suburi to make sure that playback continues.
981 * This signal is emitted from the context of a GStreamer streaming thread.
983 gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
984 g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
986 G_STRUCT_OFFSET (GstPlayBinClass, about_to_finish), NULL, NULL,
987 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
990 * GstPlayBin::video-changed
991 * @playbin: a #GstPlayBin
993 * This signal is emitted whenever the number or order of the video
994 * streams has changed. The application will most likely want to select
995 * a new video stream.
997 * This signal is usually emitted from the context of a GStreamer streaming
998 * thread. You can use gst_message_new_application() and
999 * gst_element_post_message() to notify your application's main thread.
1001 /* FIXME 0.11: turn video-changed signal into message? */
1002 gst_play_bin_signals[SIGNAL_VIDEO_CHANGED] =
1003 g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass),
1005 G_STRUCT_OFFSET (GstPlayBinClass, video_changed), NULL, NULL,
1006 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1008 * GstPlayBin::audio-changed
1009 * @playbin: a #GstPlayBin
1011 * This signal is emitted whenever the number or order of the audio
1012 * streams has changed. The application will most likely want to select
1013 * a new audio stream.
1015 * This signal may be emitted from the context of a GStreamer streaming thread.
1016 * You can use gst_message_new_application() and gst_element_post_message()
1017 * to notify your application's main thread.
1019 /* FIXME 0.11: turn audio-changed signal into message? */
1020 gst_play_bin_signals[SIGNAL_AUDIO_CHANGED] =
1021 g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass),
1023 G_STRUCT_OFFSET (GstPlayBinClass, audio_changed), NULL, NULL,
1024 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1026 * GstPlayBin::text-changed
1027 * @playbin: a #GstPlayBin
1029 * This signal is emitted whenever the number or order of the text
1030 * streams has changed. The application will most likely want to select
1031 * a new text stream.
1033 * This signal may be emitted from the context of a GStreamer streaming thread.
1034 * You can use gst_message_new_application() and gst_element_post_message()
1035 * to notify your application's main thread.
1037 /* FIXME 0.11: turn text-changed signal into message? */
1038 gst_play_bin_signals[SIGNAL_TEXT_CHANGED] =
1039 g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
1041 G_STRUCT_OFFSET (GstPlayBinClass, text_changed), NULL, NULL,
1042 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1045 * GstPlayBin::video-tags-changed
1046 * @playbin: a #GstPlayBin
1047 * @stream: stream index with changed tags
1049 * This signal is emitted whenever the tags of a video stream have changed.
1050 * The application will most likely want to get the new tags.
1052 * This signal may be emitted from the context of a GStreamer streaming thread.
1053 * You can use gst_message_new_application() and gst_element_post_message()
1054 * to notify your application's main thread.
1056 gst_play_bin_signals[SIGNAL_VIDEO_TAGS_CHANGED] =
1057 g_signal_new ("video-tags-changed", G_TYPE_FROM_CLASS (klass),
1059 G_STRUCT_OFFSET (GstPlayBinClass, video_tags_changed), NULL, NULL,
1060 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1063 * GstPlayBin::audio-tags-changed
1064 * @playbin: a #GstPlayBin
1065 * @stream: stream index with changed tags
1067 * This signal is emitted whenever the tags of an audio stream have changed.
1068 * The application will most likely want to get the new tags.
1070 * This signal may be emitted from the context of a GStreamer streaming thread.
1071 * You can use gst_message_new_application() and gst_element_post_message()
1072 * to notify your application's main thread.
1074 gst_play_bin_signals[SIGNAL_AUDIO_TAGS_CHANGED] =
1075 g_signal_new ("audio-tags-changed", G_TYPE_FROM_CLASS (klass),
1077 G_STRUCT_OFFSET (GstPlayBinClass, audio_tags_changed), NULL, NULL,
1078 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1081 * GstPlayBin::text-tags-changed
1082 * @playbin: a #GstPlayBin
1083 * @stream: stream index with changed tags
1085 * This signal is emitted whenever the tags of a text stream have changed.
1086 * The application will most likely want to get the new tags.
1088 * This signal may be emitted from the context of a GStreamer streaming thread.
1089 * You can use gst_message_new_application() and gst_element_post_message()
1090 * to notify your application's main thread.
1092 gst_play_bin_signals[SIGNAL_TEXT_TAGS_CHANGED] =
1093 g_signal_new ("text-tags-changed", G_TYPE_FROM_CLASS (klass),
1095 G_STRUCT_OFFSET (GstPlayBinClass, text_tags_changed), NULL, NULL,
1096 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1099 * GstPlayBin::source-setup:
1100 * @playbin: a #GstPlayBin
1101 * @source: source element
1103 * This signal is emitted after the source element has been created, so
1104 * it can be configured by setting additional properties (e.g. set a
1105 * proxy server for an http source, or set the device and read speed for
1106 * an audio cd source). This is functionally equivalent to connecting to
1107 * the notify::source signal, but more convenient.
1109 * This signal is usually emitted from the context of a GStreamer streaming
1112 gst_play_bin_signals[SIGNAL_SOURCE_SETUP] =
1113 g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
1114 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1115 g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1118 * GstPlayBin::get-video-tags
1119 * @playbin: a #GstPlayBin
1120 * @stream: a video stream number
1122 * Action signal to retrieve the tags of a specific video stream number.
1123 * This information can be used to select a stream.
1125 * Returns: a GstTagList with tags or NULL when the stream number does not
1128 gst_play_bin_signals[SIGNAL_GET_VIDEO_TAGS] =
1129 g_signal_new ("get-video-tags", G_TYPE_FROM_CLASS (klass),
1130 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1131 G_STRUCT_OFFSET (GstPlayBinClass, get_video_tags), NULL, NULL,
1132 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1134 * GstPlayBin::get-audio-tags
1135 * @playbin: a #GstPlayBin
1136 * @stream: an audio stream number
1138 * Action signal to retrieve the tags of a specific audio stream number.
1139 * This information can be used to select a stream.
1141 * Returns: a GstTagList with tags or NULL when the stream number does not
1144 gst_play_bin_signals[SIGNAL_GET_AUDIO_TAGS] =
1145 g_signal_new ("get-audio-tags", G_TYPE_FROM_CLASS (klass),
1146 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1147 G_STRUCT_OFFSET (GstPlayBinClass, get_audio_tags), NULL, NULL,
1148 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1150 * GstPlayBin::get-text-tags
1151 * @playbin: a #GstPlayBin
1152 * @stream: a text stream number
1154 * Action signal to retrieve the tags of a specific text stream number.
1155 * This information can be used to select a stream.
1157 * Returns: a GstTagList with tags or NULL when the stream number does not
1160 gst_play_bin_signals[SIGNAL_GET_TEXT_TAGS] =
1161 g_signal_new ("get-text-tags", G_TYPE_FROM_CLASS (klass),
1162 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1163 G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
1164 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1166 * GstPlayBin::convert-sample
1167 * @playbin: a #GstPlayBin
1168 * @caps: the target format of the frame
1170 * Action signal to retrieve the currently playing video frame in the format
1171 * specified by @caps.
1172 * If @caps is %NULL, no conversion will be performed and this function is
1173 * equivalent to the #GstPlayBin::frame property.
1175 * Returns: a #GstSample of the current video frame converted to #caps.
1176 * The caps on the sample will describe the final layout of the buffer data.
1177 * %NULL is returned when no current buffer can be retrieved or when the
1178 * conversion failed.
1180 gst_play_bin_signals[SIGNAL_CONVERT_SAMPLE] =
1181 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
1182 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1183 G_STRUCT_OFFSET (GstPlayBinClass, convert_sample), NULL, NULL,
1184 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
1187 * GstPlayBin::get-video-pad
1188 * @playbin: a #GstPlayBin
1189 * @stream: a video stream number
1191 * Action signal to retrieve the stream-combiner sinkpad for a specific
1193 * This pad can be used for notifications of caps changes, stream-specific
1196 * Returns: a #GstPad, or NULL when the stream number does not exist.
1198 gst_play_bin_signals[SIGNAL_GET_VIDEO_PAD] =
1199 g_signal_new ("get-video-pad", G_TYPE_FROM_CLASS (klass),
1200 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1201 G_STRUCT_OFFSET (GstPlayBinClass, get_video_pad), NULL, NULL,
1202 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1204 * GstPlayBin::get-audio-pad
1205 * @playbin: a #GstPlayBin
1206 * @stream: an audio stream number
1208 * Action signal to retrieve the stream-combiner sinkpad for a specific
1210 * This pad can be used for notifications of caps changes, stream-specific
1213 * Returns: a #GstPad, or NULL when the stream number does not exist.
1215 gst_play_bin_signals[SIGNAL_GET_AUDIO_PAD] =
1216 g_signal_new ("get-audio-pad", G_TYPE_FROM_CLASS (klass),
1217 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1218 G_STRUCT_OFFSET (GstPlayBinClass, get_audio_pad), NULL, NULL,
1219 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1221 * GstPlayBin::get-text-pad
1222 * @playbin: a #GstPlayBin
1223 * @stream: a text stream number
1225 * Action signal to retrieve the stream-combiner sinkpad for a specific
1227 * This pad can be used for notifications of caps changes, stream-specific
1230 * Returns: a #GstPad, or NULL when the stream number does not exist.
1232 gst_play_bin_signals[SIGNAL_GET_TEXT_PAD] =
1233 g_signal_new ("get-text-pad", G_TYPE_FROM_CLASS (klass),
1234 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1235 G_STRUCT_OFFSET (GstPlayBinClass, get_text_pad), NULL, NULL,
1236 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1238 klass->get_video_tags = gst_play_bin_get_video_tags;
1239 klass->get_audio_tags = gst_play_bin_get_audio_tags;
1240 klass->get_text_tags = gst_play_bin_get_text_tags;
1242 klass->convert_sample = gst_play_bin_convert_sample;
1244 klass->get_video_pad = gst_play_bin_get_video_pad;
1245 klass->get_audio_pad = gst_play_bin_get_audio_pad;
1246 klass->get_text_pad = gst_play_bin_get_text_pad;
1248 gst_element_class_set_static_metadata (gstelement_klass,
1249 "Player Bin 2", "Generic/Bin/Player",
1250 "Autoplug and play media from an uri",
1251 "Wim Taymans <wim.taymans@gmail.com>");
1253 gstelement_klass->change_state =
1254 GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
1255 gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
1256 gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin_set_context);
1258 gstbin_klass->handle_message =
1259 GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
1263 init_group (GstPlayBin * playbin, GstSourceGroup * group)
1265 /* store the array for the different channels */
1266 group->video_channels = g_ptr_array_new ();
1267 group->audio_channels = g_ptr_array_new ();
1268 group->text_channels = g_ptr_array_new ();
1269 g_mutex_init (&group->lock);
1271 group->stream_changed_pending = FALSE;
1272 g_mutex_init (&group->stream_changed_pending_lock);
1274 /* init combiners. The combiner is found by finding the first prefix that
1275 * matches the media. */
1276 group->playbin = playbin;
1277 /* If you add any items to these lists, check that media_list[] is defined
1278 * above to be large enough to hold MAX(items)+1, so as to accommodate a
1279 * NULL terminator (set when the memory is zeroed on allocation) */
1280 group->combiner[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
1281 group->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
1282 group->combiner[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
1283 group->combiner[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
1284 group->combiner[PLAYBIN_STREAM_VIDEO].media_list[1] = "image/";
1285 group->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
1286 group->combiner[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
1287 group->combiner[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
1288 group->combiner[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
1289 group->combiner[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
1290 group->combiner[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
1291 group->combiner[PLAYBIN_STREAM_TEXT].media_list[4] = "subpicture/x-dvd";
1292 group->combiner[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
1293 group->combiner[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
1294 group->combiner[PLAYBIN_STREAM_TEXT].get_media_caps =
1295 gst_subtitle_overlay_create_factory_caps;
1296 group->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
1297 group->combiner[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
1301 free_group (GstPlayBin * playbin, GstSourceGroup * group)
1303 g_free (group->uri);
1304 g_free (group->suburi);
1305 g_ptr_array_free (group->video_channels, TRUE);
1306 g_ptr_array_free (group->audio_channels, TRUE);
1307 g_ptr_array_free (group->text_channels, TRUE);
1309 g_mutex_clear (&group->lock);
1310 if (group->audio_sink)
1311 gst_object_unref (group->audio_sink);
1312 group->audio_sink = NULL;
1313 if (group->video_sink)
1314 gst_object_unref (group->video_sink);
1315 group->video_sink = NULL;
1316 if (group->text_sink)
1317 gst_object_unref (group->text_sink);
1318 group->text_sink = NULL;
1320 group->stream_changed_pending = FALSE;
1321 g_mutex_clear (&group->stream_changed_pending_lock);
1323 g_slist_free (group->suburi_flushes_to_drop);
1324 group->suburi_flushes_to_drop = NULL;
1326 if (group->suburi_flushes_to_drop_lock.p)
1327 g_mutex_clear (&group->suburi_flushes_to_drop_lock);
1328 group->suburi_flushes_to_drop_lock.p = NULL;
1332 notify_volume_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin * playbin)
1334 g_object_notify (G_OBJECT (playbin), "volume");
1338 notify_mute_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin * playbin)
1340 g_object_notify (G_OBJECT (playbin), "mute");
1344 colorbalance_value_changed_cb (GstColorBalance * balance,
1345 GstColorBalanceChannel * channel, gint value, GstPlayBin * playbin)
1347 gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
1351 compare_factories_func (gconstpointer p1, gconstpointer p2)
1353 GstPluginFeature *f1, *f2;
1354 gboolean is_sink1, is_sink2;
1355 gboolean is_parser1, is_parser2;
1357 f1 = (GstPluginFeature *) p1;
1358 f2 = (GstPluginFeature *) p2;
1360 is_sink1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1361 GST_ELEMENT_FACTORY_TYPE_SINK);
1362 is_sink2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1363 GST_ELEMENT_FACTORY_TYPE_SINK);
1364 is_parser1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1365 GST_ELEMENT_FACTORY_TYPE_PARSER);
1366 is_parser2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1367 GST_ELEMENT_FACTORY_TYPE_PARSER);
1369 /* First we want all sinks as we prefer a sink if it directly
1370 * supports the current caps */
1371 if (is_sink1 && !is_sink2)
1373 else if (!is_sink1 && is_sink2)
1376 /* Then we want all parsers as we always want to plug parsers
1377 * before decoders */
1378 if (is_parser1 && !is_parser2)
1380 else if (!is_parser1 && is_parser2)
1383 /* And if it's a both a parser or sink we first sort by rank
1384 * and then by factory name */
1385 return gst_plugin_feature_rank_compare_func (p1, p2);
1388 /* Must be called with elements lock! */
1390 gst_play_bin_update_elements_list (GstPlayBin * playbin)
1395 cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
1397 if (!playbin->elements || playbin->elements_cookie != cookie) {
1398 if (playbin->elements)
1399 gst_plugin_feature_list_free (playbin->elements);
1401 gst_element_factory_list_get_elements
1402 (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1404 gst_element_factory_list_get_elements
1405 (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
1406 playbin->elements = g_list_concat (res, tmp);
1407 playbin->elements = g_list_sort (playbin->elements, compare_factories_func);
1410 if (!playbin->aelements || playbin->elements_cookie != cookie) {
1411 if (playbin->aelements)
1412 g_sequence_free (playbin->aelements);
1413 playbin->aelements = avelements_create (playbin, TRUE);
1416 if (!playbin->velements || playbin->elements_cookie != cookie) {
1417 if (playbin->velements)
1418 g_sequence_free (playbin->velements);
1419 playbin->velements = avelements_create (playbin, FALSE);
1422 playbin->elements_cookie = cookie;
1426 gst_play_bin_init (GstPlayBin * playbin)
1428 g_rec_mutex_init (&playbin->lock);
1429 g_mutex_init (&playbin->dyn_lock);
1431 /* assume we can create an input-selector */
1432 playbin->have_selector = TRUE;
1435 playbin->curr_group = &playbin->groups[0];
1436 playbin->next_group = &playbin->groups[1];
1437 init_group (playbin, &playbin->groups[0]);
1438 init_group (playbin, &playbin->groups[1]);
1440 /* first filter out the interesting element factories */
1441 g_mutex_init (&playbin->elements_lock);
1445 g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
1447 gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
1448 gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1449 /* Connect to notify::volume and notify::mute signals for proxying */
1450 g_signal_connect (playbin->playsink, "notify::volume",
1451 G_CALLBACK (notify_volume_cb), playbin);
1452 g_signal_connect (playbin->playsink, "notify::mute",
1453 G_CALLBACK (notify_mute_cb), playbin);
1454 g_signal_connect (playbin->playsink, "value-changed",
1455 G_CALLBACK (colorbalance_value_changed_cb), playbin);
1457 playbin->current_video = DEFAULT_CURRENT_VIDEO;
1458 playbin->current_audio = DEFAULT_CURRENT_AUDIO;
1459 playbin->current_text = DEFAULT_CURRENT_TEXT;
1461 playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
1462 playbin->buffer_size = DEFAULT_BUFFER_SIZE;
1463 playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
1465 playbin->force_aspect_ratio = TRUE;
1469 gst_play_bin_finalize (GObject * object)
1471 GstPlayBin *playbin;
1473 playbin = GST_PLAY_BIN (object);
1475 free_group (playbin, &playbin->groups[0]);
1476 free_group (playbin, &playbin->groups[1]);
1478 if (playbin->source)
1479 gst_object_unref (playbin->source);
1481 /* Setting states to NULL is safe here because playsink
1482 * will already be gone and none of these sinks will be
1483 * a child of playsink
1485 if (playbin->video_sink) {
1486 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
1487 gst_object_unref (playbin->video_sink);
1489 if (playbin->audio_sink) {
1490 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
1491 gst_object_unref (playbin->audio_sink);
1493 if (playbin->text_sink) {
1494 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
1495 gst_object_unref (playbin->text_sink);
1498 if (playbin->video_stream_combiner) {
1499 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
1500 gst_object_unref (playbin->video_stream_combiner);
1502 if (playbin->audio_stream_combiner) {
1503 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
1504 gst_object_unref (playbin->audio_stream_combiner);
1506 if (playbin->text_stream_combiner) {
1507 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
1508 gst_object_unref (playbin->text_stream_combiner);
1511 if (playbin->elements)
1512 gst_plugin_feature_list_free (playbin->elements);
1514 if (playbin->aelements)
1515 g_sequence_free (playbin->aelements);
1517 if (playbin->velements)
1518 g_sequence_free (playbin->velements);
1520 g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref);
1522 g_rec_mutex_clear (&playbin->lock);
1523 g_mutex_clear (&playbin->dyn_lock);
1524 g_mutex_clear (&playbin->elements_lock);
1526 G_OBJECT_CLASS (parent_class)->finalize (object);
1530 gst_playbin_uri_is_valid (GstPlayBin * playbin, const gchar * uri)
1534 GST_LOG_OBJECT (playbin, "checking uri '%s'", uri);
1536 /* this just checks the protocol */
1537 if (!gst_uri_is_valid (uri))
1540 for (c = uri; *c != '\0'; ++c) {
1541 if (!g_ascii_isprint (*c))
1551 GST_WARNING_OBJECT (playbin, "uri '%s' not valid, character #%u",
1552 uri, (guint) ((guintptr) c - (guintptr) uri));
1558 gst_play_bin_set_uri (GstPlayBin * playbin, const gchar * uri)
1560 GstSourceGroup *group;
1563 g_warning ("cannot set NULL uri");
1567 if (!gst_playbin_uri_is_valid (playbin, uri)) {
1568 if (g_str_has_prefix (uri, "file:")) {
1569 GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
1570 "sure to escape spaces and non-ASCII characters properly and specify "
1571 "an absolute path. Use gst_filename_to_uri() to convert filenames "
1574 /* GST_ERROR_OBJECT (playbin, "malformed URI '%s'", uri); */
1578 GST_PLAY_BIN_LOCK (playbin);
1579 group = playbin->next_group;
1581 GST_SOURCE_GROUP_LOCK (group);
1582 /* store the uri in the next group we will play */
1583 g_free (group->uri);
1584 group->uri = g_strdup (uri);
1585 group->valid = TRUE;
1586 GST_SOURCE_GROUP_UNLOCK (group);
1588 GST_DEBUG ("set new uri to %s", uri);
1589 GST_PLAY_BIN_UNLOCK (playbin);
1593 gst_play_bin_set_suburi (GstPlayBin * playbin, const gchar * suburi)
1595 GstSourceGroup *group;
1597 GST_PLAY_BIN_LOCK (playbin);
1598 group = playbin->next_group;
1600 GST_SOURCE_GROUP_LOCK (group);
1601 g_free (group->suburi);
1602 group->suburi = g_strdup (suburi);
1603 GST_SOURCE_GROUP_UNLOCK (group);
1605 GST_DEBUG ("setting new .sub uri to %s", suburi);
1607 GST_PLAY_BIN_UNLOCK (playbin);
1611 gst_play_bin_set_flags (GstPlayBin * playbin, GstPlayFlags flags)
1613 gst_play_sink_set_flags (playbin->playsink, flags);
1614 gst_play_sink_reconfigure (playbin->playsink);
1618 gst_play_bin_get_flags (GstPlayBin * playbin)
1622 flags = gst_play_sink_get_flags (playbin->playsink);
1627 /* get the currently playing group or if nothing is playing, the next
1628 * group. Must be called with the PLAY_BIN_LOCK. */
1629 static GstSourceGroup *
1630 get_group (GstPlayBin * playbin)
1632 GstSourceGroup *result;
1634 if (!(result = playbin->curr_group))
1635 result = playbin->next_group;
1641 gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream)
1643 GstPad *sinkpad = NULL;
1644 GstSourceGroup *group;
1646 GST_PLAY_BIN_LOCK (playbin);
1647 group = get_group (playbin);
1648 if (stream < group->video_channels->len) {
1649 sinkpad = g_ptr_array_index (group->video_channels, stream);
1650 gst_object_ref (sinkpad);
1652 GST_PLAY_BIN_UNLOCK (playbin);
1658 gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream)
1660 GstPad *sinkpad = NULL;
1661 GstSourceGroup *group;
1663 GST_PLAY_BIN_LOCK (playbin);
1664 group = get_group (playbin);
1665 if (stream < group->audio_channels->len) {
1666 sinkpad = g_ptr_array_index (group->audio_channels, stream);
1667 gst_object_ref (sinkpad);
1669 GST_PLAY_BIN_UNLOCK (playbin);
1675 gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream)
1677 GstPad *sinkpad = NULL;
1678 GstSourceGroup *group;
1680 GST_PLAY_BIN_LOCK (playbin);
1681 group = get_group (playbin);
1682 if (stream < group->text_channels->len) {
1683 sinkpad = g_ptr_array_index (group->text_channels, stream);
1684 gst_object_ref (sinkpad);
1686 GST_PLAY_BIN_UNLOCK (playbin);
1693 get_tags (GstPlayBin * playbin, GstSourceGroup * group, gint type, gint stream)
1696 GPtrArray *channels;
1700 case PLAYBIN_STREAM_AUDIO:
1701 channels = group->audio_channels;
1703 case PLAYBIN_STREAM_VIDEO:
1704 channels = group->video_channels;
1706 case PLAYBIN_STREAM_TEXT:
1707 channels = group->text_channels;
1714 if (!channels || stream >= channels->len || !group->combiner[type].has_tags)
1717 sinkpad = g_ptr_array_index (channels, stream);
1718 g_object_get (sinkpad, "tags", &result, NULL);
1724 gst_play_bin_get_video_tags (GstPlayBin * playbin, gint stream)
1727 GstSourceGroup *group;
1729 GST_PLAY_BIN_LOCK (playbin);
1730 group = get_group (playbin);
1731 result = get_tags (playbin, group, PLAYBIN_STREAM_VIDEO, stream);
1732 GST_PLAY_BIN_UNLOCK (playbin);
1738 gst_play_bin_get_audio_tags (GstPlayBin * playbin, gint stream)
1741 GstSourceGroup *group;
1743 GST_PLAY_BIN_LOCK (playbin);
1744 group = get_group (playbin);
1745 result = get_tags (playbin, group, PLAYBIN_STREAM_AUDIO, stream);
1746 GST_PLAY_BIN_UNLOCK (playbin);
1752 gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
1755 GstSourceGroup *group;
1757 GST_PLAY_BIN_LOCK (playbin);
1758 group = get_group (playbin);
1759 result = get_tags (playbin, group, PLAYBIN_STREAM_TEXT, stream);
1760 GST_PLAY_BIN_UNLOCK (playbin);
1766 gst_play_bin_convert_sample (GstPlayBin * playbin, GstCaps * caps)
1768 return gst_play_sink_convert_sample (playbin->playsink, caps);
1771 /* Returns current stream number, or -1 if none has been selected yet */
1773 get_current_stream_number (GstPlayBin * playbin, GstSourceCombine * combine,
1774 GPtrArray * channels)
1776 /* Internal API cleanup would make this easier... */
1778 GstPad *pad, *current;
1779 GstObject *combiner = NULL;
1782 if (!combine->has_active_pad) {
1783 GST_WARNING_OBJECT (playbin,
1784 "combiner doesn't have the \"active-pad\" property");
1788 for (i = 0; i < channels->len; i++) {
1789 pad = g_ptr_array_index (channels, i);
1790 if ((combiner = gst_pad_get_parent (pad))) {
1791 g_object_get (combiner, "active-pad", ¤t, NULL);
1792 gst_object_unref (combiner);
1794 if (pad == current) {
1795 gst_object_unref (current);
1801 gst_object_unref (current);
1809 gst_play_bin_send_custom_event (GstObject * combiner, const gchar * event_name)
1815 gboolean ret = FALSE;
1817 src = gst_element_get_static_pad (GST_ELEMENT_CAST (combiner), "src");
1818 peer = gst_pad_get_peer (src);
1820 s = gst_structure_new_empty (event_name);
1821 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
1822 gst_pad_send_event (peer, event);
1823 gst_object_unref (peer);
1826 gst_object_unref (src);
1831 gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
1833 GstSourceGroup *group;
1834 GPtrArray *channels;
1837 GST_PLAY_BIN_LOCK (playbin);
1839 GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
1840 playbin->current_video, stream);
1842 group = get_group (playbin);
1843 if (!group->combiner[PLAYBIN_STREAM_VIDEO].has_active_pad)
1845 if (!(channels = group->video_channels))
1848 if (stream == -1 || channels->len <= stream) {
1851 /* take channel from selected stream */
1852 sinkpad = g_ptr_array_index (channels, stream);
1856 gst_object_ref (sinkpad);
1857 GST_PLAY_BIN_UNLOCK (playbin);
1860 GstObject *combiner;
1862 if ((combiner = gst_pad_get_parent (sinkpad))) {
1863 GstPad *old_sinkpad;
1865 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
1867 if (old_sinkpad != sinkpad) {
1868 if (gst_play_bin_send_custom_event (combiner,
1869 "playsink-custom-video-flush"))
1870 playbin->video_pending_flush_finish = TRUE;
1872 /* activate the selected pad */
1873 g_object_set (combiner, "active-pad", sinkpad, NULL);
1876 gst_object_unref (combiner);
1878 gst_object_unref (sinkpad);
1884 GST_PLAY_BIN_UNLOCK (playbin);
1885 GST_WARNING_OBJECT (playbin,
1886 "can't switch video, the stream combiner's sink pads don't have the \"active-pad\" property");
1891 GST_PLAY_BIN_UNLOCK (playbin);
1892 GST_DEBUG_OBJECT (playbin, "can't switch video, we have no channels");
1898 gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
1900 GstSourceGroup *group;
1901 GPtrArray *channels;
1904 GST_PLAY_BIN_LOCK (playbin);
1906 GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
1907 playbin->current_audio, stream);
1909 group = get_group (playbin);
1910 if (!group->combiner[PLAYBIN_STREAM_AUDIO].has_active_pad)
1912 if (!(channels = group->audio_channels))
1915 if (stream == -1 || channels->len <= stream) {
1918 /* take channel from selected stream */
1919 sinkpad = g_ptr_array_index (channels, stream);
1923 gst_object_ref (sinkpad);
1924 GST_PLAY_BIN_UNLOCK (playbin);
1927 GstObject *combiner;
1929 if ((combiner = gst_pad_get_parent (sinkpad))) {
1930 GstPad *old_sinkpad;
1932 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
1934 if (old_sinkpad != sinkpad) {
1935 if (gst_play_bin_send_custom_event (combiner,
1936 "playsink-custom-audio-flush"))
1937 playbin->audio_pending_flush_finish = TRUE;
1939 /* activate the selected pad */
1940 g_object_set (combiner, "active-pad", sinkpad, NULL);
1942 gst_object_unref (combiner);
1944 gst_object_unref (sinkpad);
1950 GST_PLAY_BIN_UNLOCK (playbin);
1951 GST_WARNING_OBJECT (playbin,
1952 "can't switch audio, the stream combiner's sink pads don't have the \"active-pad\" property");
1957 GST_PLAY_BIN_UNLOCK (playbin);
1958 GST_DEBUG_OBJECT (playbin, "can't switch audio, we have no channels");
1964 gst_play_bin_suburidecodebin_seek_to_start (GstSourceGroup * group)
1966 GstElement *suburidecodebin = group->suburidecodebin;
1967 GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
1969 GValue item = { 0, };
1971 if (it && gst_iterator_next (it, &item) == GST_ITERATOR_OK
1972 && ((sinkpad = g_value_get_object (&item)) != NULL)) {
1977 gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
1978 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
1979 seqnum = gst_event_get_seqnum (event);
1981 /* store the seqnum to drop flushes from this seek later */
1982 g_mutex_lock (&group->suburi_flushes_to_drop_lock);
1983 group->suburi_flushes_to_drop =
1984 g_slist_append (group->suburi_flushes_to_drop,
1985 GUINT_TO_POINTER (seqnum));
1986 g_mutex_unlock (&group->suburi_flushes_to_drop_lock);
1988 if (!gst_pad_send_event (sinkpad, event)) {
1990 gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
1991 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
1992 gst_event_set_seqnum (event, seqnum);
1993 if (!gst_pad_send_event (sinkpad, event)) {
1994 GST_DEBUG_OBJECT (suburidecodebin, "Seeking to the beginning failed!");
1996 g_mutex_lock (&group->suburi_flushes_to_drop_lock);
1997 group->suburi_flushes_to_drop =
1998 g_slist_remove (group->suburi_flushes_to_drop,
1999 GUINT_TO_POINTER (seqnum));
2000 g_mutex_unlock (&group->suburi_flushes_to_drop_lock);
2004 g_value_unset (&item);
2008 gst_iterator_free (it);
2012 gst_play_bin_suburidecodebin_block (GstSourceGroup * group,
2013 GstElement * suburidecodebin, gboolean block)
2015 GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
2016 gboolean done = FALSE;
2017 GValue item = { 0, };
2019 GST_DEBUG_OBJECT (suburidecodebin, "Blocking suburidecodebin: %d", block);
2026 switch (gst_iterator_next (it, &item)) {
2027 case GST_ITERATOR_OK:
2028 sinkpad = g_value_get_object (&item);
2031 gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2033 } else if (group->block_id) {
2034 gst_pad_remove_probe (sinkpad, group->block_id);
2035 group->block_id = 0;
2037 g_value_reset (&item);
2039 case GST_ITERATOR_DONE:
2042 case GST_ITERATOR_RESYNC:
2043 gst_iterator_resync (it);
2045 case GST_ITERATOR_ERROR:
2050 g_value_unset (&item);
2051 gst_iterator_free (it);
2055 gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
2057 GstSourceGroup *group;
2058 GPtrArray *channels;
2061 GST_PLAY_BIN_LOCK (playbin);
2063 GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
2064 playbin->current_text, stream);
2066 group = get_group (playbin);
2067 if (!group->combiner[PLAYBIN_STREAM_TEXT].has_active_pad)
2069 if (!(channels = group->text_channels))
2072 if (stream == -1 || channels->len <= stream) {
2075 /* take channel from selected stream */
2076 sinkpad = g_ptr_array_index (channels, stream);
2080 gst_object_ref (sinkpad);
2081 GST_PLAY_BIN_UNLOCK (playbin);
2084 GstObject *combiner;
2086 if ((combiner = gst_pad_get_parent (sinkpad))) {
2087 GstPad *old_sinkpad;
2089 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
2091 if (old_sinkpad != sinkpad) {
2092 gboolean need_unblock, need_block, need_seek;
2093 GstPad *peer = NULL, *oldpeer = NULL;
2094 GstElement *parent_element = NULL, *old_parent_element = NULL;
2096 /* Now check if we need to seek the suburidecodebin to the beginning
2097 * or if we need to block all suburidecodebin sinkpads or if we need
2098 * to unblock all suburidecodebin sinkpads
2101 peer = gst_pad_get_peer (sinkpad);
2103 oldpeer = gst_pad_get_peer (old_sinkpad);
2106 parent_element = gst_pad_get_parent_element (peer);
2108 old_parent_element = gst_pad_get_parent_element (oldpeer);
2110 need_block = (old_parent_element == group->suburidecodebin
2111 && parent_element != old_parent_element);
2112 need_unblock = (parent_element == group->suburidecodebin
2113 && parent_element != old_parent_element);
2114 need_seek = (parent_element == group->suburidecodebin);
2117 gst_object_unref (peer);
2119 gst_object_unref (oldpeer);
2121 gst_object_unref (parent_element);
2122 if (old_parent_element)
2123 gst_object_unref (old_parent_element);
2125 /* Block all suburidecodebin sinkpads */
2127 gst_play_bin_suburidecodebin_block (group, group->suburidecodebin,
2130 if (gst_play_bin_send_custom_event (combiner,
2131 "playsink-custom-subtitle-flush"))
2132 playbin->text_pending_flush_finish = TRUE;
2134 /* activate the selected pad */
2135 g_object_set (combiner, "active-pad", sinkpad, NULL);
2137 /* Unblock pads if necessary */
2139 gst_play_bin_suburidecodebin_block (group, group->suburidecodebin,
2142 /* seek to the beginning */
2144 gst_play_bin_suburidecodebin_seek_to_start (group);
2146 gst_object_unref (combiner);
2149 gst_object_unref (old_sinkpad);
2151 gst_object_unref (sinkpad);
2157 GST_PLAY_BIN_UNLOCK (playbin);
2158 GST_WARNING_OBJECT (playbin,
2159 "can't switch text, the stream combiner's sink pads don't have the \"active-pad\" property");
2164 GST_PLAY_BIN_UNLOCK (playbin);
2170 gst_play_bin_set_sink (GstPlayBin * playbin, GstPlaySinkType type,
2171 const gchar * dbg, GstElement ** elem, GstElement * sink)
2173 GST_INFO_OBJECT (playbin, "Setting %s sink to %" GST_PTR_FORMAT, dbg, sink);
2175 gst_play_sink_set_sink (playbin->playsink, type, sink);
2178 gst_object_unref (*elem);
2179 *elem = sink ? gst_object_ref (sink) : NULL;
2183 gst_play_bin_set_stream_combiner (GstPlayBin * playbin, GstElement ** elem,
2184 const gchar * dbg, GstElement * combiner)
2186 GST_INFO_OBJECT (playbin, "Setting %s stream combiner to %" GST_PTR_FORMAT,
2189 GST_PLAY_BIN_LOCK (playbin);
2190 if (*elem != combiner) {
2195 gst_object_ref_sink (combiner);
2199 gst_object_unref (old);
2201 GST_LOG_OBJECT (playbin, "%s stream combiner now %" GST_PTR_FORMAT, dbg,
2203 GST_PLAY_BIN_UNLOCK (playbin);
2207 gst_play_bin_set_encoding (GstPlayBin * playbin, const gchar * encoding)
2211 GST_PLAY_BIN_LOCK (playbin);
2213 /* set subtitles on all current and next decodebins. */
2214 if ((elem = playbin->groups[0].uridecodebin))
2215 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2216 if ((elem = playbin->groups[0].suburidecodebin))
2217 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2218 if ((elem = playbin->groups[1].uridecodebin))
2219 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2220 if ((elem = playbin->groups[1].suburidecodebin))
2221 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2223 gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
2224 GST_PLAY_BIN_UNLOCK (playbin);
2228 gst_play_bin_set_property (GObject * object, guint prop_id,
2229 const GValue * value, GParamSpec * pspec)
2231 GstPlayBin *playbin = GST_PLAY_BIN (object);
2235 gst_play_bin_set_uri (playbin, g_value_get_string (value));
2238 gst_play_bin_set_suburi (playbin, g_value_get_string (value));
2241 gst_play_bin_set_flags (playbin, g_value_get_flags (value));
2243 case PROP_CURRENT_VIDEO:
2244 gst_play_bin_set_current_video_stream (playbin, g_value_get_int (value));
2246 case PROP_CURRENT_AUDIO:
2247 gst_play_bin_set_current_audio_stream (playbin, g_value_get_int (value));
2249 case PROP_CURRENT_TEXT:
2250 gst_play_bin_set_current_text_stream (playbin, g_value_get_int (value));
2252 case PROP_SUBTITLE_ENCODING:
2253 gst_play_bin_set_encoding (playbin, g_value_get_string (value));
2255 case PROP_VIDEO_SINK:
2256 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_VIDEO, "video",
2257 &playbin->video_sink, g_value_get_object (value));
2259 case PROP_AUDIO_SINK:
2260 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_AUDIO, "audio",
2261 &playbin->audio_sink, g_value_get_object (value));
2263 case PROP_VIS_PLUGIN:
2264 gst_play_sink_set_vis_plugin (playbin->playsink,
2265 g_value_get_object (value));
2267 case PROP_TEXT_SINK:
2268 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_TEXT, "text",
2269 &playbin->text_sink, g_value_get_object (value));
2271 case PROP_VIDEO_STREAM_COMBINER:
2272 gst_play_bin_set_stream_combiner (playbin,
2273 &playbin->video_stream_combiner, "video", g_value_get_object (value));
2275 case PROP_AUDIO_STREAM_COMBINER:
2276 gst_play_bin_set_stream_combiner (playbin,
2277 &playbin->audio_stream_combiner, "audio", g_value_get_object (value));
2279 case PROP_TEXT_STREAM_COMBINER:
2280 gst_play_bin_set_stream_combiner (playbin,
2281 &playbin->text_stream_combiner, "text", g_value_get_object (value));
2284 gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
2287 gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
2289 case PROP_FONT_DESC:
2290 gst_play_sink_set_font_desc (playbin->playsink,
2291 g_value_get_string (value));
2293 case PROP_CONNECTION_SPEED:
2294 GST_PLAY_BIN_LOCK (playbin);
2295 playbin->connection_speed = g_value_get_uint64 (value) * 1000;
2296 GST_PLAY_BIN_UNLOCK (playbin);
2298 case PROP_BUFFER_SIZE:
2299 playbin->buffer_size = g_value_get_int (value);
2301 case PROP_BUFFER_DURATION:
2302 playbin->buffer_duration = g_value_get_int64 (value);
2304 case PROP_AV_OFFSET:
2305 gst_play_sink_set_av_offset (playbin->playsink,
2306 g_value_get_int64 (value));
2308 case PROP_RING_BUFFER_MAX_SIZE:
2309 playbin->ring_buffer_max_size = g_value_get_uint64 (value);
2311 case PROP_FORCE_ASPECT_RATIO:
2312 g_object_set (playbin->playsink, "force-aspect-ratio",
2313 g_value_get_boolean (value), NULL);
2316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2322 gst_play_bin_get_current_sink (GstPlayBin * playbin, GstElement ** elem,
2323 const gchar * dbg, GstPlaySinkType type)
2325 GstElement *sink = gst_play_sink_get_sink (playbin->playsink, type);
2327 GST_LOG_OBJECT (playbin, "play_sink_get_sink() returned %s sink %"
2328 GST_PTR_FORMAT ", the originally set %s sink is %" GST_PTR_FORMAT,
2329 dbg, sink, dbg, *elem);
2332 GST_PLAY_BIN_LOCK (playbin);
2334 gst_object_ref (sink);
2335 GST_PLAY_BIN_UNLOCK (playbin);
2342 gst_play_bin_get_current_stream_combiner (GstPlayBin * playbin,
2343 GstElement ** elem, const gchar * dbg, int stream_type)
2345 GstElement *combiner;
2347 GST_PLAY_BIN_LOCK (playbin);
2348 if ((combiner = playbin->curr_group->combiner[stream_type].combiner))
2349 gst_object_ref (combiner);
2350 else if ((combiner = *elem))
2351 gst_object_ref (combiner);
2352 GST_PLAY_BIN_UNLOCK (playbin);
2358 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
2361 GstPlayBin *playbin = GST_PLAY_BIN (object);
2366 GstSourceGroup *group;
2368 GST_PLAY_BIN_LOCK (playbin);
2369 group = playbin->next_group;
2370 g_value_set_string (value, group->uri);
2371 GST_PLAY_BIN_UNLOCK (playbin);
2375 case PROP_CURRENT_URI:
2377 GstSourceGroup *group;
2379 GST_PLAY_BIN_LOCK (playbin);
2380 group = get_group (playbin);
2381 g_value_set_string (value, group->uri);
2382 GST_PLAY_BIN_UNLOCK (playbin);
2387 GstSourceGroup *group;
2389 GST_PLAY_BIN_LOCK (playbin);
2390 group = playbin->next_group;
2391 g_value_set_string (value, group->suburi);
2392 GST_PLAY_BIN_UNLOCK (playbin);
2395 case PROP_CURRENT_SUBURI:
2397 GstSourceGroup *group;
2399 GST_PLAY_BIN_LOCK (playbin);
2400 group = get_group (playbin);
2401 g_value_set_string (value, group->suburi);
2402 GST_PLAY_BIN_UNLOCK (playbin);
2407 GST_OBJECT_LOCK (playbin);
2408 g_value_set_object (value, playbin->source);
2409 GST_OBJECT_UNLOCK (playbin);
2413 g_value_set_flags (value, gst_play_bin_get_flags (playbin));
2417 GstSourceGroup *group;
2420 GST_PLAY_BIN_LOCK (playbin);
2421 group = get_group (playbin);
2422 n_video = (group->video_channels ? group->video_channels->len : 0);
2423 g_value_set_int (value, n_video);
2424 GST_PLAY_BIN_UNLOCK (playbin);
2427 case PROP_CURRENT_VIDEO:
2428 GST_PLAY_BIN_LOCK (playbin);
2429 g_value_set_int (value, playbin->current_video);
2430 GST_PLAY_BIN_UNLOCK (playbin);
2434 GstSourceGroup *group;
2437 GST_PLAY_BIN_LOCK (playbin);
2438 group = get_group (playbin);
2439 n_audio = (group->audio_channels ? group->audio_channels->len : 0);
2440 g_value_set_int (value, n_audio);
2441 GST_PLAY_BIN_UNLOCK (playbin);
2444 case PROP_CURRENT_AUDIO:
2445 GST_PLAY_BIN_LOCK (playbin);
2446 g_value_set_int (value, playbin->current_audio);
2447 GST_PLAY_BIN_UNLOCK (playbin);
2451 GstSourceGroup *group;
2454 GST_PLAY_BIN_LOCK (playbin);
2455 group = get_group (playbin);
2456 n_text = (group->text_channels ? group->text_channels->len : 0);
2457 g_value_set_int (value, n_text);
2458 GST_PLAY_BIN_UNLOCK (playbin);
2461 case PROP_CURRENT_TEXT:
2462 GST_PLAY_BIN_LOCK (playbin);
2463 g_value_set_int (value, playbin->current_text);
2464 GST_PLAY_BIN_UNLOCK (playbin);
2466 case PROP_SUBTITLE_ENCODING:
2467 GST_PLAY_BIN_LOCK (playbin);
2468 g_value_take_string (value,
2469 gst_play_sink_get_subtitle_encoding (playbin->playsink));
2470 GST_PLAY_BIN_UNLOCK (playbin);
2472 case PROP_VIDEO_SINK:
2473 g_value_take_object (value,
2474 gst_play_bin_get_current_sink (playbin, &playbin->video_sink,
2475 "video", GST_PLAY_SINK_TYPE_VIDEO));
2477 case PROP_AUDIO_SINK:
2478 g_value_take_object (value,
2479 gst_play_bin_get_current_sink (playbin, &playbin->audio_sink,
2480 "audio", GST_PLAY_SINK_TYPE_AUDIO));
2482 case PROP_VIS_PLUGIN:
2483 g_value_take_object (value,
2484 gst_play_sink_get_vis_plugin (playbin->playsink));
2486 case PROP_TEXT_SINK:
2487 g_value_take_object (value,
2488 gst_play_bin_get_current_sink (playbin, &playbin->text_sink,
2489 "text", GST_PLAY_SINK_TYPE_TEXT));
2491 case PROP_VIDEO_STREAM_COMBINER:
2492 g_value_take_object (value,
2493 gst_play_bin_get_current_stream_combiner (playbin,
2494 &playbin->video_stream_combiner, "video", PLAYBIN_STREAM_VIDEO));
2496 case PROP_AUDIO_STREAM_COMBINER:
2497 g_value_take_object (value,
2498 gst_play_bin_get_current_stream_combiner (playbin,
2499 &playbin->audio_stream_combiner, "audio", PLAYBIN_STREAM_VIDEO));
2501 case PROP_TEXT_STREAM_COMBINER:
2502 g_value_take_object (value,
2503 gst_play_bin_get_current_stream_combiner (playbin,
2504 &playbin->text_stream_combiner, "text", PLAYBIN_STREAM_VIDEO));
2507 g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
2510 g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
2513 gst_value_take_sample (value,
2514 gst_play_sink_get_last_sample (playbin->playsink));
2516 case PROP_FONT_DESC:
2517 g_value_take_string (value,
2518 gst_play_sink_get_font_desc (playbin->playsink));
2520 case PROP_CONNECTION_SPEED:
2521 GST_PLAY_BIN_LOCK (playbin);
2522 g_value_set_uint64 (value, playbin->connection_speed / 1000);
2523 GST_PLAY_BIN_UNLOCK (playbin);
2525 case PROP_BUFFER_SIZE:
2526 GST_OBJECT_LOCK (playbin);
2527 g_value_set_int (value, playbin->buffer_size);
2528 GST_OBJECT_UNLOCK (playbin);
2530 case PROP_BUFFER_DURATION:
2531 GST_OBJECT_LOCK (playbin);
2532 g_value_set_int64 (value, playbin->buffer_duration);
2533 GST_OBJECT_UNLOCK (playbin);
2535 case PROP_AV_OFFSET:
2536 g_value_set_int64 (value,
2537 gst_play_sink_get_av_offset (playbin->playsink));
2539 case PROP_RING_BUFFER_MAX_SIZE:
2540 g_value_set_uint64 (value, playbin->ring_buffer_max_size);
2542 case PROP_FORCE_ASPECT_RATIO:{
2545 g_object_get (playbin->playsink, "force-aspect-ratio", &v, NULL);
2546 g_value_set_boolean (value, v);
2550 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2556 gst_play_bin_update_cached_duration_from_query (GstPlayBin * playbin,
2557 gboolean valid, GstQuery * query)
2563 GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
2564 gst_query_parse_duration (query, &fmt, &duration);
2566 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2567 if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
2568 playbin->duration[i].valid = valid;
2569 playbin->duration[i].format = fmt;
2570 playbin->duration[i].duration = valid ? duration : -1;
2577 gst_play_bin_update_cached_duration (GstPlayBin * playbin)
2579 const GstFormat formats[] =
2580 { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
2585 GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
2586 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
2587 query = gst_query_new_duration (formats[i]);
2589 GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
2591 gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
2592 gst_query_unref (query);
2597 gst_play_bin_query (GstElement * element, GstQuery * query)
2599 GstPlayBin *playbin = GST_PLAY_BIN (element);
2602 /* During a group switch we shouldn't allow duration queries
2603 * because it's not clear if the old or new group's duration
2604 * is returned and if the sinks are already playing new data
2605 * or old data. See bug #585969
2607 * While we're at it, also don't do any other queries during
2608 * a group switch or any other event that causes topology changes
2609 * by taking the playbin lock in any case.
2611 GST_PLAY_BIN_LOCK (playbin);
2613 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
2614 GstSourceGroup *group = playbin->curr_group;
2617 GST_SOURCE_GROUP_LOCK (group);
2619 pending = group->pending || group->stream_changed_pending;
2626 gst_query_parse_duration (query, &fmt, NULL);
2627 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2628 if (fmt == playbin->duration[i].format) {
2629 ret = playbin->duration[i].valid;
2630 gst_query_set_duration (query, fmt,
2631 (ret ? playbin->duration[i].duration : -1));
2635 /* if nothing cached yet, we might as well request duration,
2636 * such as during initial startup */
2638 GST_DEBUG_OBJECT (playbin,
2639 "Taking cached duration because of pending group switch: %d", ret);
2640 GST_SOURCE_GROUP_UNLOCK (group);
2641 GST_PLAY_BIN_UNLOCK (playbin);
2645 GST_SOURCE_GROUP_UNLOCK (group);
2648 ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
2650 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
2651 gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
2652 GST_PLAY_BIN_UNLOCK (playbin);
2657 /* mime types we are not handling on purpose right now, don't post a
2658 * missing-plugin message for these */
2659 static const gchar *blacklisted_mimes[] = {
2664 gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
2666 GstPlayBin *playbin = GST_PLAY_BIN (bin);
2667 GstSourceGroup *group;
2669 if (gst_is_missing_plugin_message (msg)) {
2673 detail = gst_missing_plugin_message_get_installer_detail (msg);
2674 for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
2675 if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
2676 GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
2677 gst_message_unref (msg);
2683 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_START ||
2684 GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE) {
2685 GstObject *src = GST_OBJECT_CAST (msg->src);
2687 /* Ignore async state changes from the uridecodebin children,
2688 * see bug #602000. */
2689 group = playbin->curr_group;
2690 if (src && (group = playbin->curr_group) &&
2691 ((group->uridecodebin && src == GST_OBJECT_CAST (group->uridecodebin))
2692 || (group->suburidecodebin
2693 && src == GST_OBJECT_CAST (group->suburidecodebin)))) {
2694 GST_DEBUG_OBJECT (playbin,
2695 "Ignoring async state change of uridecodebin: %s",
2696 GST_OBJECT_NAME (src));
2697 gst_message_unref (msg);
2700 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
2701 GstSourceGroup *new_group = playbin->curr_group;
2703 new_group->stream_changed_pending = FALSE;
2704 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
2705 /* If we get an error of the subtitle uridecodebin transform
2706 * them into warnings and disable the subtitles */
2707 group = playbin->curr_group;
2708 if (group && group->suburidecodebin) {
2709 if (G_UNLIKELY (gst_object_has_ancestor (msg->src, GST_OBJECT_CAST
2710 (group->suburidecodebin)))) {
2712 gchar *debug = NULL;
2713 GstMessage *new_msg;
2715 gboolean done = FALSE;
2716 GValue item = { 0, };
2718 gst_message_parse_error (msg, &err, &debug);
2719 new_msg = gst_message_new_warning (msg->src, err, debug);
2721 gst_message_unref (msg);
2726 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
2727 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
2728 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
2729 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
2730 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
2732 it = gst_element_iterate_src_pads (group->suburidecodebin);
2733 while (it && !done) {
2735 GstIteratorResult res;
2737 res = gst_iterator_next (it, &item);
2740 case GST_ITERATOR_DONE:
2743 case GST_ITERATOR_OK:
2744 p = g_value_get_object (&item);
2745 pad_removed_cb (NULL, p, group);
2746 g_value_reset (&item);
2749 case GST_ITERATOR_RESYNC:
2750 gst_iterator_resync (it);
2752 case GST_ITERATOR_ERROR:
2757 g_value_unset (&item);
2759 gst_iterator_free (it);
2761 gst_object_ref (group->suburidecodebin);
2762 gst_bin_remove (bin, group->suburidecodebin);
2763 gst_element_set_locked_state (group->suburidecodebin, FALSE);
2765 if (group->sub_pending) {
2766 group->sub_pending = FALSE;
2767 no_more_pads_cb (NULL, group);
2774 GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
2778 combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
2779 GstPlayBin * playbin)
2781 const gchar *property;
2782 GstSourceGroup *group;
2783 GstSourceCombine *combine = NULL;
2786 GST_PLAY_BIN_LOCK (playbin);
2787 group = get_group (playbin);
2789 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
2790 if (combiner == G_OBJECT (group->combiner[i].combiner)) {
2791 combine = &group->combiner[i];
2795 /* We got a pad-change after our group got switched out; no need to notify */
2797 GST_PLAY_BIN_UNLOCK (playbin);
2801 switch (combine->type) {
2802 case GST_PLAY_SINK_TYPE_VIDEO:
2803 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2804 property = "current-video";
2805 playbin->current_video = get_current_stream_number (playbin,
2806 combine, group->video_channels);
2808 if (playbin->video_pending_flush_finish) {
2809 playbin->video_pending_flush_finish = FALSE;
2810 GST_PLAY_BIN_UNLOCK (playbin);
2811 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
2812 "playsink-custom-video-flush-finish");
2816 case GST_PLAY_SINK_TYPE_AUDIO:
2817 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2818 property = "current-audio";
2819 playbin->current_audio = get_current_stream_number (playbin,
2820 combine, group->audio_channels);
2822 if (playbin->audio_pending_flush_finish) {
2823 playbin->audio_pending_flush_finish = FALSE;
2824 GST_PLAY_BIN_UNLOCK (playbin);
2825 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
2826 "playsink-custom-audio-flush-finish");
2830 case GST_PLAY_SINK_TYPE_TEXT:
2831 property = "current-text";
2832 playbin->current_text = get_current_stream_number (playbin,
2833 combine, group->text_channels);
2835 if (playbin->text_pending_flush_finish) {
2836 playbin->text_pending_flush_finish = FALSE;
2837 GST_PLAY_BIN_UNLOCK (playbin);
2838 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
2839 "playsink-custom-subtitle-flush-finish");
2846 GST_PLAY_BIN_UNLOCK (playbin);
2850 g_object_notify (G_OBJECT (playbin), property);
2853 static GstPadProbeReturn
2854 _uridecodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
2856 GstPadProbeReturn ret = GST_PAD_PROBE_OK;
2857 GstSourceGroup *group = udata;
2858 GstEvent *event = GST_PAD_PROBE_INFO_DATA (info);
2859 gboolean suburidecodebin = (GST_PAD_PARENT (pad) == group->suburidecodebin);
2861 if (suburidecodebin) {
2862 /* Drop flushes that we caused from the suburidecodebin */
2863 switch (GST_EVENT_TYPE (event)) {
2864 case GST_EVENT_FLUSH_START:
2865 case GST_EVENT_FLUSH_STOP:
2867 guint32 seqnum = gst_event_get_seqnum (event);
2868 GSList *item = g_slist_find (group->suburi_flushes_to_drop,
2869 GUINT_TO_POINTER (seqnum));
2871 if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
2872 group->suburi_flushes_to_drop =
2873 g_slist_delete_link (group->suburi_flushes_to_drop, item);
2882 switch (GST_EVENT_TYPE (event)) {
2883 case GST_EVENT_STREAM_START:{
2886 GST_SOURCE_GROUP_LOCK (group);
2887 if (gst_event_parse_group_id (event, &group_id)) {
2888 if (group->have_group_id) {
2889 if (group->group_id != group_id) {
2890 event = gst_event_copy (event);
2891 gst_event_set_group_id (event, group->group_id);
2892 gst_event_replace ((GstEvent **) & info->data, event);
2893 gst_event_unref (event);
2896 group->group_id = group_id;
2897 group->have_group_id = TRUE;
2900 GST_FIXME_OBJECT (pad,
2901 "Consider implementing group-id handling on stream-start event");
2903 if (!group->have_group_id) {
2904 group->group_id = gst_util_group_id_next ();
2905 group->have_group_id = TRUE;
2908 event = gst_event_copy (event);
2909 gst_event_set_group_id (event, group->group_id);
2910 gst_event_replace ((GstEvent **) & info->data, event);
2911 gst_event_unref (event);
2913 GST_SOURCE_GROUP_UNLOCK (group);
2923 /* helper function to lookup stuff in lists */
2925 array_has_value (const gchar * values[], const gchar * value, gboolean exact)
2929 for (i = 0; values[i]; i++) {
2930 if (exact && !strcmp (value, values[i]))
2932 if (!exact && g_str_has_prefix (value, values[i]))
2940 GstPlayBin *playbin;
2942 GstPlaySinkType type;
2946 notify_tags_cb (GObject * object, GParamSpec * pspec, gpointer user_data)
2948 NotifyTagsData *ntdata = (NotifyTagsData *) user_data;
2951 GST_DEBUG_OBJECT (ntdata->playbin, "Tags on pad %" GST_PTR_FORMAT
2952 " with stream id %d and type %d have changed",
2953 object, ntdata->stream_id, ntdata->type);
2955 switch (ntdata->type) {
2956 case GST_PLAY_SINK_TYPE_VIDEO:
2957 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
2958 signal = SIGNAL_VIDEO_TAGS_CHANGED;
2960 case GST_PLAY_SINK_TYPE_AUDIO:
2961 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
2962 signal = SIGNAL_AUDIO_TAGS_CHANGED;
2964 case GST_PLAY_SINK_TYPE_TEXT:
2965 signal = SIGNAL_TEXT_TAGS_CHANGED;
2973 g_signal_emit (G_OBJECT (ntdata->playbin), gst_play_bin_signals[signal], 0,
2977 /* this function is called when a new pad is added to decodebin. We check the
2978 * type of the pad and add it to the combiner element of the group.
2981 pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
2983 GstPlayBin *playbin;
2985 const GstStructure *s;
2988 GstPadLinkReturn res;
2989 GstSourceCombine *combine = NULL;
2991 gboolean changed = FALSE;
2992 GstElement *custom_combiner = NULL;
2993 gulong group_id_probe_handler;
2995 playbin = group->playbin;
2997 caps = gst_pad_query_caps (pad, NULL);
2998 s = gst_caps_get_structure (caps, 0);
2999 name = gst_structure_get_name (s);
3001 GST_DEBUG_OBJECT (playbin,
3002 "pad %s:%s with caps %" GST_PTR_FORMAT " added in group %p",
3003 GST_DEBUG_PAD_NAME (pad), caps, group);
3005 /* major type of the pad, this determines the combiner to use,
3006 try exact match first */
3007 for (pass = 0; !combine && pass < 2; pass++) {
3008 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3009 if (array_has_value (group->combiner[i].media_list, name, pass == 0)) {
3010 combine = &group->combiner[i];
3012 } else if (group->combiner[i].get_media_caps) {
3013 GstCaps *media_caps = group->combiner[i].get_media_caps ();
3015 if (media_caps && gst_caps_can_intersect (media_caps, caps)) {
3016 combine = &group->combiner[i];
3017 gst_caps_unref (media_caps);
3020 gst_caps_unref (media_caps);
3023 /* get custom stream combiner if there is one */
3025 if (i == PLAYBIN_STREAM_AUDIO) {
3026 custom_combiner = playbin->audio_stream_combiner;
3027 } else if (i == PLAYBIN_STREAM_TEXT) {
3028 custom_combiner = playbin->text_stream_combiner;
3029 } else if (i == PLAYBIN_STREAM_VIDEO) {
3030 custom_combiner = playbin->video_stream_combiner;
3034 /* no combiner found for the media type, don't bother linking it to a
3035 * combiner. This will leave the pad unlinked and thus ignored. */
3036 if (combine == NULL)
3039 GST_SOURCE_GROUP_LOCK (group);
3040 if (combine->combiner == NULL && playbin->have_selector) {
3041 /* no combiner, create one */
3042 GST_DEBUG_OBJECT (playbin, "creating new input selector");
3043 if (custom_combiner)
3044 combine->combiner = custom_combiner;
3046 combine->combiner = gst_element_factory_make ("input-selector", NULL);
3048 if (combine->combiner == NULL) {
3049 /* post the missing input-selector message only once */
3050 playbin->have_selector = FALSE;
3051 gst_element_post_message (GST_ELEMENT_CAST (playbin),
3052 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
3054 GST_ELEMENT_WARNING (playbin, CORE, MISSING_PLUGIN,
3055 (_("Missing element '%s' - check your GStreamer installation."),
3056 "input-selector"), (NULL));
3058 /* find out which properties the stream combiner supports */
3059 combine->has_active_pad =
3060 g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
3061 "active-pad") != NULL;
3063 if (!custom_combiner) {
3064 /* sync-mode=1, use clock */
3065 if (combine->type == GST_PLAY_SINK_TYPE_TEXT)
3066 g_object_set (combine->combiner, "sync-streams", TRUE,
3067 "sync-mode", 1, "cache-buffers", TRUE, NULL);
3069 g_object_set (combine->combiner, "sync-streams", TRUE, NULL);
3072 if (combine->has_active_pad)
3073 g_signal_connect (combine->combiner, "notify::active-pad",
3074 G_CALLBACK (combiner_active_pad_changed), playbin);
3076 GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p",
3078 gst_element_set_state (combine->combiner, GST_STATE_PAUSED);
3079 gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
3083 if (combine->srcpad == NULL) {
3084 if (combine->combiner) {
3085 /* save source pad of the combiner */
3086 combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
3088 /* no combiner, use the pad as the source pad then */
3089 combine->srcpad = gst_object_ref (pad);
3092 /* block the combiner srcpad. It's possible that multiple decodebins start
3093 * pushing data into the combiners before we have a chance to collect all
3094 * streams and connect the sinks, resulting in not-linked errors. After we
3095 * configured the sinks we will unblock them all. */
3096 GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad);
3098 gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3102 /* get sinkpad for the new stream */
3103 if (combine->combiner) {
3104 if ((sinkpad = gst_element_get_request_pad (combine->combiner, "sink_%u"))) {
3106 GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner",
3107 GST_DEBUG_PAD_NAME (sinkpad));
3109 /* find out which properties the sink pad supports */
3110 combine->has_always_ok =
3111 g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
3112 "always-ok") != NULL;
3114 g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
3117 /* store the combiner for the pad */
3118 g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine);
3120 if (combine->has_tags) {
3121 gulong notify_tags_handler = 0;
3122 NotifyTagsData *ntdata;
3124 /* connect to the notify::tags signal for our
3125 * own *-tags-changed signals
3127 ntdata = g_new0 (NotifyTagsData, 1);
3128 ntdata->playbin = playbin;
3129 ntdata->stream_id = combine->channels->len;
3130 ntdata->type = combine->type;
3132 notify_tags_handler =
3133 g_signal_connect_data (G_OBJECT (sinkpad), "notify::tags",
3134 G_CALLBACK (notify_tags_cb), ntdata, (GClosureNotify) g_free,
3136 g_object_set_data (G_OBJECT (sinkpad), "playbin.notify_tags_handler",
3137 (gpointer) (guintptr) notify_tags_handler);
3140 /* store the pad in the array */
3141 GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
3142 g_ptr_array_add (combine->channels, sinkpad);
3144 res = gst_pad_link (pad, sinkpad);
3145 if (GST_PAD_LINK_FAILED (res))
3148 /* store combiner pad so we can release it */
3149 g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
3152 GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p",
3153 GST_DEBUG_PAD_NAME (pad), combine->combiner);
3155 goto request_pad_failed;
3158 /* no combiner, don't configure anything, we'll link the new pad directly to
3163 /* store the combiner for the pad */
3164 g_object_set_data (G_OBJECT (pad), "playbin.combine", combine);
3166 GST_SOURCE_GROUP_UNLOCK (group);
3168 group_id_probe_handler =
3169 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
3170 _uridecodebin_event_probe, group, NULL);
3171 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id",
3172 (gpointer) group_id_probe_handler);
3177 switch (combine->type) {
3178 case GST_PLAY_SINK_TYPE_VIDEO:
3179 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3180 signal = SIGNAL_VIDEO_CHANGED;
3182 case GST_PLAY_SINK_TYPE_AUDIO:
3183 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3184 signal = SIGNAL_AUDIO_CHANGED;
3186 case GST_PLAY_SINK_TYPE_TEXT:
3187 signal = SIGNAL_TEXT_CHANGED;
3194 /* we want to return NOT_LINKED for unselected pads but only for pads
3195 * from the normal uridecodebin. This makes sure that subtitle streams
3196 * are not raced past audio/video from decodebin's multiqueue.
3197 * For pads from suburidecodebin OK should always be returned, otherwise
3198 * it will most likely stop. */
3199 if (combine->has_always_ok) {
3200 gboolean always_ok = (decodebin == group->suburidecodebin);
3201 g_object_set (sinkpad, "always-ok", always_ok, NULL);
3203 g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0, NULL);
3208 gst_caps_unref (caps);
3214 GST_ERROR_OBJECT (playbin, "unknown type %s for pad %s:%s",
3215 name, GST_DEBUG_PAD_NAME (pad));
3220 GST_ERROR_OBJECT (playbin,
3221 "failed to link pad %s:%s to combiner, reason %d",
3222 GST_DEBUG_PAD_NAME (pad), res);
3223 GST_SOURCE_GROUP_UNLOCK (group);
3227 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3228 ("Internal playbin error."),
3229 ("Failed to get request pad from combiner %p.", combine->combiner));
3230 GST_SOURCE_GROUP_UNLOCK (group);
3234 /* called when a pad is removed from the uridecodebin. We unlink the pad from
3235 * the combiner. This will make the combiner select a new pad. */
3237 pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
3239 GstPlayBin *playbin;
3241 GstElement *combiner;
3242 GstSourceCombine *combine;
3244 gulong group_id_probe_handler;
3246 playbin = group->playbin;
3248 GST_DEBUG_OBJECT (playbin,
3249 "pad %s:%s removed from group %p", GST_DEBUG_PAD_NAME (pad), group);
3251 GST_SOURCE_GROUP_LOCK (group);
3253 if ((group_id_probe_handler =
3254 (guintptr) g_object_get_data (G_OBJECT (pad),
3255 "playbin.event_probe_id"))) {
3256 gst_pad_remove_probe (pad, group_id_probe_handler);
3257 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", 0);
3260 if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) {
3261 g_assert (combine->combiner == NULL);
3262 g_assert (combine->srcpad == pad);
3263 gst_object_unref (pad);
3264 combine->srcpad = NULL;
3268 /* get the combiner sinkpad */
3269 if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad")))
3272 if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) {
3273 if (combine->has_tags) {
3274 gulong notify_tags_handler;
3276 notify_tags_handler =
3277 (guintptr) g_object_get_data (G_OBJECT (peer),
3278 "playbin.notify_tags_handler");
3279 if (notify_tags_handler != 0)
3280 g_signal_handler_disconnect (G_OBJECT (peer), notify_tags_handler);
3281 g_object_set_data (G_OBJECT (peer), "playbin.notify_tags_handler", NULL);
3284 /* remove the pad from the array */
3285 g_ptr_array_remove (combine->channels, peer);
3286 GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer);
3288 /* get the correct type-changed signal */
3289 switch (combine->type) {
3290 case GST_PLAY_SINK_TYPE_VIDEO:
3291 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3292 signal = SIGNAL_VIDEO_CHANGED;
3294 case GST_PLAY_SINK_TYPE_AUDIO:
3295 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3296 signal = SIGNAL_AUDIO_CHANGED;
3298 case GST_PLAY_SINK_TYPE_TEXT:
3299 signal = SIGNAL_TEXT_CHANGED;
3305 if (!combine->channels->len && combine->combiner) {
3306 GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed");
3307 GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner);
3308 gst_object_unref (combine->srcpad);
3309 combine->srcpad = NULL;
3310 gst_element_set_state (combine->combiner, GST_STATE_NULL);
3311 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
3312 combine->combiner = NULL;
3316 /* unlink the pad now (can fail, the pad is unlinked before it's removed) */
3317 gst_pad_unlink (pad, peer);
3319 /* get combiner, this can be NULL when the element is removing the pads
3320 * because it's being disposed. */
3321 combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer));
3323 gst_object_unref (peer);
3327 /* release the pad to the combiner, this will make the combiner choose a new
3329 gst_element_release_request_pad (combiner, peer);
3330 gst_object_unref (peer);
3332 gst_object_unref (combiner);
3334 GST_SOURCE_GROUP_UNLOCK (group);
3337 g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0, NULL);
3344 GST_DEBUG_OBJECT (playbin, "pad not linked");
3349 GST_DEBUG_OBJECT (playbin, "combiner not found");
3354 /* we get called when all pads are available and we must connect the sinks to
3356 * The main purpose of the code is to see if we have video/audio and subtitles
3357 * and pick the right pipelines to display them.
3359 * The combiners installed on the group tell us about the presence of
3360 * audio/video and subtitle streams. This allows us to see if we need
3361 * visualisation, video or/and audio.
3364 no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
3366 GstPlayBin *playbin;
3367 GstPadLinkReturn res;
3371 playbin = group->playbin;
3373 GST_DEBUG_OBJECT (playbin, "no more pads in group %p", group);
3375 GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
3377 GST_SOURCE_GROUP_LOCK (group);
3378 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3379 GstSourceCombine *combine = &group->combiner[i];
3381 /* check if the specific media type was detected and thus has a combiner
3382 * created for it. If there is the media type, get a sinkpad from the sink
3383 * and link it. We only do this if we have not yet requested the sinkpad
3385 if (combine->srcpad && combine->sinkpad == NULL) {
3386 GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type);
3388 gst_play_sink_request_pad (playbin->playsink, combine->type);
3389 } else if (combine->srcpad && combine->sinkpad) {
3390 GST_DEBUG_OBJECT (playbin, "refreshing new sink pad %d", combine->type);
3391 gst_play_sink_refresh_pad (playbin->playsink, combine->sinkpad,
3393 } else if (combine->sinkpad && combine->srcpad == NULL) {
3394 GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type);
3395 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
3396 combine->sinkpad = NULL;
3398 if (combine->sinkpad && combine->srcpad &&
3399 !gst_pad_is_linked (combine->srcpad)) {
3400 res = gst_pad_link (combine->srcpad, combine->sinkpad);
3401 GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
3402 combine->media_list[0], res);
3403 if (res != GST_PAD_LINK_OK) {
3404 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3405 ("Internal playbin error."),
3406 ("Failed to link combiner to sink. Error %d", res));
3410 GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
3411 group->pending - 1);
3413 if (group->pending > 0)
3416 if (group->suburidecodebin == decodebin)
3417 group->sub_pending = FALSE;
3419 if (group->pending == 0) {
3420 /* we are the last group to complete, we will configure the output and then
3421 * signal the other waiters. */
3422 GST_LOG_OBJECT (playbin, "last group complete");
3425 GST_LOG_OBJECT (playbin, "have more pending groups");
3428 GST_SOURCE_GROUP_UNLOCK (group);
3431 /* if we have custom sinks, configure them now */
3432 GST_SOURCE_GROUP_LOCK (group);
3434 if (group->audio_sink) {
3435 GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
3437 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
3441 if (group->video_sink) {
3442 GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
3444 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
3448 if (group->text_sink) {
3449 GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
3451 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
3455 GST_SOURCE_GROUP_UNLOCK (group);
3457 /* signal the other decodebins that they can continue now. */
3458 GST_SOURCE_GROUP_LOCK (group);
3459 /* unblock all combiners */
3460 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3461 GstSourceCombine *combine = &group->combiner[i];
3463 if (combine->srcpad) {
3464 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
3466 if (combine->block_id) {
3467 gst_pad_remove_probe (combine->srcpad, combine->block_id);
3468 combine->block_id = 0;
3472 GST_SOURCE_GROUP_UNLOCK (group);
3473 gst_play_sink_reconfigure (playbin->playsink);
3476 GST_PLAY_BIN_SHUTDOWN_UNLOCK (playbin);
3482 GST_DEBUG ("ignoring, we are shutting down");
3483 /* Request a flushing pad from playsink that we then link to the combiner.
3484 * Then we unblock the combiners so that they stop with a WRONG_STATE
3485 * instead of a NOT_LINKED error.
3487 GST_SOURCE_GROUP_LOCK (group);
3488 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3489 GstSourceCombine *combine = &group->combiner[i];
3491 if (combine->srcpad) {
3492 if (combine->sinkpad == NULL) {
3493 GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad");
3495 gst_play_sink_request_pad (playbin->playsink,
3496 GST_PLAY_SINK_TYPE_FLUSHING);
3497 res = gst_pad_link (combine->srcpad, combine->sinkpad);
3498 GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res);
3500 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
3502 if (combine->block_id) {
3503 gst_pad_remove_probe (combine->srcpad, combine->block_id);
3504 combine->block_id = 0;
3508 GST_SOURCE_GROUP_UNLOCK (group);
3514 drained_cb (GstElement * decodebin, GstSourceGroup * group)
3516 GstPlayBin *playbin;
3518 playbin = group->playbin;
3520 GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
3522 /* after this call, we should have a next group to activate or we EOS */
3523 g_signal_emit (G_OBJECT (playbin),
3524 gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
3526 /* now activate the next group. If the app did not set a uri, this will
3527 * fail and we can do EOS */
3528 setup_next_source (playbin, GST_STATE_PAUSED);
3531 /* Like gst_element_factory_can_sink_any_caps() but doesn't
3532 * allow ANY caps on the sinkpad template */
3534 _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
3536 const GList *templs;
3538 templs = gst_element_factory_get_static_pad_templates (factory);
3541 GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data;
3543 if (templ->direction == GST_PAD_SINK) {
3544 GstCaps *templcaps = gst_static_caps_get (&templ->static_caps);
3546 if (!gst_caps_is_any (templcaps)
3547 && gst_caps_is_subset (caps, templcaps)) {
3548 gst_caps_unref (templcaps);
3551 gst_caps_unref (templcaps);
3553 templs = g_list_next (templs);
3560 avelements_free (gpointer avelement)
3562 GstAVElement *elm = (GstAVElement *) avelement;
3565 gst_object_unref (elm->dec);
3567 gst_object_unref (elm->sink);
3568 g_slice_free (GstAVElement, elm);
3572 avelement_compare_decoder (gconstpointer p1, gconstpointer p2,
3575 GstAVElement *v1, *v2;
3577 v1 = (GstAVElement *) p1;
3578 v2 = (GstAVElement *) p2;
3580 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (v2->dec));
3584 avelement_lookup_decoder (gconstpointer p1, gconstpointer p2,
3588 GstElementFactory *f2;
3590 v1 = (GstAVElement *) p1;
3591 f2 = (GstElementFactory *) p2;
3593 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (f2));
3597 avelement_compare (gconstpointer p1, gconstpointer p2)
3599 GstAVElement *v1, *v2;
3600 GstPluginFeature *fd1, *fd2, *fs1, *fs2;
3601 gint64 diff, v1_rank, v2_rank;
3603 v1 = (GstAVElement *) p1;
3604 v2 = (GstAVElement *) p2;
3606 fd1 = (GstPluginFeature *) v1->dec;
3607 fd2 = (GstPluginFeature *) v2->dec;
3609 /* If both have a sink, we also compare their ranks */
3610 if (v1->sink && v2->sink) {
3611 fs1 = (GstPluginFeature *) v1->sink;
3612 fs2 = (GstPluginFeature *) v2->sink;
3614 gst_plugin_feature_get_rank (fd1) * gst_plugin_feature_get_rank (fs1);
3616 gst_plugin_feature_get_rank (fd2) * gst_plugin_feature_get_rank (fs2);
3618 v1_rank = gst_plugin_feature_get_rank (fd1);
3619 v2_rank = gst_plugin_feature_get_rank (fd2);
3623 /* comparison based on the rank */
3624 diff = v2_rank - v1_rank;
3630 /* comparison based on number of common caps features */
3631 diff = v2->n_comm_cf - v1->n_comm_cf;
3636 /* comparison based on the name of sink elements */
3637 diff = strcmp (GST_OBJECT_NAME (fs1), GST_OBJECT_NAME (fs2));
3642 /* comparison based on the name of decoder elements */
3643 return strcmp (GST_OBJECT_NAME (fd1), GST_OBJECT_NAME (fd2));
3646 /* unref the caps after usage */
3648 get_template_caps (GstElementFactory * factory, GstPadDirection direction)
3650 const GList *templates;
3651 GstStaticPadTemplate *templ = NULL;
3654 templates = gst_element_factory_get_static_pad_templates (factory);
3655 for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
3657 if (templ->direction == direction)
3661 return gst_static_caps_get (&templ->static_caps);
3667 is_included (GList * list, GstCapsFeatures * cf)
3669 for (; list; list = list->next) {
3670 if (gst_caps_features_is_equal ((GstCapsFeatures *) list->data, cf))
3676 /* compute the number of common caps features */
3678 get_n_common_capsfeatures (GstPlayBin * playbin, GstElementFactory * dec,
3679 GstElementFactory * sink, gboolean isaudioelement)
3681 GstCaps *d_tmpl_caps, *s_tmpl_caps;
3682 GstCapsFeatures *d_features, *s_features;
3683 GstStructure *d_struct, *s_struct;
3684 GList *cf_list = NULL;
3685 guint d_caps_size, s_caps_size;
3686 guint i, j, n_common_cf = 0;
3688 (isaudioelement) ? gst_static_caps_get (&raw_audio_caps) :
3689 gst_static_caps_get (&raw_video_caps);
3690 GstStructure *raw_struct = gst_caps_get_structure (raw_caps, 0);
3691 GstPlayFlags flags = gst_play_bin_get_flags (playbin);
3692 gboolean native_raw =
3693 (isaudioelement ? ! !(flags & GST_PLAY_FLAG_NATIVE_AUDIO) : ! !(flags &
3694 GST_PLAY_FLAG_NATIVE_VIDEO));
3696 d_tmpl_caps = get_template_caps (dec, GST_PAD_SRC);
3697 s_tmpl_caps = get_template_caps (sink, GST_PAD_SINK);
3699 if (!d_tmpl_caps || !s_tmpl_caps) {
3700 GST_ERROR ("Failed to get template caps from decoder or sink");
3704 d_caps_size = gst_caps_get_size (d_tmpl_caps);
3705 s_caps_size = gst_caps_get_size (s_tmpl_caps);
3707 for (i = 0; i < d_caps_size; i++) {
3708 d_features = gst_caps_get_features ((const GstCaps *) d_tmpl_caps, i);
3709 d_struct = gst_caps_get_structure ((const GstCaps *) d_tmpl_caps, i);
3710 for (j = 0; j < s_caps_size; j++) {
3712 s_features = gst_caps_get_features ((const GstCaps *) s_tmpl_caps, j);
3713 s_struct = gst_caps_get_structure ((const GstCaps *) s_tmpl_caps, j);
3715 /* A common caps feature is given if the caps features are equal
3716 * and the structures can intersect. If the NATIVE_AUDIO/NATIVE_VIDEO
3717 * flags are not set we also allow if both structures are raw caps with
3718 * system memory caps features, because in that case we have converters in
3721 if (gst_caps_features_is_equal (d_features, s_features) &&
3722 (gst_structure_can_intersect (d_struct, s_struct) ||
3724 && gst_caps_features_is_equal (d_features,
3725 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)
3726 && gst_structure_can_intersect (raw_struct, d_struct)
3727 && gst_structure_can_intersect (raw_struct, s_struct)))
3728 && !is_included (cf_list, s_features)) {
3729 cf_list = g_list_prepend (cf_list, s_features);
3735 g_list_free (cf_list);
3737 gst_caps_unref (d_tmpl_caps);
3738 gst_caps_unref (s_tmpl_caps);
3744 avelements_create (GstPlayBin * playbin, gboolean isaudioelement)
3746 GstElementFactory *d_factory, *s_factory;
3747 GList *dec_list, *sink_list, *dl, *sl;
3748 GSequence *ave_seq = NULL;
3750 guint n_common_cf = 0;
3752 if (isaudioelement) {
3753 sink_list = gst_element_factory_list_get_elements
3754 (GST_ELEMENT_FACTORY_TYPE_SINK |
3755 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
3757 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
3758 | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
3760 sink_list = gst_element_factory_list_get_elements
3761 (GST_ELEMENT_FACTORY_TYPE_SINK |
3762 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3763 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
3766 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
3767 | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3768 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
3771 /* create a list of audio/video elements. Each element in the list
3772 * is holding an audio/video decoder and an auido/video sink in which
3773 * the decoders srcpad template caps and sink element's sinkpad template
3774 * caps are compatible */
3778 ave_seq = g_sequence_new ((GDestroyNotify) avelements_free);
3780 for (; dl; dl = dl->next) {
3781 d_factory = (GstElementFactory *) dl->data;
3782 for (; sl; sl = sl->next) {
3783 s_factory = (GstElementFactory *) sl->data;
3786 get_n_common_capsfeatures (playbin, d_factory, s_factory,
3788 if (n_common_cf < 1)
3791 ave = g_slice_new (GstAVElement);
3792 ave->dec = gst_object_ref (d_factory);
3793 ave->sink = gst_object_ref (s_factory);
3794 ave->n_comm_cf = n_common_cf;
3795 g_sequence_append (ave_seq, ave);
3799 g_sequence_sort (ave_seq, (GCompareDataFunc) avelement_compare_decoder, NULL);
3801 gst_plugin_feature_list_free (dec_list);
3802 gst_plugin_feature_list_free (sink_list);
3808 avelement_iter_is_equal (GSequenceIter * iter, GstElementFactory * factory)
3815 ave = g_sequence_get (iter);
3819 return strcmp (GST_OBJECT_NAME (ave->dec), GST_OBJECT_NAME (factory)) == 0;
3823 create_decoders_list (GList * factory_list, GSequence * avelements)
3825 GList *dec_list = NULL, *tmp;
3826 GList *ave_list = NULL;
3827 GstAVElement *ave, *best_ave;
3829 g_return_val_if_fail (factory_list != NULL, NULL);
3830 g_return_val_if_fail (avelements != NULL, NULL);
3832 for (tmp = factory_list; tmp; tmp = tmp->next) {
3833 GstElementFactory *factory = (GstElementFactory *) tmp->data;
3835 /* if there are parsers or sink elements, add them first */
3836 if (!gst_element_factory_list_is_type (factory,
3837 GST_ELEMENT_FACTORY_TYPE_DECODER)) {
3838 dec_list = g_list_prepend (dec_list, factory);
3840 GSequenceIter *seq_iter;
3843 g_sequence_lookup (avelements, factory,
3844 (GCompareDataFunc) avelement_lookup_decoder, NULL);
3846 GstAVElement *ave = g_slice_new0 (GstAVElement);
3850 /* There's at least raw */
3853 dec_list = g_list_prepend (dec_list, factory);
3857 /* Go to first iter with that decoder */
3859 GSequenceIter *tmp_seq_iter;
3861 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
3862 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
3864 seq_iter = tmp_seq_iter;
3865 } while (!g_sequence_iter_is_begin (seq_iter));
3867 /* Get the best ranked GstAVElement for that factory */
3869 while (!g_sequence_iter_is_end (seq_iter)
3870 && avelement_iter_is_equal (seq_iter, factory)) {
3871 ave = g_sequence_get (seq_iter);
3873 if (!best_ave || avelement_compare (ave, best_ave) < 0)
3876 seq_iter = g_sequence_iter_next (seq_iter);
3878 ave_list = g_list_prepend (ave_list, best_ave);
3882 /* Sort all GstAVElements by their relative ranks and insert
3883 * into the decoders list */
3884 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
3885 for (tmp = ave_list; tmp; tmp = tmp->next) {
3886 ave = (GstAVElement *) tmp->data;
3887 dec_list = g_list_prepend (dec_list, ave->dec);
3889 g_list_free (ave_list);
3891 dec_list = g_list_reverse (dec_list);
3896 /* Called when we must provide a list of factories to plug to @pad with @caps.
3897 * We first check if we have a sink that can handle the format and if we do, we
3898 * return NULL, to expose the pad. If we have no sink (or the sink does not
3899 * work), we return the list of elements that can connect. */
3900 static GValueArray *
3901 autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
3902 GstCaps * caps, GstSourceGroup * group)
3904 GstPlayBin *playbin;
3905 GList *factory_list, *tmp;
3906 GValueArray *result;
3907 gboolean unref_caps = FALSE;
3908 gboolean isaudiodeclist = FALSE;
3909 gboolean isvideodeclist = FALSE;
3912 caps = gst_caps_new_any ();
3916 playbin = group->playbin;
3918 GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
3919 group, GST_DEBUG_PAD_NAME (pad), caps);
3921 /* filter out the elements based on the caps. */
3922 g_mutex_lock (&playbin->elements_lock);
3923 gst_play_bin_update_elements_list (playbin);
3925 gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
3926 gst_caps_is_fixed (caps));
3927 g_mutex_unlock (&playbin->elements_lock);
3929 GST_DEBUG_OBJECT (playbin, "found factories %p", factory_list);
3930 GST_PLUGIN_FEATURE_LIST_DEBUG (factory_list);
3932 /* check whether the caps are asking for a list of audio/video decoders */
3934 if (!gst_caps_is_any (caps)) {
3935 for (; tmp; tmp = tmp->next) {
3936 GstElementFactory *factory = (GstElementFactory *) tmp->data;
3938 isvideodeclist = gst_element_factory_list_is_type (factory,
3939 GST_ELEMENT_FACTORY_TYPE_DECODER |
3940 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
3941 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
3942 isaudiodeclist = gst_element_factory_list_is_type (factory,
3943 GST_ELEMENT_FACTORY_TYPE_DECODER |
3944 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
3946 if (isaudiodeclist || isvideodeclist)
3951 if (isaudiodeclist || isvideodeclist) {
3952 GSequence **ave_list;
3954 ave_list = &playbin->aelements;
3956 ave_list = &playbin->velements;
3958 g_mutex_lock (&playbin->elements_lock);
3959 /* sort factory_list based on the GstAVElement list priority */
3960 factory_list = create_decoders_list (factory_list, *ave_list);
3961 g_mutex_unlock (&playbin->elements_lock);
3964 /* 2 additional elements for the already set audio/video sinks */
3965 result = g_value_array_new (g_list_length (factory_list) + 2);
3967 /* Check if we already have an audio/video sink and if this is the case
3968 * put it as the first element of the array */
3969 if (group->audio_sink) {
3970 GstElementFactory *factory = gst_element_get_factory (group->audio_sink);
3972 if (factory && _factory_can_sink_caps (factory, caps)) {
3973 GValue val = { 0, };
3975 g_value_init (&val, G_TYPE_OBJECT);
3976 g_value_set_object (&val, factory);
3977 result = g_value_array_append (result, &val);
3978 g_value_unset (&val);
3982 if (group->video_sink) {
3983 GstElementFactory *factory = gst_element_get_factory (group->video_sink);
3985 if (factory && _factory_can_sink_caps (factory, caps)) {
3986 GValue val = { 0, };
3988 g_value_init (&val, G_TYPE_OBJECT);
3989 g_value_set_object (&val, factory);
3990 result = g_value_array_append (result, &val);
3991 g_value_unset (&val);
3995 for (tmp = factory_list; tmp; tmp = tmp->next) {
3996 GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
3997 GValue val = { 0, };
3999 if (group->audio_sink && gst_element_factory_list_is_type (factory,
4000 GST_ELEMENT_FACTORY_TYPE_SINK |
4001 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4004 if (group->video_sink && gst_element_factory_list_is_type (factory,
4005 GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO
4006 | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4010 g_value_init (&val, G_TYPE_OBJECT);
4011 g_value_set_object (&val, factory);
4012 g_value_array_append (result, &val);
4013 g_value_unset (&val);
4015 gst_plugin_feature_list_free (factory_list);
4018 gst_caps_unref (caps);
4024 gst_play_bin_set_context (GstElement * element, GstContext * context)
4026 GstPlayBin *playbin = GST_PLAY_BIN (element);
4028 /* Proxy contexts to the sinks, they might not be in playsink yet */
4029 GST_PLAY_BIN_LOCK (playbin);
4030 if (playbin->audio_sink)
4031 gst_element_set_context (playbin->audio_sink, context);
4032 if (playbin->video_sink)
4033 gst_element_set_context (playbin->video_sink, context);
4034 if (playbin->text_sink)
4035 gst_element_set_context (playbin->text_sink, context);
4037 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
4039 if (playbin->curr_group->audio_sink)
4040 gst_element_set_context (playbin->curr_group->audio_sink, context);
4041 if (playbin->curr_group->video_sink)
4042 gst_element_set_context (playbin->curr_group->video_sink, context);
4043 if (playbin->curr_group->text_sink)
4044 gst_element_set_context (playbin->curr_group->text_sink, context);
4046 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4047 GST_PLAY_BIN_UNLOCK (playbin);
4049 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
4052 /* Pass sink messages to the application, e.g. NEED_CONTEXT messages */
4054 gst_play_bin_update_context (GstPlayBin * playbin, GstContext * context)
4057 const gchar *context_type;
4059 GST_OBJECT_LOCK (playbin);
4060 context_type = gst_context_get_context_type (context);
4061 for (l = playbin->contexts; l; l = l->next) {
4062 GstContext *tmp = l->data;
4063 const gchar *tmp_type = gst_context_get_context_type (tmp);
4065 /* Always store newest context but never replace
4066 * a persistent one by a non-persistent one */
4067 if (strcmp (context_type, tmp_type) == 0 &&
4068 (gst_context_is_persistent (context) ||
4069 !gst_context_is_persistent (tmp))) {
4070 gst_context_replace ((GstContext **) & l->data, context);
4074 /* Not found? Add */
4077 g_list_prepend (playbin->contexts, gst_context_ref (context));
4078 GST_OBJECT_UNLOCK (playbin);
4081 static GstBusSyncReply
4082 activate_sink_bus_handler (GstBus * bus, GstMessage * msg, GstPlayBin * playbin)
4084 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
4085 /* Only proxy errors from a fixed sink. If that fails we can just error out
4086 * early as stuff will fail later anyway */
4087 if (playbin->audio_sink
4088 && gst_object_has_ancestor (GST_MESSAGE_SRC (msg),
4089 GST_OBJECT_CAST (playbin->audio_sink)))
4090 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4091 else if (playbin->video_sink
4092 && gst_object_has_ancestor (GST_MESSAGE_SRC (msg),
4093 GST_OBJECT_CAST (playbin->video_sink)))
4094 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4095 else if (playbin->text_sink
4096 && gst_object_has_ancestor (GST_MESSAGE_SRC (msg),
4097 GST_OBJECT_CAST (playbin->text_sink)))
4098 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4100 gst_message_unref (msg);
4101 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
4102 const gchar *context_type;
4105 gst_message_parse_context_type (msg, &context_type);
4106 GST_OBJECT_LOCK (playbin);
4107 for (l = playbin->contexts; l; l = l->next) {
4108 GstContext *tmp = l->data;
4109 const gchar *tmp_type = gst_context_get_context_type (tmp);
4111 if (strcmp (context_type, tmp_type) == 0) {
4112 gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
4116 GST_OBJECT_UNLOCK (playbin);
4118 /* Forward if we couldn't answer the message */
4120 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4122 gst_message_unref (msg);
4124 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
4125 GstContext *context;
4127 gst_message_parse_have_context (msg, &context);
4128 gst_play_bin_update_context (playbin, context);
4129 gst_context_unref (context);
4131 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4133 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4136 /* Doesn't really matter, nothing is using this bus */
4137 return GST_BUS_DROP;
4141 activate_sink (GstPlayBin * playbin, GstElement * sink, gboolean * activated)
4145 GstStateChangeReturn sret;
4146 gboolean ret = FALSE;
4151 GST_OBJECT_LOCK (sink);
4152 state = GST_STATE (sink);
4153 GST_OBJECT_UNLOCK (sink);
4154 if (state >= GST_STATE_READY) {
4159 if (!GST_OBJECT_PARENT (sink)) {
4160 bus = gst_bus_new ();
4161 gst_bus_set_sync_handler (bus,
4162 (GstBusSyncHandler) activate_sink_bus_handler, playbin, NULL);
4163 gst_element_set_bus (sink, bus);
4166 sret = gst_element_set_state (sink, GST_STATE_READY);
4167 if (sret == GST_STATE_CHANGE_FAILURE)
4176 gst_element_set_bus (sink, NULL);
4177 gst_object_unref (bus);
4183 /* autoplug-continue decides, if a pad has raw caps that can be exposed
4184 * directly or if further decoding is necessary. We use this to expose
4185 * supported subtitles directly */
4187 /* FIXME 0.11: Remove the checks for ANY caps, a sink should specify
4188 * explicitly the caps it supports and if it claims to support ANY
4189 * caps it really should support everything */
4191 autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps,
4192 GstSourceGroup * group)
4194 gboolean ret = TRUE;
4196 GstPad *sinkpad = NULL;
4198 GST_SOURCE_GROUP_LOCK (group);
4200 if ((sink = group->text_sink))
4201 sinkpad = gst_element_get_static_pad (sink, "sink");
4205 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4206 if (!gst_caps_is_any (sinkcaps))
4207 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4208 gst_caps_unref (sinkcaps);
4209 gst_object_unref (sinkpad);
4211 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4212 ret = !gst_caps_is_subset (caps, subcaps);
4213 gst_caps_unref (subcaps);
4215 /* If autoplugging can stop don't do additional checks */
4219 /* If this is from the subtitle uridecodebin we don't need to
4220 * check the audio and video sink */
4221 if (group->suburidecodebin
4222 && gst_object_has_ancestor (GST_OBJECT_CAST (element),
4223 GST_OBJECT_CAST (group->suburidecodebin)))
4226 if ((sink = group->audio_sink)) {
4227 sinkpad = gst_element_get_static_pad (sink, "sink");
4231 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4232 if (!gst_caps_is_any (sinkcaps))
4233 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4234 gst_caps_unref (sinkcaps);
4235 gst_object_unref (sinkpad);
4241 if ((sink = group->video_sink)) {
4242 sinkpad = gst_element_get_static_pad (sink, "sink");
4246 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4247 if (!gst_caps_is_any (sinkcaps))
4248 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4249 gst_caps_unref (sinkcaps);
4250 gst_object_unref (sinkpad);
4255 GST_SOURCE_GROUP_UNLOCK (group);
4257 GST_DEBUG_OBJECT (group->playbin,
4258 "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d",
4259 group, GST_DEBUG_PAD_NAME (pad), caps, ret);
4265 sink_accepts_caps (GstPlayBin * playbin, GstElement * sink, GstCaps * caps)
4269 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4270 /* Got the sink pad, now let's see if the element actually does accept the
4271 * caps that we have */
4272 if (!gst_pad_query_accept_caps (sinkpad, caps)) {
4273 gst_object_unref (sinkpad);
4276 gst_object_unref (sinkpad);
4282 /* We are asked to select an element. See if the next element to check
4283 * is a sink. If this is the case, we see if the sink works by setting it to
4284 * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
4285 * expose the raw pad so that we can setup the mixers. */
4286 static GstAutoplugSelectResult
4287 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
4288 GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
4290 GstPlayBin *playbin;
4291 GstElement *element;
4293 GstPlaySinkType type;
4295 GList *ave_list = NULL, *l;
4296 GstAVElement *ave = NULL;
4297 GSequence *ave_seq = NULL;
4298 GSequenceIter *seq_iter;
4300 playbin = group->playbin;
4302 GST_DEBUG_OBJECT (playbin, "select group %p for %s:%s, %" GST_PTR_FORMAT,
4303 group, GST_DEBUG_PAD_NAME (pad), caps);
4305 GST_DEBUG_OBJECT (playbin, "checking factory %s", GST_OBJECT_NAME (factory));
4307 /* if it's not a sink, we make sure the element is compatible with
4309 if (!gst_element_factory_list_is_type (factory,
4310 GST_ELEMENT_FACTORY_TYPE_SINK)) {
4311 gboolean isvideodec = gst_element_factory_list_is_type (factory,
4312 GST_ELEMENT_FACTORY_TYPE_DECODER |
4313 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4314 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
4315 gboolean isaudiodec = gst_element_factory_list_is_type (factory,
4316 GST_ELEMENT_FACTORY_TYPE_DECODER |
4317 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
4319 if (!isvideodec && !isaudiodec)
4320 return GST_AUTOPLUG_SELECT_TRY;
4322 GST_SOURCE_GROUP_LOCK (group);
4323 g_mutex_lock (&playbin->elements_lock);
4326 ave_seq = playbin->aelements;
4327 sinkp = &group->audio_sink;
4329 ave_seq = playbin->velements;
4330 sinkp = &group->video_sink;
4334 g_sequence_lookup (ave_seq, factory,
4335 (GCompareDataFunc) avelement_lookup_decoder, NULL);
4337 /* Go to first iter with that decoder */
4339 GSequenceIter *tmp_seq_iter;
4341 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
4342 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
4344 seq_iter = tmp_seq_iter;
4345 } while (!g_sequence_iter_is_begin (seq_iter));
4347 while (!g_sequence_iter_is_end (seq_iter)
4348 && avelement_iter_is_equal (seq_iter, factory)) {
4349 ave = g_sequence_get (seq_iter);
4350 ave_list = g_list_prepend (ave_list, ave);
4351 seq_iter = g_sequence_iter_next (seq_iter);
4354 /* Sort all GstAVElements by their relative ranks and insert
4355 * into the decoders list */
4356 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
4358 ave_list = g_list_prepend (ave_list, NULL);
4361 /* if it is a decoder and we don't have a fixed sink, then find out
4362 * the matching audio/video sink from GstAVElements list */
4363 for (l = ave_list; l; l = l->next) {
4364 gboolean created_sink = FALSE;
4366 ave = (GstAVElement *) l->data;
4368 if (((isaudiodec && !group->audio_sink) ||
4369 (isvideodec && !group->video_sink))) {
4370 if (ave && ave->sink) {
4371 GST_DEBUG_OBJECT (playbin,
4372 "Trying to create sink '%s' for decoder '%s'",
4373 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)),
4374 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4375 if ((*sinkp = gst_element_factory_create (ave->sink, NULL)) == NULL) {
4376 GST_WARNING_OBJECT (playbin,
4377 "Could not create an element from %s",
4378 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4381 if (!activate_sink (playbin, *sinkp, NULL)) {
4382 gst_object_unref (*sinkp);
4384 GST_WARNING_OBJECT (playbin,
4385 "Could not activate sink %s",
4386 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4389 gst_object_ref_sink (*sinkp);
4390 created_sink = TRUE;
4395 /* If it is a decoder and we have a fixed sink for the media
4396 * type it outputs, check that the decoder is compatible with this sink */
4397 if ((isaudiodec && group->audio_sink) || (isvideodec
4398 && group->video_sink)) {
4399 gboolean compatible = FALSE;
4406 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4407 GstPlayFlags flags = gst_play_bin_get_flags (playbin);
4409 (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
4410 gst_static_caps_get (&raw_video_caps);
4412 caps = gst_pad_query_caps (sinkpad, NULL);
4414 /* If the sink supports raw audio/video, we first check
4415 * if the decoder could output any raw audio/video format
4416 * and assume it is compatible with the sink then. We don't
4417 * do a complete compatibility check here if converters
4418 * are plugged between the decoder and the sink because
4419 * the converters will convert between raw formats and
4420 * even if the decoder format is not supported by the decoder
4421 * a converter will convert it.
4423 * We assume here that the converters can convert between
4426 if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
4427 && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
4428 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
4429 && gst_caps_can_intersect (caps, raw_caps))) {
4431 gst_element_factory_can_src_any_caps (factory, raw_caps)
4432 || gst_element_factory_can_src_any_caps (factory, caps);
4434 compatible = gst_element_factory_can_src_any_caps (factory, caps);
4437 gst_object_unref (sinkpad);
4438 gst_caps_unref (caps);
4444 GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
4445 GST_OBJECT_NAME (factory));
4447 /* If it is not compatible, either continue with the next possible
4448 * sink or if we have a fixed sink, skip the decoder */
4450 gst_element_set_state (*sinkp, GST_STATE_NULL);
4451 gst_object_unref (*sinkp);
4454 g_mutex_unlock (&playbin->elements_lock);
4455 GST_SOURCE_GROUP_UNLOCK (group);
4456 return GST_AUTOPLUG_SELECT_SKIP;
4460 g_list_free (ave_list);
4461 g_mutex_unlock (&playbin->elements_lock);
4462 GST_SOURCE_GROUP_UNLOCK (group);
4463 return GST_AUTOPLUG_SELECT_TRY;
4466 /* it's a sink, see if an instance of it actually works */
4467 GST_DEBUG_OBJECT (playbin, "we found a sink '%s'", GST_OBJECT_NAME (factory));
4470 gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
4472 /* figure out the klass */
4473 if (strstr (klass, "Audio")) {
4474 GST_DEBUG_OBJECT (playbin, "we found an audio sink");
4475 type = GST_PLAY_SINK_TYPE_AUDIO;
4476 sinkp = &group->audio_sink;
4477 } else if (strstr (klass, "Video")) {
4478 GST_DEBUG_OBJECT (playbin, "we found a video sink");
4479 type = GST_PLAY_SINK_TYPE_VIDEO;
4480 sinkp = &group->video_sink;
4482 /* unknown klass, skip this element */
4483 GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
4484 return GST_AUTOPLUG_SELECT_SKIP;
4487 /* if we are asked to do visualisations and it's an audio sink, skip the
4488 * element. We can only do visualisations with raw sinks */
4489 if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
4490 if (type == GST_PLAY_SINK_TYPE_AUDIO) {
4491 GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
4492 return GST_AUTOPLUG_SELECT_SKIP;
4496 /* now see if we already have a sink element */
4497 GST_SOURCE_GROUP_LOCK (group);
4499 GstElement *sink = gst_object_ref (*sinkp);
4501 if (sink_accepts_caps (playbin, sink, caps)) {
4502 GST_DEBUG_OBJECT (playbin,
4503 "Existing sink '%s' accepts caps: %" GST_PTR_FORMAT,
4504 GST_ELEMENT_NAME (sink), caps);
4505 gst_object_unref (sink);
4506 GST_SOURCE_GROUP_UNLOCK (group);
4507 return GST_AUTOPLUG_SELECT_EXPOSE;
4509 GST_DEBUG_OBJECT (playbin,
4510 "Existing sink '%s' does not accept caps: %" GST_PTR_FORMAT,
4511 GST_ELEMENT_NAME (sink), caps);
4512 gst_object_unref (sink);
4513 GST_SOURCE_GROUP_UNLOCK (group);
4514 return GST_AUTOPLUG_SELECT_SKIP;
4517 GST_DEBUG_OBJECT (playbin, "we have no pending sink, try to create '%s'",
4518 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4520 if ((*sinkp = gst_element_factory_create (factory, NULL)) == NULL) {
4521 GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
4522 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4523 GST_SOURCE_GROUP_UNLOCK (group);
4524 return GST_AUTOPLUG_SELECT_SKIP;
4529 if (!activate_sink (playbin, element, NULL)) {
4530 GST_WARNING_OBJECT (playbin, "Could not activate sink %s",
4531 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4533 gst_object_unref (element);
4534 GST_SOURCE_GROUP_UNLOCK (group);
4535 return GST_AUTOPLUG_SELECT_SKIP;
4538 /* Check if the selected sink actually supports the
4539 * caps and can be set to READY*/
4540 if (!sink_accepts_caps (playbin, element, caps)) {
4542 gst_element_set_state (element, GST_STATE_NULL);
4543 gst_object_unref (element);
4544 GST_SOURCE_GROUP_UNLOCK (group);
4545 return GST_AUTOPLUG_SELECT_SKIP;
4548 /* remember the sink in the group now, the element is floating, we take
4551 * store the sink in the group, we will configure it later when we
4552 * reconfigure the sink */
4553 GST_DEBUG_OBJECT (playbin, "remember sink");
4554 gst_object_ref_sink (element);
4555 GST_SOURCE_GROUP_UNLOCK (group);
4557 /* tell decodebin to expose the pad because we are going to use this
4559 GST_DEBUG_OBJECT (playbin, "we found a working sink, expose pad");
4561 return GST_AUTOPLUG_SELECT_EXPOSE;
4565 autoplug_query_caps (GstElement * uridecodebin, GstPad * pad,
4566 GstElement * element, GstQuery * query, GstSourceGroup * group)
4568 GstCaps *filter, *result = NULL;
4570 GstPad *sinkpad = NULL;
4571 GstElementFactory *factory;
4572 GstElementFactoryListType factory_type;
4573 gboolean have_sink = FALSE;
4575 GST_SOURCE_GROUP_LOCK (group);
4576 gst_query_parse_caps (query, &filter);
4578 factory = gst_element_get_factory (element);
4582 if (gst_element_factory_list_is_type (factory,
4583 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4584 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4586 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4587 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE;
4589 /* If this is from the subtitle uridecodebin we don't need to
4590 * check the audio and video sink */
4591 if (group->suburidecodebin
4592 && gst_object_has_ancestor (GST_OBJECT_CAST (pad),
4593 GST_OBJECT_CAST (group->suburidecodebin))) {
4597 if ((sink = group->video_sink)) {
4598 sinkpad = gst_element_get_static_pad (sink, "sink");
4602 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4603 if (!gst_caps_is_any (sinkcaps)) {
4607 result = gst_caps_merge (result, sinkcaps);
4609 gst_caps_unref (sinkcaps);
4611 gst_object_unref (sinkpad);
4615 } else if (gst_element_factory_list_is_type (factory,
4616 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4617 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
4619 /* If this is from the subtitle uridecodebin we don't need to
4620 * check the audio and video sink */
4621 if (group->suburidecodebin
4622 && gst_object_has_ancestor (GST_OBJECT_CAST (pad),
4623 GST_OBJECT_CAST (group->suburidecodebin))) {
4627 if ((sink = group->audio_sink)) {
4628 sinkpad = gst_element_get_static_pad (sink, "sink");
4632 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4633 if (!gst_caps_is_any (sinkcaps)) {
4637 result = gst_caps_merge (result, sinkcaps);
4639 gst_caps_unref (sinkcaps);
4641 gst_object_unref (sinkpad);
4645 } else if (gst_element_factory_list_is_type (factory,
4646 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
4647 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE;
4649 if ((sink = group->playbin->text_sink)) {
4650 sinkpad = gst_element_get_static_pad (sink, "sink");
4654 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4655 if (!gst_caps_is_any (sinkcaps)) {
4659 result = gst_caps_merge (result, sinkcaps);
4661 gst_caps_unref (sinkcaps);
4663 gst_object_unref (sinkpad);
4667 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4671 result = gst_caps_merge (result, subcaps);
4678 GValueArray *factories;
4681 factories = autoplug_factories_cb (uridecodebin, pad, NULL, group);
4682 n = factories->n_values;
4683 for (i = 0; i < n; i++) {
4684 GValue *v = g_value_array_get_nth (factories, i);
4685 GstElementFactory *f = g_value_get_object (v);
4686 const GList *templates;
4688 GstCaps *templ_caps;
4690 if (!gst_element_factory_list_is_type (f, factory_type))
4693 templates = gst_element_factory_get_static_pad_templates (f);
4695 for (l = templates; l; l = l->next) {
4696 templ_caps = gst_static_pad_template_get_caps (l->data);
4698 if (!gst_caps_is_any (templ_caps)) {
4700 result = templ_caps;
4702 result = gst_caps_merge (result, templ_caps);
4704 gst_caps_unref (templ_caps);
4708 g_value_array_free (factories);
4712 GST_SOURCE_GROUP_UNLOCK (group);
4717 /* Add the actual decoder/parser/etc caps at the very end to
4718 * make sure we don't cause empty caps to be returned, e.g.
4719 * if a parser asks us but a decoder is required after it
4720 * because no sink can handle the format directly.
4723 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4726 result = gst_caps_merge (result, gst_pad_get_pad_template_caps (target));
4727 gst_object_unref (target);
4732 GstCaps *intersection =
4733 gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
4734 gst_caps_unref (result);
4735 result = intersection;
4738 gst_query_set_caps_result (query, result);
4739 gst_caps_unref (result);
4745 autoplug_query_context (GstElement * uridecodebin, GstPad * pad,
4746 GstElement * element, GstQuery * query, GstSourceGroup * group)
4749 GstPad *sinkpad = NULL;
4750 GstElementFactory *factory;
4751 gboolean res = FALSE;
4753 GST_SOURCE_GROUP_LOCK (group);
4755 factory = gst_element_get_factory (element);
4759 if (gst_element_factory_list_is_type (factory,
4760 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4761 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4762 /* If this is from the subtitle uridecodebin we don't need to
4763 * check the audio and video sink */
4764 if (group->suburidecodebin
4765 && gst_object_has_ancestor (GST_OBJECT_CAST (pad),
4766 GST_OBJECT_CAST (group->suburidecodebin))) {
4770 if ((sink = group->video_sink)) {
4771 sinkpad = gst_element_get_static_pad (sink, "sink");
4773 res = gst_pad_query (sinkpad, query);
4774 gst_object_unref (sinkpad);
4777 } else if (gst_element_factory_list_is_type (factory,
4778 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4779 /* If this is from the subtitle uridecodebin we don't need to
4780 * check the audio and video sink */
4781 if (group->suburidecodebin
4782 && gst_object_has_ancestor (GST_OBJECT_CAST (pad),
4783 GST_OBJECT_CAST (group->suburidecodebin))) {
4787 if ((sink = group->audio_sink)) {
4788 sinkpad = gst_element_get_static_pad (sink, "sink");
4790 res = gst_pad_query (sinkpad, query);
4791 gst_object_unref (sinkpad);
4794 } else if (gst_element_factory_list_is_type (factory,
4795 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
4796 if ((sink = group->playbin->text_sink)) {
4797 sinkpad = gst_element_get_static_pad (sink, "sink");
4799 res = gst_pad_query (sinkpad, query);
4800 gst_object_unref (sinkpad);
4808 GST_SOURCE_GROUP_UNLOCK (group);
4814 autoplug_query_cb (GstElement * uridecodebin, GstPad * pad,
4815 GstElement * element, GstQuery * query, GstSourceGroup * group)
4818 switch (GST_QUERY_TYPE (query)) {
4819 case GST_QUERY_CAPS:
4820 return autoplug_query_caps (uridecodebin, pad, element, query, group);
4821 case GST_QUERY_CONTEXT:
4822 return autoplug_query_context (uridecodebin, pad, element, query, group);
4829 notify_source_cb (GstElement * uridecodebin, GParamSpec * pspec,
4830 GstSourceGroup * group)
4832 GstPlayBin *playbin;
4835 playbin = group->playbin;
4837 g_object_get (group->uridecodebin, "source", &source, NULL);
4839 GST_OBJECT_LOCK (playbin);
4840 if (playbin->source)
4841 gst_object_unref (playbin->source);
4842 playbin->source = source;
4843 GST_OBJECT_UNLOCK (playbin);
4845 g_object_notify (G_OBJECT (playbin), "source");
4847 g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_SOURCE_SETUP],
4848 0, playbin->source);
4851 /* must be called with the group lock */
4853 group_set_locked_state_unlocked (GstPlayBin * playbin, GstSourceGroup * group,
4856 GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
4858 if (group->uridecodebin)
4859 gst_element_set_locked_state (group->uridecodebin, locked);
4860 if (group->suburidecodebin)
4861 gst_element_set_locked_state (group->suburidecodebin, locked);
4866 /* must be called with PLAY_BIN_LOCK */
4868 activate_group (GstPlayBin * playbin, GstSourceGroup * group, GstState target)
4870 GstElement *uridecodebin = NULL;
4871 GstElement *suburidecodebin = NULL;
4873 gboolean audio_sink_activated = FALSE;
4874 gboolean video_sink_activated = FALSE;
4875 gboolean text_sink_activated = FALSE;
4877 g_return_val_if_fail (group->valid, FALSE);
4878 g_return_val_if_fail (!group->active, FALSE);
4880 GST_DEBUG_OBJECT (playbin, "activating group %p", group);
4882 GST_SOURCE_GROUP_LOCK (group);
4884 /* First set up the custom sources */
4885 if (playbin->audio_sink)
4886 group->audio_sink = gst_object_ref (playbin->audio_sink);
4889 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO);
4891 if (group->audio_sink) {
4892 if (!activate_sink (playbin, group->audio_sink, &audio_sink_activated)) {
4893 if (group->audio_sink == playbin->audio_sink) {
4896 gst_object_unref (group->audio_sink);
4897 group->audio_sink = NULL;
4902 if (playbin->video_sink)
4903 group->video_sink = gst_object_ref (playbin->video_sink);
4906 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO);
4908 if (group->video_sink) {
4909 if (!activate_sink (playbin, group->video_sink, &video_sink_activated)) {
4910 if (group->video_sink == playbin->video_sink) {
4913 gst_object_unref (group->video_sink);
4914 group->video_sink = NULL;
4919 if (playbin->text_sink)
4920 group->text_sink = gst_object_ref (playbin->text_sink);
4923 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT);
4925 if (group->text_sink) {
4926 if (!activate_sink (playbin, group->text_sink, &text_sink_activated)) {
4927 if (group->text_sink == playbin->text_sink) {
4930 gst_object_unref (group->text_sink);
4931 group->text_sink = NULL;
4936 g_slist_free (group->suburi_flushes_to_drop);
4937 group->suburi_flushes_to_drop = NULL;
4938 if (!group->suburi_flushes_to_drop_lock.p)
4939 g_mutex_init (&group->suburi_flushes_to_drop_lock);
4941 if (group->uridecodebin) {
4942 GST_DEBUG_OBJECT (playbin, "reusing existing uridecodebin");
4943 uridecodebin = group->uridecodebin;
4944 gst_element_set_state (uridecodebin, GST_STATE_READY);
4945 /* no need to take extra ref, we already have one
4946 * and the bin will add one since it is no longer floating,
4947 * as it was at least added once before (below) */
4948 gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
4950 GST_DEBUG_OBJECT (playbin, "making new uridecodebin");
4951 uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
4954 gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
4955 group->uridecodebin = gst_object_ref (uridecodebin);
4958 flags = gst_play_sink_get_flags (playbin->playsink);
4960 g_object_set (uridecodebin,
4961 /* configure connection speed */
4962 "connection-speed", playbin->connection_speed / 1000,
4965 /* configure download buffering */
4966 "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
4967 /* configure buffering of demuxed/parsed data */
4968 "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
4969 /* configure buffering parameters */
4970 "buffer-duration", playbin->buffer_duration,
4971 "buffer-size", playbin->buffer_size,
4972 "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
4974 /* connect pads and other things */
4975 group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",
4976 G_CALLBACK (pad_added_cb), group);
4977 group->pad_removed_id = g_signal_connect (uridecodebin, "pad-removed",
4978 G_CALLBACK (pad_removed_cb), group);
4979 group->no_more_pads_id = g_signal_connect (uridecodebin, "no-more-pads",
4980 G_CALLBACK (no_more_pads_cb), group);
4981 group->notify_source_id = g_signal_connect (uridecodebin, "notify::source",
4982 G_CALLBACK (notify_source_cb), group);
4984 /* we have 1 pending no-more-pads */
4987 /* is called when the uridecodebin is out of data and we can switch to the
4990 g_signal_connect (uridecodebin, "drained", G_CALLBACK (drained_cb),
4993 /* will be called when a new media type is found. We return a list of decoders
4994 * including sinks for decodebin to try */
4995 group->autoplug_factories_id =
4996 g_signal_connect (uridecodebin, "autoplug-factories",
4997 G_CALLBACK (autoplug_factories_cb), group);
4998 group->autoplug_select_id =
4999 g_signal_connect (uridecodebin, "autoplug-select",
5000 G_CALLBACK (autoplug_select_cb), group);
5001 group->autoplug_continue_id =
5002 g_signal_connect (uridecodebin, "autoplug-continue",
5003 G_CALLBACK (autoplug_continue_cb), group);
5004 group->autoplug_query_id =
5005 g_signal_connect (uridecodebin, "autoplug-query",
5006 G_CALLBACK (autoplug_query_cb), group);
5008 if (group->suburi) {
5010 if (group->suburidecodebin) {
5011 GST_DEBUG_OBJECT (playbin, "reusing existing suburidecodebin");
5012 suburidecodebin = group->suburidecodebin;
5013 gst_element_set_state (suburidecodebin, GST_STATE_READY);
5014 /* no need to take extra ref, we already have one
5015 * and the bin will add one since it is no longer floating,
5016 * as it was at least added once before (below) */
5017 gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
5019 GST_DEBUG_OBJECT (playbin, "making new suburidecodebin");
5020 suburidecodebin = gst_element_factory_make ("uridecodebin", NULL);
5021 if (!suburidecodebin)
5024 gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
5025 group->suburidecodebin = gst_object_ref (suburidecodebin);
5028 g_object_set (suburidecodebin,
5029 /* configure connection speed */
5030 "connection-speed", playbin->connection_speed,
5032 "uri", group->suburi, NULL);
5034 /* connect pads and other things */
5035 group->sub_pad_added_id = g_signal_connect (suburidecodebin, "pad-added",
5036 G_CALLBACK (pad_added_cb), group);
5037 group->sub_pad_removed_id = g_signal_connect (suburidecodebin,
5038 "pad-removed", G_CALLBACK (pad_removed_cb), group);
5039 group->sub_no_more_pads_id = g_signal_connect (suburidecodebin,
5040 "no-more-pads", G_CALLBACK (no_more_pads_cb), group);
5042 group->sub_autoplug_continue_id =
5043 g_signal_connect (suburidecodebin, "autoplug-continue",
5044 G_CALLBACK (autoplug_continue_cb), group);
5046 group->sub_autoplug_query_id =
5047 g_signal_connect (suburidecodebin, "autoplug-query",
5048 G_CALLBACK (autoplug_query_cb), group);
5050 /* we have 2 pending no-more-pads */
5052 group->sub_pending = TRUE;
5054 group->sub_pending = FALSE;
5057 /* release the group lock before setting the state of the decodebins, they
5058 * might fire signals in this thread that we need to handle with the
5059 * group_lock taken. */
5060 GST_SOURCE_GROUP_UNLOCK (group);
5062 if (suburidecodebin) {
5063 if (gst_element_set_state (suburidecodebin,
5064 target) == GST_STATE_CHANGE_FAILURE) {
5065 GST_DEBUG_OBJECT (playbin,
5066 "failed state change of subtitle uridecodebin");
5067 GST_SOURCE_GROUP_LOCK (group);
5069 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
5070 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
5071 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
5072 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
5073 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
5074 /* Might already be removed because of an error message */
5075 if (GST_OBJECT_PARENT (suburidecodebin) == GST_OBJECT_CAST (playbin))
5076 gst_bin_remove (GST_BIN_CAST (playbin), suburidecodebin);
5077 if (group->sub_pending) {
5079 group->sub_pending = FALSE;
5081 gst_element_set_state (suburidecodebin, GST_STATE_READY);
5082 GST_SOURCE_GROUP_UNLOCK (group);
5085 if (gst_element_set_state (uridecodebin, target) == GST_STATE_CHANGE_FAILURE)
5086 goto uridecodebin_failure;
5088 GST_SOURCE_GROUP_LOCK (group);
5089 /* alow state changes of the playbin affect the group elements now */
5090 group_set_locked_state_unlocked (playbin, group, FALSE);
5091 group->active = TRUE;
5092 GST_SOURCE_GROUP_UNLOCK (group);
5101 GST_SOURCE_GROUP_UNLOCK (group);
5103 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
5105 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
5107 GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
5108 (_("Could not create \"uridecodebin\" element.")), (NULL));
5110 GST_SOURCE_GROUP_LOCK (group);
5114 uridecodebin_failure:
5116 GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
5121 GST_ERROR_OBJECT (playbin, "failed to activate sinks");
5127 /* delete any custom sinks we might have */
5128 if (group->audio_sink) {
5129 /* If this is a automatically created sink set it to NULL */
5130 if (audio_sink_activated)
5131 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5132 gst_object_unref (group->audio_sink);
5134 group->audio_sink = NULL;
5136 if (group->video_sink) {
5137 /* If this is a automatically created sink set it to NULL */
5138 if (video_sink_activated)
5139 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5140 gst_object_unref (group->video_sink);
5142 group->video_sink = NULL;
5144 if (group->text_sink) {
5145 /* If this is a automatically created sink set it to NULL */
5146 if (text_sink_activated)
5147 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5148 gst_object_unref (group->text_sink);
5150 group->text_sink = NULL;
5153 gst_element_set_state (uridecodebin, GST_STATE_NULL);
5154 gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin);
5157 GST_SOURCE_GROUP_UNLOCK (group);
5163 /* unlink a group of uridecodebins from the sink.
5164 * must be called with PLAY_BIN_LOCK */
5166 deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
5170 g_return_val_if_fail (group->valid, FALSE);
5171 g_return_val_if_fail (group->active, FALSE);
5173 GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
5175 GST_SOURCE_GROUP_LOCK (group);
5176 group->active = FALSE;
5177 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
5178 GstSourceCombine *combine = &group->combiner[i];
5180 GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_list[0]);
5182 if (combine->srcpad) {
5183 if (combine->sinkpad) {
5184 GST_LOG_OBJECT (playbin, "unlinking from sink");
5185 gst_pad_unlink (combine->srcpad, combine->sinkpad);
5188 GST_LOG_OBJECT (playbin, "release sink pad");
5189 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
5190 combine->sinkpad = NULL;
5193 gst_object_unref (combine->srcpad);
5194 combine->srcpad = NULL;
5197 if (combine->combiner) {
5200 /* release and unref requests pad from the combiner */
5201 for (n = 0; n < combine->channels->len; n++) {
5202 GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
5204 gst_element_release_request_pad (combine->combiner, sinkpad);
5205 gst_object_unref (sinkpad);
5207 g_ptr_array_set_size (combine->channels, 0);
5209 gst_element_set_state (combine->combiner, GST_STATE_NULL);
5210 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
5211 combine->combiner = NULL;
5214 /* delete any custom sinks we might have.
5215 * conditionally set them to null if they aren't inside playsink yet */
5216 if (group->audio_sink) {
5217 if (!gst_object_has_ancestor (GST_OBJECT_CAST (group->audio_sink),
5218 GST_OBJECT_CAST (playbin->playsink))) {
5219 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5221 gst_object_unref (group->audio_sink);
5223 group->audio_sink = NULL;
5224 if (group->video_sink) {
5225 if (!gst_object_has_ancestor (GST_OBJECT_CAST (group->video_sink),
5226 GST_OBJECT_CAST (playbin->playsink))) {
5227 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5229 gst_object_unref (group->video_sink);
5231 group->video_sink = NULL;
5232 if (group->text_sink) {
5233 if (!gst_object_has_ancestor (GST_OBJECT_CAST (group->text_sink),
5234 GST_OBJECT_CAST (playbin->playsink))) {
5235 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5237 gst_object_unref (group->text_sink);
5239 group->text_sink = NULL;
5241 if (group->uridecodebin) {
5242 REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
5243 REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
5244 REMOVE_SIGNAL (group->uridecodebin, group->no_more_pads_id);
5245 REMOVE_SIGNAL (group->uridecodebin, group->notify_source_id);
5246 REMOVE_SIGNAL (group->uridecodebin, group->drained_id);
5247 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_factories_id);
5248 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_select_id);
5249 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_continue_id);
5250 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_query_id);
5251 gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
5254 if (group->suburidecodebin) {
5255 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
5256 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
5257 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
5258 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
5259 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
5261 /* Might already be removed because of errors */
5262 if (GST_OBJECT_PARENT (group->suburidecodebin) == GST_OBJECT_CAST (playbin))
5263 gst_bin_remove (GST_BIN_CAST (playbin), group->suburidecodebin);
5266 group->have_group_id = FALSE;
5268 GST_SOURCE_GROUP_UNLOCK (group);
5273 /* setup the next group to play, this assumes the next_group is valid and
5274 * configured. It swaps out the current_group and activates the valid
5277 setup_next_source (GstPlayBin * playbin, GstState target)
5279 GstSourceGroup *new_group, *old_group;
5281 GST_DEBUG_OBJECT (playbin, "setup sources");
5283 /* see if there is a next group */
5284 GST_PLAY_BIN_LOCK (playbin);
5285 new_group = playbin->next_group;
5286 if (!new_group || !new_group->valid)
5289 new_group->stream_changed_pending = TRUE;
5291 /* first unlink the current source, if any */
5292 old_group = playbin->curr_group;
5293 if (old_group && old_group->valid && old_group->active) {
5294 gst_play_bin_update_cached_duration (playbin);
5295 /* unlink our pads with the sink */
5296 deactivate_group (playbin, old_group);
5297 old_group->valid = FALSE;
5300 /* swap old and new */
5301 playbin->curr_group = new_group;
5302 playbin->next_group = old_group;
5304 /* activate the new group */
5305 if (!activate_group (playbin, new_group, target))
5306 goto activate_failed;
5308 GST_PLAY_BIN_UNLOCK (playbin);
5315 GST_DEBUG_OBJECT (playbin, "no next group");
5316 if (target == GST_STATE_READY && new_group && new_group->uri == NULL)
5317 GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL));
5318 GST_PLAY_BIN_UNLOCK (playbin);
5323 new_group->stream_changed_pending = FALSE;
5324 GST_DEBUG_OBJECT (playbin, "activate failed");
5325 GST_PLAY_BIN_UNLOCK (playbin);
5330 /* The group that is currently playing is copied again to the
5331 * next_group so that it will start playing the next time.
5334 save_current_group (GstPlayBin * playbin)
5336 GstSourceGroup *curr_group;
5338 GST_DEBUG_OBJECT (playbin, "save current group");
5340 /* see if there is a current group */
5341 GST_PLAY_BIN_LOCK (playbin);
5342 curr_group = playbin->curr_group;
5343 if (curr_group && curr_group->valid && curr_group->active) {
5344 /* unlink our pads with the sink */
5345 deactivate_group (playbin, curr_group);
5347 /* swap old and new */
5348 playbin->curr_group = playbin->next_group;
5349 playbin->next_group = curr_group;
5350 GST_PLAY_BIN_UNLOCK (playbin);
5355 /* clear the locked state from all groups. This function is called before a
5356 * state change to NULL is performed on them. */
5358 groups_set_locked_state (GstPlayBin * playbin, gboolean locked)
5360 GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
5363 GST_PLAY_BIN_LOCK (playbin);
5364 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
5365 group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
5366 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
5367 GST_SOURCE_GROUP_LOCK (playbin->next_group);
5368 group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
5369 GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
5370 GST_PLAY_BIN_UNLOCK (playbin);
5375 static GstStateChangeReturn
5376 gst_play_bin_change_state (GstElement * element, GstStateChange transition)
5378 GstStateChangeReturn ret;
5379 GstPlayBin *playbin;
5380 gboolean do_save = FALSE;
5382 playbin = GST_PLAY_BIN (element);
5384 switch (transition) {
5385 case GST_STATE_CHANGE_NULL_TO_READY:
5386 memset (&playbin->duration, 0, sizeof (playbin->duration));
5388 case GST_STATE_CHANGE_READY_TO_PAUSED:
5389 GST_LOG_OBJECT (playbin, "clearing shutdown flag");
5390 memset (&playbin->duration, 0, sizeof (playbin->duration));
5391 g_atomic_int_set (&playbin->shutdown, 0);
5393 if (!setup_next_source (playbin, GST_STATE_READY)) {
5394 ret = GST_STATE_CHANGE_FAILURE;
5398 case GST_STATE_CHANGE_PAUSED_TO_READY:
5400 /* FIXME unlock our waiting groups */
5401 GST_LOG_OBJECT (playbin, "setting shutdown flag");
5402 g_atomic_int_set (&playbin->shutdown, 1);
5403 memset (&playbin->duration, 0, sizeof (playbin->duration));
5405 /* wait for all callbacks to end by taking the lock.
5406 * No dynamic (critical) new callbacks will
5407 * be able to happen as we set the shutdown flag. */
5408 GST_PLAY_BIN_DYN_LOCK (playbin);
5409 GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
5410 GST_PLAY_BIN_DYN_UNLOCK (playbin);
5413 case GST_STATE_CHANGE_READY_TO_NULL:
5414 /* we go async to PAUSED, so if that fails, we never make it to PAUSED
5415 * an no state change PAUSED to READY passes here,
5416 * though it is a nice-to-have ... */
5417 if (!g_atomic_int_get (&playbin->shutdown)) {
5421 memset (&playbin->duration, 0, sizeof (playbin->duration));
5423 /* unlock so that all groups go to NULL */
5424 groups_set_locked_state (playbin, FALSE);
5430 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5431 if (ret == GST_STATE_CHANGE_FAILURE)
5434 switch (transition) {
5435 case GST_STATE_CHANGE_READY_TO_PAUSED:
5437 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5438 /* FIXME Release audio device when we implement that */
5440 case GST_STATE_CHANGE_PAUSED_TO_READY:
5441 save_current_group (playbin);
5443 case GST_STATE_CHANGE_READY_TO_NULL:
5448 /* also do missed state change down to READY */
5450 save_current_group (playbin);
5451 /* Deactive the groups, set the uridecodebins to NULL
5454 for (i = 0; i < 2; i++) {
5455 if (playbin->groups[i].active && playbin->groups[i].valid) {
5456 deactivate_group (playbin, &playbin->groups[i]);
5457 playbin->groups[i].valid = FALSE;
5460 if (playbin->groups[i].uridecodebin) {
5461 gst_element_set_state (playbin->groups[i].uridecodebin,
5463 gst_object_unref (playbin->groups[i].uridecodebin);
5464 playbin->groups[i].uridecodebin = NULL;
5467 if (playbin->groups[i].suburidecodebin) {
5468 gst_element_set_state (playbin->groups[i].suburidecodebin,
5470 gst_object_unref (playbin->groups[i].suburidecodebin);
5471 playbin->groups[i].suburidecodebin = NULL;
5475 /* Set our sinks back to NULL, they might not be child of playbin */
5476 if (playbin->audio_sink)
5477 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
5478 if (playbin->video_sink)
5479 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
5480 if (playbin->text_sink)
5481 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
5483 if (playbin->video_stream_combiner)
5484 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
5485 if (playbin->audio_stream_combiner)
5486 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
5487 if (playbin->text_stream_combiner)
5488 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
5490 /* make sure the groups don't perform a state change anymore until we
5491 * enable them again */
5492 groups_set_locked_state (playbin, TRUE);
5494 /* Remove all non-persistent contexts */
5495 GST_OBJECT_LOCK (playbin);
5496 for (l = playbin->contexts; l;) {
5497 GstContext *context = l->data;
5499 if (!gst_context_is_persistent (context)) {
5502 gst_context_unref (context);
5505 playbin->contexts = g_list_delete_link (playbin->contexts, l);
5511 GST_OBJECT_UNLOCK (playbin);
5523 if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
5524 GstSourceGroup *curr_group;
5526 curr_group = playbin->curr_group;
5527 if (curr_group && curr_group->active && curr_group->valid) {
5528 /* unlink our pads with the sink */
5529 deactivate_group (playbin, curr_group);
5530 curr_group->valid = FALSE;
5533 /* Swap current and next group back */
5534 playbin->curr_group = playbin->next_group;
5535 playbin->next_group = curr_group;
5542 gst_play_bin_overlay_expose (GstVideoOverlay * overlay)
5544 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
5546 gst_video_overlay_expose (GST_VIDEO_OVERLAY (playbin->playsink));
5550 gst_play_bin_overlay_handle_events (GstVideoOverlay * overlay,
5551 gboolean handle_events)
5553 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
5555 gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (playbin->playsink),
5560 gst_play_bin_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
5561 gint y, gint width, gint height)
5563 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
5565 gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (playbin->playsink),
5566 x, y, width, height);
5570 gst_play_bin_overlay_set_window_handle (GstVideoOverlay * overlay,
5573 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
5575 gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (playbin->playsink),
5580 gst_play_bin_overlay_init (gpointer g_iface, gpointer g_iface_data)
5582 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
5583 iface->expose = gst_play_bin_overlay_expose;
5584 iface->handle_events = gst_play_bin_overlay_handle_events;
5585 iface->set_render_rectangle = gst_play_bin_overlay_set_render_rectangle;
5586 iface->set_window_handle = gst_play_bin_overlay_set_window_handle;
5590 gst_play_bin_navigation_send_event (GstNavigation * navigation,
5591 GstStructure * structure)
5593 GstPlayBin *playbin = GST_PLAY_BIN (navigation);
5595 gst_navigation_send_event (GST_NAVIGATION (playbin->playsink), structure);
5599 gst_play_bin_navigation_init (gpointer g_iface, gpointer g_iface_data)
5601 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
5603 iface->send_event = gst_play_bin_navigation_send_event;
5606 static const GList *
5607 gst_play_bin_colorbalance_list_channels (GstColorBalance * balance)
5609 GstPlayBin *playbin = GST_PLAY_BIN (balance);
5612 gst_color_balance_list_channels (GST_COLOR_BALANCE (playbin->playsink));
5616 gst_play_bin_colorbalance_set_value (GstColorBalance * balance,
5617 GstColorBalanceChannel * channel, gint value)
5619 GstPlayBin *playbin = GST_PLAY_BIN (balance);
5621 gst_color_balance_set_value (GST_COLOR_BALANCE (playbin->playsink), channel,
5626 gst_play_bin_colorbalance_get_value (GstColorBalance * balance,
5627 GstColorBalanceChannel * channel)
5629 GstPlayBin *playbin = GST_PLAY_BIN (balance);
5631 return gst_color_balance_get_value (GST_COLOR_BALANCE (playbin->playsink),
5635 static GstColorBalanceType
5636 gst_play_bin_colorbalance_get_balance_type (GstColorBalance * balance)
5638 GstPlayBin *playbin = GST_PLAY_BIN (balance);
5641 gst_color_balance_get_balance_type (GST_COLOR_BALANCE
5642 (playbin->playsink));
5646 gst_play_bin_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
5648 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
5650 iface->list_channels = gst_play_bin_colorbalance_list_channels;
5651 iface->set_value = gst_play_bin_colorbalance_set_value;
5652 iface->get_value = gst_play_bin_colorbalance_get_value;
5653 iface->get_balance_type = gst_play_bin_colorbalance_get_balance_type;
5657 gst_play_bin2_plugin_init (GstPlugin * plugin)
5659 GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
5661 return gst_element_register (plugin, "playbin", GST_RANK_NONE,