2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * Copyright (C) <2013> Collabora Ltd.
5 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * Copyright (C) <2015> Jan Schmidt <jan@centricular.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 * SECTION:element-playbin3
27 * Playbin provides a stand-alone everything-in-one abstraction for an
28 * audio and/or video player.
30 * Playbin can handle both audio and video files and features
33 * automatic file type recognition and based on that automatic
34 * selection and usage of the right audio/video/subtitle demuxers/decoders
37 * visualisations for audio files
40 * subtitle support for video files. Subtitles can be store in external
44 * stream selection between different video/audio/subtitles streams
47 * meta info (tag) extraction
50 * easy access to the last video sample
53 * buffering when playing streams over a network
56 * volume control with mute option
61 * <title>Usage</title>
63 * A playbin element can be created just like any other element using
64 * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin3:uri
65 * property. This must be an absolute URI, relative file paths are not allowed.
66 * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
68 * Playbin is a #GstPipeline. It will notify the application of everything
69 * that's happening (errors, end of stream, tags found, state changes, etc.)
70 * by posting messages on its #GstBus. The application needs to watch the
73 * Playback can be initiated by setting the element to PLAYING state using
74 * gst_element_set_state(). Note that the state change will take place in
75 * the background in a separate thread, when the function returns playback
76 * is probably not happening yet and any errors might not have occured yet.
77 * Applications using playbin should ideally be written to deal with things
78 * completely asynchroneous.
80 * When playback has finished (an EOS message has been received on the bus)
81 * or an error has occured (an ERROR message has been received on the bus) or
82 * the user wants to play a different track, playbin should be set back to
83 * READY or NULL state, then the #GstPlayBin3:uri property should be set to the
84 * new location and then playbin be set to PLAYING state again.
86 * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
87 * on the playbin element. Again, the seek will not be executed
88 * instantaneously, but will be done in a background thread. When the seek
89 * call returns the seek will most likely still be in process. An application
90 * may wait for the seek to finish (or fail) using gst_element_get_state() with
91 * -1 as the timeout, but this will block the user interface and is not
94 * Applications may query the current position and duration of the stream
95 * via gst_element_query_position() and gst_element_query_duration() and
96 * setting the format passed to GST_FORMAT_TIME. If the query was successful,
97 * the duration or position will have been returned in units of nanoseconds.
101 * <title>Advanced Usage: specifying the audio and video sink</title>
103 * By default, if no audio sink or video sink has been specified via the
104 * #GstPlayBin3:audio-sink or #GstPlayBin3:video-sink property, playbin will use the autoaudiosink
105 * and autovideosink elements to find the first-best available output method.
106 * This should work in most cases, but is not always desirable. Often either
107 * the user or application might want to specify more explicitly what to use
108 * for audio and video output.
110 * If the application wants more control over how audio or video should be
111 * output, it may create the audio/video sink elements itself (for example
112 * using gst_element_factory_make()) and provide them to playbin using the
113 * #GstPlayBin3:audio-sink or #GstPlayBin3:video-sink property.
115 * GNOME-based applications, for example, will usually want to create
116 * gconfaudiosink and gconfvideosink elements and make playbin use those,
117 * so that output happens to whatever the user has configured in the GNOME
118 * Multimedia System Selector configuration dialog.
120 * The sink elements do not necessarily need to be ready-made sinks. It is
121 * possible to create container elements that look like a sink to playbin,
122 * but in reality contain a number of custom elements linked together. This
123 * can be achieved by creating a #GstBin and putting elements in there and
124 * linking them, and then creating a sink #GstGhostPad for the bin and pointing
125 * it to the sink pad of the first element within the bin. This can be used
126 * for a number of purposes, for example to force output to a particular
127 * format or to modify or observe the data before it is output.
129 * It is also possible to 'suppress' audio and/or video output by using
130 * 'fakesink' elements (or capture it from there using the fakesink element's
131 * "handoff" signal, which, nota bene, is fired from the streaming thread!).
135 * <title>Retrieving Tags and Other Meta Data</title>
137 * Most of the common meta data (artist, title, etc.) can be retrieved by
138 * watching for TAG messages on the pipeline's bus (see above).
140 * Other more specific meta information like width/height/framerate of video
141 * streams or samplerate/number of channels of audio streams can be obtained
142 * from the negotiated caps on the sink pads of the sinks.
146 * <title>Buffering</title>
147 * Playbin handles buffering automatically for the most part, but applications
148 * need to handle parts of the buffering process as well. Whenever playbin is
149 * buffering, it will post BUFFERING messages on the bus with a percentage
150 * value that shows the progress of the buffering process. Applications need
151 * to set playbin to PLAYING or PAUSED state in response to these messages.
152 * They may also want to convey the buffering progress to the user in some
153 * way. Here is how to extract the percentage information from the message:
155 * switch (GST_MESSAGE_TYPE (msg)) {
156 * case GST_MESSAGE_BUFFERING: {
158 * gst_message_parse_buffering (msg, &percent);
159 * g_print ("Buffering (%u percent done)", percent);
165 * Note that applications should keep/set the pipeline in the PAUSED state when
166 * a BUFFERING message is received with a buffer percent value < 100 and set
167 * the pipeline back to PLAYING state when a BUFFERING message with a value
168 * of 100 percent is received (if PLAYING is the desired state, that is).
171 * <title>Embedding the video window in your application</title>
172 * By default, playbin (or rather the video sinks used) will create their own
173 * window. Applications will usually want to force output to a window of their
174 * own, however. This can be done using the #GstVideoOverlay interface, which most
175 * video sinks implement. See the documentation there for more details.
178 * <title>Specifying which CD/DVD device to use</title>
179 * The device to use for CDs/DVDs needs to be set on the source element
180 * playbin creates before it is opened. The most generic way of doing this
181 * is to connect to playbin's "source-setup" (or "notify::source") signal,
182 * which will be emitted by playbin when it has created the source element
183 * for a particular URI. In the signal callback you can check if the source
184 * element has a "device" property and set it appropriately. In some cases
185 * the device can also be set as part of the URI, but it depends on the
186 * elements involved if this will work or not. For example, for DVD menu
187 * playback, the following syntax might work (if the resindvd plugin is used):
188 * dvd://[/path/to/device]
191 * <title>Handling redirects</title>
193 * Some elements may post 'redirect' messages on the bus to tell the
194 * application to open another location. These are element messages containing
195 * a structure named 'redirect' along with a 'new-location' field of string
196 * type. The new location may be a relative or an absolute URI. Examples
197 * for such redirects can be found in many quicktime movie trailers.
201 * <title>Examples</title>
203 * gst-launch-1.0 -v playbin uri=file:///path/to/somefile.mp4
204 * ]| This will play back the given AVI video file, given that the video and
205 * audio decoders required to decode the content are installed. Since no
206 * special audio sink or video sink is supplied (via playbin's audio-sink or
207 * video-sink properties) playbin will try to find a suitable audio and
208 * video sink automatically using the autoaudiosink and autovideosink elements.
210 * gst-launch-1.0 -v playbin uri=cdda://4
211 * ]| This will play back track 4 on an audio CD in your disc drive (assuming
212 * the drive is detected automatically by the plugin).
214 * gst-launch-1.0 -v playbin uri=dvd://
215 * ]| This will play back the DVD in your disc drive (assuming
216 * the drive is detected automatically by the plugin).
220 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
221 * with newer GLib versions (>= 2.31.0) */
222 #define GLIB_DISABLE_DEPRECATION_WARNINGS
231 #include <gst/gst-i18n-plugin.h>
232 #include <gst/pbutils/pbutils.h>
233 #include <gst/audio/streamvolume.h>
234 #include <gst/video/video-info.h>
235 #include <gst/video/video-multiview.h>
236 #include <gst/video/videooverlay.h>
237 #include <gst/video/navigation.h>
238 #include <gst/video/colorbalance.h>
239 #include "gstplay-enum.h"
240 #include "gstplayback.h"
241 #include "gstplaysink.h"
242 #include "gstsubtitleoverlay.h"
243 #include "gstplaybackutils.h"
245 GST_DEBUG_CATEGORY_STATIC (gst_play_bin3_debug);
246 #define GST_CAT_DEFAULT gst_play_bin3_debug
248 #define GST_TYPE_PLAY_BIN (gst_play_bin3_get_type())
249 #define GST_PLAY_BIN3(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin3))
250 #define GST_PLAY_BIN3_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBin3Class))
251 #define GST_IS_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
252 #define GST_IS_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
254 #define ULONG_TO_POINTER(number) ((gpointer) (guintptr) (number))
255 #define POINTER_TO_ULONG(number) ((guintptr) (number))
257 #define VOLUME_MAX_DOUBLE 10.0
259 typedef struct _GstPlayBin3 GstPlayBin3;
260 typedef struct _GstPlayBin3Class GstPlayBin3Class;
261 typedef struct _GstSourceGroup GstSourceGroup;
262 typedef struct _GstSourceCombine GstSourceCombine;
264 typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void);
266 /* has the info for a combiner and provides the link to the sink */
267 struct _GstSourceCombine
269 const gchar *media_type; /* the media type for the combiner */
270 SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */
271 GstPlaySinkType type; /* the sink pad type of the combiner */
273 GstElement *combiner; /* the combiner */
275 GstPad *srcpad; /* the source pad of the combiner */
276 GstPad *sinkpad; /* the sinkpad of the sink when the combiner
281 GPtrArray *streams; /* Sorted array of GstStream for the given type */
282 gint current_stream; /* Currently selected GstStream */
284 gboolean has_active_pad; /* stream combiner has the "active-pad" property */
286 gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */
289 #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
290 #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
291 #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
295 PLAYBIN_STREAM_AUDIO = 0,
296 PLAYBIN_STREAM_VIDEO,
301 /* names matching the enum above */
302 static const gchar *stream_type_names[] = {
303 "audio", "video", "text"
306 static void avelements_free (gpointer data);
307 static GSequence *avelements_create (GstPlayBin3 * playbin,
308 gboolean isaudioelement);
310 /* The GstAudioVideoElement structure holding the audio/video decoder
311 * and the audio/video sink factories together with field indicating
312 * the number of common caps features */
315 GstElementFactory *dec; /* audio:video decoder */
316 GstElementFactory *sink; /* audio:video sink */
317 guint n_comm_cf; /* number of common caps features */
320 /* a structure to hold the objects for decoding a uri and the subtitle uri
322 struct _GstSourceGroup
324 GstPlayBin3 *playbin;
328 gboolean valid; /* the group has valid info to start playback */
329 gboolean active; /* the group is active */
334 GValueArray *streaminfo;
338 /* urisourcebins for uri and subtitle uri */
339 /* FIXME: Just make this an array of uris */
340 GstElement *urisourcebin;
341 GstElement *suburisourcebin;
343 /* Active sinks for each media type. These are initialized with
344 * the configured or currently used sink, otherwise
345 * left as NULL and playbin tries to automatically
346 * select a good sink */
347 GstElement *audio_sink;
348 GstElement *video_sink;
349 GstElement *text_sink;
352 gboolean sub_pending;
354 /* primary uri signals */
355 gulong urisrc_pad_added_id;
356 gulong urisrc_pad_removed_id;
357 gulong notify_source_id;
358 gulong autoplug_factories_id;
359 gulong autoplug_select_id;
360 gulong autoplug_continue_id;
361 gulong autoplug_query_id;
363 /* subtitle uri signals */
364 gulong sub_pad_added_id;
365 gulong sub_pad_removed_id;
366 gulong sub_autoplug_continue_id;
367 gulong sub_autoplug_query_id;
371 GMutex stream_changed_pending_lock;
372 gboolean stream_changed_pending;
374 /* buffering message stored for after switching */
375 GstMessage *pending_buffering_msg;
378 #define GST_PLAY_BIN3_GET_LOCK(bin) (&((GstPlayBin3*)(bin))->lock)
379 #define GST_PLAY_BIN3_LOCK(bin) (g_rec_mutex_lock (GST_PLAY_BIN3_GET_LOCK(bin)))
380 #define GST_PLAY_BIN3_UNLOCK(bin) (g_rec_mutex_unlock (GST_PLAY_BIN3_GET_LOCK(bin)))
382 /* lock to protect dynamic callbacks, like no-more-pads */
383 #define GST_PLAY_BIN3_DYN_LOCK(bin) g_mutex_lock (&(bin)->dyn_lock)
384 #define GST_PLAY_BIN3_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock)
386 /* lock for shutdown */
387 #define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \
389 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \
391 GST_PLAY_BIN3_DYN_LOCK (bin); \
392 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
393 GST_PLAY_BIN3_DYN_UNLOCK (bin); \
398 /* unlock for shutdown */
399 #define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \
400 GST_PLAY_BIN3_DYN_UNLOCK (bin); \
405 * playbin element structure
411 GRecMutex lock; /* to protect group switching */
413 /* the input groups, we use a double buffer to switch between current and next */
414 GstSourceGroup groups[2]; /* array with group info */
415 GstSourceGroup *curr_group; /* pointer to the currently playing group */
416 GstSourceGroup *next_group; /* pointer to the next group */
418 /* combiners for different streams */
419 GPtrArray *channels[PLAYBIN_STREAM_LAST]; /* links to combiner pads */
420 GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
422 /* A global decodebin3 that's used to actually do decoding */
423 gboolean decodebin_active;
424 GstElement *decodebin;
425 /* Bit-wise set of stream types we have
426 * requested from decodebin vs stream types
427 * decodebin has provided */
428 GstStreamType selected_stream_types;
429 GstStreamType active_stream_types;
431 /* Decodebin signals */
432 gulong db_pad_added_id;
433 gulong db_pad_removed_id;
434 gulong db_no_more_pads_id;
435 gulong db_drained_id;
436 gulong db_select_stream_id;
439 guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */
440 gint current_video; /* the currently selected stream */
441 gint current_audio; /* the currently selected stream */
442 gint current_text; /* the currently selected stream */
444 gboolean do_stream_selections; /* Set to TRUE when any of current-{video|audio|text} are set to
445 say playbin should do backwards-compatibility behaviours */
447 guint64 buffer_duration; /* When buffering, the max buffer duration (ns) */
448 guint buffer_size; /* When buffering, the max buffer size (bytes) */
449 gboolean force_aspect_ratio;
451 /* Multiview/stereoscopic overrides */
452 GstVideoMultiviewFramePacking multiview_mode;
453 GstVideoMultiviewFlags multiview_flags;
456 GstPlaySink *playsink;
458 /* the last activated source */
461 /* lock protecting dynamic adding/removing */
463 /* if we are shutting down or not */
465 gboolean async_pending; /* async-start has been emitted */
467 GMutex elements_lock;
468 guint32 elements_cookie;
469 GList *elements; /* factories we can use for selecting elements */
471 gboolean have_selector; /* set to FALSE when we fail to create an
472 * input-selector, so that we only post a
475 gboolean video_pending_flush_finish; /* whether we are pending to send a custom
476 * custom-video-flush-finish event
477 * on pad activation */
478 gboolean audio_pending_flush_finish; /* whether we are pending to send a custom
479 * custom-audio-flush-finish event
480 * on pad activation */
481 gboolean text_pending_flush_finish; /* whether we are pending to send a custom
482 * custom-subtitle-flush-finish event
483 * on pad activation */
485 GstElement *audio_sink; /* configured audio sink, or NULL */
486 GstElement *video_sink; /* configured video sink, or NULL */
487 GstElement *text_sink; /* configured text sink, or NULL */
489 GstElement *audio_stream_combiner; /* configured audio stream combiner, or NULL */
490 GstElement *video_stream_combiner; /* configured video stream combiner, or NULL */
491 GstElement *text_stream_combiner; /* configured text stream combiner, or NULL */
493 GSequence *aelements; /* a list of GstAVElements for audio stream */
494 GSequence *velements; /* a list of GstAVElements for video stream */
501 } duration[5]; /* cached durations */
503 guint64 ring_buffer_max_size; /* 0 means disabled */
507 /* Active stream collection */
508 GstStreamCollection *collection;
509 guint collection_notify_id;
512 struct _GstPlayBin3Class
514 GstPipelineClass parent_class;
516 /* notify app that the current uri finished decoding and it is possible to
517 * queue a new one for gapless playback */
518 void (*about_to_finish) (GstPlayBin3 * playbin);
520 /* notify app that number of audio/video/text streams changed */
521 void (*video_changed) (GstPlayBin3 * playbin);
522 void (*audio_changed) (GstPlayBin3 * playbin);
523 void (*text_changed) (GstPlayBin3 * playbin);
525 /* notify app that the tags of audio/video/text streams changed */
526 void (*video_tags_changed) (GstPlayBin3 * playbin, gint stream);
527 void (*audio_tags_changed) (GstPlayBin3 * playbin, gint stream);
528 void (*text_tags_changed) (GstPlayBin3 * playbin, gint stream);
530 /* get audio/video/text tags for a stream */
531 GstTagList *(*get_video_tags) (GstPlayBin3 * playbin, gint stream);
532 GstTagList *(*get_audio_tags) (GstPlayBin3 * playbin, gint stream);
533 GstTagList *(*get_text_tags) (GstPlayBin3 * playbin, gint stream);
535 /* get the last video sample and convert it to the given caps */
536 GstSample *(*convert_sample) (GstPlayBin3 * playbin, GstCaps * caps);
538 /* get audio/video/text pad for a stream */
539 GstPad *(*get_video_pad) (GstPlayBin3 * playbin, gint stream);
540 GstPad *(*get_audio_pad) (GstPlayBin3 * playbin, gint stream);
541 GstPad *(*get_text_pad) (GstPlayBin3 * playbin, gint stream);
545 #define DEFAULT_URI NULL
546 #define DEFAULT_SUBURI NULL
547 #define DEFAULT_SOURCE NULL
548 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
549 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
550 GST_PLAY_FLAG_SOFT_COLORBALANCE
551 #define DEFAULT_N_VIDEO 0
552 #define DEFAULT_CURRENT_VIDEO -1
553 #define DEFAULT_N_AUDIO 0
554 #define DEFAULT_CURRENT_AUDIO -1
555 #define DEFAULT_N_TEXT 0
556 #define DEFAULT_CURRENT_TEXT -1
557 #define DEFAULT_AUTO_SELECT_STREAMS TRUE
558 #define DEFAULT_SUBTITLE_ENCODING NULL
559 #define DEFAULT_AUDIO_SINK NULL
560 #define DEFAULT_VIDEO_SINK NULL
561 #define DEFAULT_VIS_PLUGIN NULL
562 #define DEFAULT_TEXT_SINK NULL
563 #define DEFAULT_VOLUME 1.0
564 #define DEFAULT_MUTE FALSE
565 #define DEFAULT_FRAME NULL
566 #define DEFAULT_FONT_DESC NULL
567 #define DEFAULT_CONNECTION_SPEED 0
568 #define DEFAULT_BUFFER_DURATION -1
569 #define DEFAULT_BUFFER_SIZE -1
570 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
587 PROP_AUTO_SELECT_STREAMS,
588 PROP_SUBTITLE_ENCODING,
593 PROP_VIDEO_STREAM_COMBINER,
594 PROP_AUDIO_STREAM_COMBINER,
595 PROP_TEXT_STREAM_COMBINER,
600 PROP_CONNECTION_SPEED,
602 PROP_BUFFER_DURATION,
604 PROP_RING_BUFFER_MAX_SIZE,
605 PROP_FORCE_ASPECT_RATIO,
615 SIGNAL_ABOUT_TO_FINISH,
616 SIGNAL_CONVERT_SAMPLE,
617 SIGNAL_VIDEO_CHANGED,
618 SIGNAL_AUDIO_CHANGED,
620 SIGNAL_VIDEO_TAGS_CHANGED,
621 SIGNAL_AUDIO_TAGS_CHANGED,
622 SIGNAL_TEXT_TAGS_CHANGED,
623 SIGNAL_GET_VIDEO_TAGS,
624 SIGNAL_GET_AUDIO_TAGS,
625 SIGNAL_GET_TEXT_TAGS,
626 SIGNAL_GET_VIDEO_PAD,
627 SIGNAL_GET_AUDIO_PAD,
633 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
634 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
636 static void gst_play_bin3_class_init (GstPlayBin3Class * klass);
637 static void gst_play_bin3_init (GstPlayBin3 * playbin);
638 static void gst_play_bin3_finalize (GObject * object);
640 static void gst_play_bin3_set_property (GObject * object, guint prop_id,
641 const GValue * value, GParamSpec * spec);
642 static void gst_play_bin3_get_property (GObject * object, guint prop_id,
643 GValue * value, GParamSpec * spec);
645 static GstStateChangeReturn gst_play_bin3_change_state (GstElement * element,
646 GstStateChange transition);
648 static void gst_play_bin3_handle_message (GstBin * bin, GstMessage * message);
649 static gboolean gst_play_bin3_query (GstElement * element, GstQuery * query);
650 static void gst_play_bin3_set_context (GstElement * element,
651 GstContext * context);
652 static gboolean gst_play_bin3_send_event (GstElement * element,
655 static GstTagList *gst_play_bin3_get_video_tags (GstPlayBin3 * playbin,
657 static GstTagList *gst_play_bin3_get_audio_tags (GstPlayBin3 * playbin,
659 static GstTagList *gst_play_bin3_get_text_tags (GstPlayBin3 * playbin,
662 static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin,
665 static GstPad *gst_play_bin3_get_video_pad (GstPlayBin3 * playbin, gint stream);
666 static GstPad *gst_play_bin3_get_audio_pad (GstPlayBin3 * playbin, gint stream);
667 static GstPad *gst_play_bin3_get_text_pad (GstPlayBin3 * playbin, gint stream);
669 static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin,
672 static void no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin);
673 static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
674 GstPlayBin3 * playbin);
676 static gint select_stream_cb (GstElement * decodebin,
677 GstStreamCollection * collection, GstStream * stream,
678 GstPlayBin3 * playbin);
680 static void do_stream_selection (GstPlayBin3 * playbin);
681 static void notify_tags_cb (GstStreamCollection * collection,
682 GstStream * stream, GParamSpec * pspec, GstPlayBin3 * playbin);
683 static void notify_tags_for_stream (GstPlayBin3 * playbin,
684 GstStreamCollection * collection, GstStream * stream);
686 static GstElementClass *parent_class;
688 static guint gst_play_bin3_signals[LAST_SIGNAL] = { 0 };
690 #define REMOVE_SIGNAL(obj,id) \
692 g_signal_handler_disconnect (obj, id); \
696 static void gst_play_bin3_overlay_init (gpointer g_iface,
697 gpointer g_iface_data);
698 static void gst_play_bin3_navigation_init (gpointer g_iface,
699 gpointer g_iface_data);
700 static void gst_play_bin3_colorbalance_init (gpointer g_iface,
701 gpointer g_iface_data);
704 gst_play_bin3_get_type (void)
706 static GType gst_play_bin3_type = 0;
708 if (!gst_play_bin3_type) {
709 static const GTypeInfo gst_play_bin3_info = {
710 sizeof (GstPlayBin3Class),
713 (GClassInitFunc) gst_play_bin3_class_init,
716 sizeof (GstPlayBin3),
718 (GInstanceInitFunc) gst_play_bin3_init,
721 static const GInterfaceInfo svol_info = {
724 static const GInterfaceInfo ov_info = {
725 gst_play_bin3_overlay_init,
728 static const GInterfaceInfo nav_info = {
729 gst_play_bin3_navigation_init,
732 static const GInterfaceInfo col_info = {
733 gst_play_bin3_colorbalance_init,
737 gst_play_bin3_type = g_type_register_static (GST_TYPE_PIPELINE,
738 "GstPlayBin3", &gst_play_bin3_info, 0);
740 g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_STREAM_VOLUME,
742 g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_VIDEO_OVERLAY,
744 g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_NAVIGATION,
746 g_type_add_interface_static (gst_play_bin3_type, GST_TYPE_COLOR_BALANCE,
750 return gst_play_bin3_type;
754 gst_play_bin3_class_init (GstPlayBin3Class * klass)
756 GObjectClass *gobject_klass;
757 GstElementClass *gstelement_klass;
758 GstBinClass *gstbin_klass;
760 gobject_klass = (GObjectClass *) klass;
761 gstelement_klass = (GstElementClass *) klass;
762 gstbin_klass = (GstBinClass *) klass;
764 parent_class = g_type_class_peek_parent (klass);
766 gobject_klass->set_property = gst_play_bin3_set_property;
767 gobject_klass->get_property = gst_play_bin3_get_property;
769 gobject_klass->finalize = gst_play_bin3_finalize;
774 * Set the next URI that playbin will play. This property can be set from the
775 * about-to-finish signal to queue the next media file.
777 g_object_class_install_property (gobject_klass, PROP_URI,
778 g_param_spec_string ("uri", "URI", "URI of the media to play",
779 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
782 * GstPlayBin3:current-uri
784 * The currently playing uri.
786 g_object_class_install_property (gobject_klass, PROP_CURRENT_URI,
787 g_param_spec_string ("current-uri", "Current URI",
788 "The currently playing URI", NULL,
789 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
794 * Set the next subtitle URI that playbin will play. This property can be
795 * set from the about-to-finish signal to queue the next subtitle media file.
797 g_object_class_install_property (gobject_klass, PROP_SUBURI,
798 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
799 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
802 * GstPlayBin3:current-suburi
804 * The currently playing subtitle uri.
806 g_object_class_install_property (gobject_klass, PROP_CURRENT_SUBURI,
807 g_param_spec_string ("current-suburi", "Current .sub-URI",
808 "The currently playing URI of a subtitle",
809 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
811 g_object_class_install_property (gobject_klass, PROP_SOURCE,
812 g_param_spec_object ("source", "Source", "Source element",
813 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
818 * Control the behaviour of playbin.
820 g_object_class_install_property (gobject_klass, PROP_FLAGS,
821 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
822 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
823 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
826 * GstPlayBin3:n-video
828 * Get the total number of available video streams.
830 g_object_class_install_property (gobject_klass, PROP_N_VIDEO,
831 g_param_spec_int ("n-video", "Number Video",
832 "Total number of video streams", 0, G_MAXINT, 0,
833 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
835 * GstPlayBin3:current-video
837 * Get or set the currently playing video stream. By default the first video
838 * stream with data is played.
840 g_object_class_install_property (gobject_klass, PROP_CURRENT_VIDEO,
841 g_param_spec_int ("current-video", "Current Video",
842 "Currently playing video stream (-1 = auto)",
843 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
845 * GstPlayBin3:n-audio
847 * Get the total number of available audio streams.
849 g_object_class_install_property (gobject_klass, PROP_N_AUDIO,
850 g_param_spec_int ("n-audio", "Number Audio",
851 "Total number of audio streams", 0, G_MAXINT, 0,
852 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
854 * GstPlayBin3:current-audio
856 * Get or set the currently playing audio stream. By default the first audio
857 * stream with data is played.
859 g_object_class_install_property (gobject_klass, PROP_CURRENT_AUDIO,
860 g_param_spec_int ("current-audio", "Current audio",
861 "Currently playing audio stream (-1 = auto)",
862 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
866 * Get the total number of available subtitle streams.
868 g_object_class_install_property (gobject_klass, PROP_N_TEXT,
869 g_param_spec_int ("n-text", "Number Text",
870 "Total number of text streams", 0, G_MAXINT, 0,
871 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
873 * GstPlayBin3:current-text:
875 * Get or set the currently playing subtitle stream. By default the first
876 * subtitle stream with data is played.
878 g_object_class_install_property (gobject_klass, PROP_CURRENT_TEXT,
879 g_param_spec_int ("current-text", "Current Text",
880 "Currently playing text stream (-1 = auto)",
881 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
884 * GstPlayBin3::auto-select-streams:
886 * If TRUE the playbin will respond to stream-collection messages
887 * by sending a SELECT_STREAMS event to decodebin. Set to FALSE
888 * if the application will manage stream selection. This property
889 * will automatically be set to FALSE if playbin receives a select-streams
890 * event from the application, but setting it explicitly avoids any
891 * races where playbin mind send a select-streams event before the
894 g_object_class_install_property (gobject_klass, PROP_AUTO_SELECT_STREAMS,
895 g_param_spec_boolean ("auto-select-streams", "Automatic Select-Streams",
896 "Whether playbin should respond to stream-collection messags with select-streams events",
897 DEFAULT_AUTO_SELECT_STREAMS,
898 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
900 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
901 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
902 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
903 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
904 "be checked for an encoding to use. If that is not set either, "
905 "ISO-8859-15 will be assumed.", NULL,
906 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
908 g_object_class_install_property (gobject_klass, PROP_VIDEO_FILTER,
909 g_param_spec_object ("video-filter", "Video filter",
910 "the video filter(s) to apply, if possible",
911 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
912 g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
913 g_param_spec_object ("audio-filter", "Audio filter",
914 "the audio filter(s) to apply, if possible",
915 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
916 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
917 g_param_spec_object ("video-sink", "Video Sink",
918 "the video output element to use (NULL = default sink)",
919 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
920 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
921 g_param_spec_object ("audio-sink", "Audio Sink",
922 "the audio output element to use (NULL = default sink)",
923 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
924 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
925 g_param_spec_object ("vis-plugin", "Vis plugin",
926 "the visualization element to use (NULL = default)",
927 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
928 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
929 g_param_spec_object ("text-sink", "Text plugin",
930 "the text output element to use (NULL = default subtitleoverlay)",
931 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
933 * GstPlayBin3:video-stream-combiner
935 * Get or set the current video stream combiner. By default, an input-selector
936 * is created and deleted as-needed.
938 g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
939 g_param_spec_object ("video-stream-combiner", "Video stream combiner",
940 "Current video stream combiner (NULL = input-selector)",
941 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
943 * GstPlayBin3:audio-stream-combiner
945 * Get or set the current audio stream combiner. By default, an input-selector
946 * is created and deleted as-needed.
948 g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
949 g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
950 "Current audio stream combiner (NULL = input-selector)",
951 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
953 * GstPlayBin3:text-stream-combiner
955 * Get or set the current text stream combiner. By default, an input-selector
956 * is created and deleted as-needed.
958 g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
959 g_param_spec_object ("text-stream-combiner", "Text stream combiner",
960 "Current text stream combiner (NULL = input-selector)",
961 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
964 * GstPlayBin3:volume:
966 * Get or set the current audio stream volume. 1.0 means 100%,
967 * 0.0 means mute. This uses a linear volume scale.
970 g_object_class_install_property (gobject_klass, PROP_VOLUME,
971 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
972 0.0, VOLUME_MAX_DOUBLE, 1.0,
973 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
974 g_object_class_install_property (gobject_klass, PROP_MUTE,
975 g_param_spec_boolean ("mute", "Mute",
976 "Mute the audio channel without changing the volume", FALSE,
977 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
980 * GstPlayBin3:sample:
981 * @playbin: a #GstPlayBin3
983 * Get the currently rendered or prerolled sample in the video sink.
984 * The #GstCaps in the sample will describe the format of the buffer.
986 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
987 g_param_spec_boxed ("sample", "Sample",
988 "The last sample (NULL = no video available)",
989 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
991 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
992 g_param_spec_string ("subtitle-font-desc",
993 "Subtitle font description",
994 "Pango font description of font "
995 "to be used for subtitle rendering", NULL,
996 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
998 g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
999 g_param_spec_uint64 ("connection-speed", "Connection Speed",
1000 "Network connection speed in kbps (0 = unknown)",
1001 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
1002 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1004 g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
1005 g_param_spec_int ("buffer-size", "Buffer size (bytes)",
1006 "Buffer size when buffering network streams",
1007 -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
1008 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1009 g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
1010 g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
1011 "Buffer duration when buffering network streams",
1012 -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
1013 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1015 * GstPlayBin3:av-offset:
1017 * Control the synchronisation offset between the audio and video streams.
1018 * Positive values make the audio ahead of the video and negative values make
1019 * the audio go behind the video.
1021 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
1022 g_param_spec_int64 ("av-offset", "AV Offset",
1023 "The synchronisation offset between audio and video in nanoseconds",
1024 G_MININT64, G_MAXINT64, 0,
1025 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1028 * GstPlayBin3:ring-buffer-max-size
1030 * The maximum size of the ring buffer in bytes. If set to 0, the ring
1031 * buffer is disabled. Default 0.
1033 g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
1034 g_param_spec_uint64 ("ring-buffer-max-size",
1035 "Max. ring buffer size (bytes)",
1036 "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
1037 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
1038 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1041 * GstPlayBin3::force-aspect-ratio:
1043 * Requests the video sink to enforce the video display aspect ratio.
1045 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
1046 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
1047 "When enabled, scaling will respect original aspect ratio", TRUE,
1048 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1051 * GstPlayBin3::video-multiview-mode:
1053 * Set the stereoscopic mode for video streams that don't contain
1054 * any information in the stream, so they can be correctly played
1055 * as 3D streams. If a video already has multiview information
1056 * encoded, this property can override other modes in the set,
1057 * but cannot be used to re-interpret MVC or mixed-mono streams.
1059 * See Also: The #GstPlayBin3::video-multiview-flags property
1062 g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_MODE,
1063 g_param_spec_enum ("video-multiview-mode",
1064 "Multiview Mode Override",
1065 "Re-interpret a video stream as one of several frame-packed stereoscopic modes.",
1066 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
1067 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE,
1068 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1071 * GstPlayBin3::video-multiview-flags:
1073 * When overriding the multiview mode of an input stream,
1074 * these flags modify details of the view layout.
1076 * See Also: The #GstPlayBin3::video-multiview-mode property
1078 g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_FLAGS,
1079 g_param_spec_flags ("video-multiview-flags",
1080 "Multiview Flags Override",
1081 "Override details of the multiview frame layout",
1082 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
1083 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1086 * GstPlayBin3::about-to-finish
1087 * @playbin: a #GstPlayBin3
1089 * This signal is emitted when the current uri is about to finish. You can
1090 * set the uri and suburi to make sure that playback continues.
1092 * This signal is emitted from the context of a GStreamer streaming thread.
1094 gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
1095 g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
1097 G_STRUCT_OFFSET (GstPlayBin3Class, about_to_finish), NULL, NULL,
1098 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1101 * GstPlayBin3::video-changed
1102 * @playbin: a #GstPlayBin3
1104 * This signal is emitted whenever the number or order of the video
1105 * streams has changed. The application will most likely want to select
1106 * a new video stream.
1108 * This signal is usually emitted from the context of a GStreamer streaming
1109 * thread. You can use gst_message_new_application() and
1110 * gst_element_post_message() to notify your application's main thread.
1112 /* FIXME 0.11: turn video-changed signal into message? */
1113 gst_play_bin3_signals[SIGNAL_VIDEO_CHANGED] =
1114 g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass),
1116 G_STRUCT_OFFSET (GstPlayBin3Class, video_changed), NULL, NULL,
1117 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1119 * GstPlayBin3::audio-changed
1120 * @playbin: a #GstPlayBin3
1122 * This signal is emitted whenever the number or order of the audio
1123 * streams has changed. The application will most likely want to select
1124 * a new audio stream.
1126 * This signal may be emitted from the context of a GStreamer streaming thread.
1127 * You can use gst_message_new_application() and gst_element_post_message()
1128 * to notify your application's main thread.
1130 /* FIXME 0.11: turn audio-changed signal into message? */
1131 gst_play_bin3_signals[SIGNAL_AUDIO_CHANGED] =
1132 g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass),
1134 G_STRUCT_OFFSET (GstPlayBin3Class, audio_changed), NULL, NULL,
1135 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1137 * GstPlayBin3::text-changed
1138 * @playbin: a #GstPlayBin3
1140 * This signal is emitted whenever the number or order of the text
1141 * streams has changed. The application will most likely want to select
1142 * a new text stream.
1144 * This signal may be emitted from the context of a GStreamer streaming thread.
1145 * You can use gst_message_new_application() and gst_element_post_message()
1146 * to notify your application's main thread.
1148 /* FIXME 0.11: turn text-changed signal into message? */
1149 gst_play_bin3_signals[SIGNAL_TEXT_CHANGED] =
1150 g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
1152 G_STRUCT_OFFSET (GstPlayBin3Class, text_changed), NULL, NULL,
1153 g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
1156 * GstPlayBin3::video-tags-changed
1157 * @playbin: a #GstPlayBin3
1158 * @stream: stream index with changed tags
1160 * This signal is emitted whenever the tags of a video stream have changed.
1161 * The application will most likely want to get the new tags.
1163 * This signal may be emitted from the context of a GStreamer streaming thread.
1164 * You can use gst_message_new_application() and gst_element_post_message()
1165 * to notify your application's main thread.
1167 gst_play_bin3_signals[SIGNAL_VIDEO_TAGS_CHANGED] =
1168 g_signal_new ("video-tags-changed", G_TYPE_FROM_CLASS (klass),
1170 G_STRUCT_OFFSET (GstPlayBin3Class, video_tags_changed), NULL, NULL,
1171 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1174 * GstPlayBin3::audio-tags-changed
1175 * @playbin: a #GstPlayBin3
1176 * @stream: stream index with changed tags
1178 * This signal is emitted whenever the tags of an audio stream have changed.
1179 * The application will most likely want to get the new tags.
1181 * This signal may be emitted from the context of a GStreamer streaming thread.
1182 * You can use gst_message_new_application() and gst_element_post_message()
1183 * to notify your application's main thread.
1185 gst_play_bin3_signals[SIGNAL_AUDIO_TAGS_CHANGED] =
1186 g_signal_new ("audio-tags-changed", G_TYPE_FROM_CLASS (klass),
1188 G_STRUCT_OFFSET (GstPlayBin3Class, audio_tags_changed), NULL, NULL,
1189 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1192 * GstPlayBin3::text-tags-changed
1193 * @playbin: a #GstPlayBin3
1194 * @stream: stream index with changed tags
1196 * This signal is emitted whenever the tags of a text stream have changed.
1197 * The application will most likely want to get the new tags.
1199 * This signal may be emitted from the context of a GStreamer streaming thread.
1200 * You can use gst_message_new_application() and gst_element_post_message()
1201 * to notify your application's main thread.
1203 gst_play_bin3_signals[SIGNAL_TEXT_TAGS_CHANGED] =
1204 g_signal_new ("text-tags-changed", G_TYPE_FROM_CLASS (klass),
1206 G_STRUCT_OFFSET (GstPlayBin3Class, text_tags_changed), NULL, NULL,
1207 g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
1210 * GstPlayBin3::source-setup:
1211 * @playbin: a #GstPlayBin3
1212 * @source: source element
1214 * This signal is emitted after the source element has been created, so
1215 * it can be configured by setting additional properties (e.g. set a
1216 * proxy server for an http source, or set the device and read speed for
1217 * an audio cd source). This is functionally equivalent to connecting to
1218 * the notify::source signal, but more convenient.
1220 * This signal is usually emitted from the context of a GStreamer streaming
1223 gst_play_bin3_signals[SIGNAL_SOURCE_SETUP] =
1224 g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
1225 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1226 g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1229 * GstPlayBin3::get-video-tags
1230 * @playbin: a #GstPlayBin3
1231 * @stream: a video stream number
1233 * Action signal to retrieve the tags of a specific video stream number.
1234 * This information can be used to select a stream.
1236 * Returns: a GstTagList with tags or NULL when the stream number does not
1239 gst_play_bin3_signals[SIGNAL_GET_VIDEO_TAGS] =
1240 g_signal_new ("get-video-tags", G_TYPE_FROM_CLASS (klass),
1241 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1242 G_STRUCT_OFFSET (GstPlayBin3Class, get_video_tags), NULL, NULL,
1243 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1245 * GstPlayBin3::get-audio-tags
1246 * @playbin: a #GstPlayBin3
1247 * @stream: an audio stream number
1249 * Action signal to retrieve the tags of a specific audio stream number.
1250 * This information can be used to select a stream.
1252 * Returns: a GstTagList with tags or NULL when the stream number does not
1255 gst_play_bin3_signals[SIGNAL_GET_AUDIO_TAGS] =
1256 g_signal_new ("get-audio-tags", G_TYPE_FROM_CLASS (klass),
1257 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1258 G_STRUCT_OFFSET (GstPlayBin3Class, get_audio_tags), NULL, NULL,
1259 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1261 * GstPlayBin3::get-text-tags
1262 * @playbin: a #GstPlayBin3
1263 * @stream: a text stream number
1265 * Action signal to retrieve the tags of a specific text stream number.
1266 * This information can be used to select a stream.
1268 * Returns: a GstTagList with tags or NULL when the stream number does not
1271 gst_play_bin3_signals[SIGNAL_GET_TEXT_TAGS] =
1272 g_signal_new ("get-text-tags", G_TYPE_FROM_CLASS (klass),
1273 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1274 G_STRUCT_OFFSET (GstPlayBin3Class, get_text_tags), NULL, NULL,
1275 g_cclosure_marshal_generic, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1277 * GstPlayBin3::convert-sample
1278 * @playbin: a #GstPlayBin3
1279 * @caps: the target format of the frame
1281 * Action signal to retrieve the currently playing video frame in the format
1282 * specified by @caps.
1283 * If @caps is %NULL, no conversion will be performed and this function is
1284 * equivalent to the #GstPlayBin3::frame property.
1286 * Returns: a #GstSample of the current video frame converted to #caps.
1287 * The caps on the sample will describe the final layout of the buffer data.
1288 * %NULL is returned when no current buffer can be retrieved or when the
1289 * conversion failed.
1291 gst_play_bin3_signals[SIGNAL_CONVERT_SAMPLE] =
1292 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
1293 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1294 G_STRUCT_OFFSET (GstPlayBin3Class, convert_sample), NULL, NULL,
1295 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
1298 * GstPlayBin3::get-video-pad
1299 * @playbin: a #GstPlayBin3
1300 * @stream: a video stream number
1302 * Action signal to retrieve the stream-combiner sinkpad for a specific
1304 * This pad can be used for notifications of caps changes, stream-specific
1307 * Returns: a #GstPad, or NULL when the stream number does not exist.
1309 gst_play_bin3_signals[SIGNAL_GET_VIDEO_PAD] =
1310 g_signal_new ("get-video-pad", G_TYPE_FROM_CLASS (klass),
1311 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1312 G_STRUCT_OFFSET (GstPlayBin3Class, get_video_pad), NULL, NULL,
1313 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1315 * GstPlayBin3::get-audio-pad
1316 * @playbin: a #GstPlayBin3
1317 * @stream: an audio stream number
1319 * Action signal to retrieve the stream-combiner sinkpad for a specific
1321 * This pad can be used for notifications of caps changes, stream-specific
1324 * Returns: a #GstPad, or NULL when the stream number does not exist.
1326 gst_play_bin3_signals[SIGNAL_GET_AUDIO_PAD] =
1327 g_signal_new ("get-audio-pad", G_TYPE_FROM_CLASS (klass),
1328 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1329 G_STRUCT_OFFSET (GstPlayBin3Class, get_audio_pad), NULL, NULL,
1330 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1332 * GstPlayBin3::get-text-pad
1333 * @playbin: a #GstPlayBin3
1334 * @stream: a text stream number
1336 * Action signal to retrieve the stream-combiner sinkpad for a specific
1338 * This pad can be used for notifications of caps changes, stream-specific
1341 * Returns: a #GstPad, or NULL when the stream number does not exist.
1343 gst_play_bin3_signals[SIGNAL_GET_TEXT_PAD] =
1344 g_signal_new ("get-text-pad", G_TYPE_FROM_CLASS (klass),
1345 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1346 G_STRUCT_OFFSET (GstPlayBin3Class, get_text_pad), NULL, NULL,
1347 g_cclosure_marshal_generic, GST_TYPE_PAD, 1, G_TYPE_INT);
1349 klass->get_video_tags = gst_play_bin3_get_video_tags;
1350 klass->get_audio_tags = gst_play_bin3_get_audio_tags;
1351 klass->get_text_tags = gst_play_bin3_get_text_tags;
1353 klass->convert_sample = gst_play_bin3_convert_sample;
1355 klass->get_video_pad = gst_play_bin3_get_video_pad;
1356 klass->get_audio_pad = gst_play_bin3_get_audio_pad;
1357 klass->get_text_pad = gst_play_bin3_get_text_pad;
1359 gst_element_class_set_static_metadata (gstelement_klass,
1360 "Player Bin 3", "Generic/Bin/Player",
1361 "Autoplug and play media from an uri",
1362 "Wim Taymans <wim.taymans@gmail.com>");
1364 gstelement_klass->change_state =
1365 GST_DEBUG_FUNCPTR (gst_play_bin3_change_state);
1366 gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin3_query);
1367 gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin3_set_context);
1368 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin3_send_event);
1370 gstbin_klass->handle_message =
1371 GST_DEBUG_FUNCPTR (gst_play_bin3_handle_message);
1375 do_async_start (GstPlayBin3 * playbin)
1377 GstMessage *message;
1379 playbin->async_pending = TRUE;
1381 message = gst_message_new_async_start (GST_OBJECT_CAST (playbin));
1382 GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1387 do_async_done (GstPlayBin3 * playbin)
1389 GstMessage *message;
1391 if (playbin->async_pending) {
1392 GST_DEBUG_OBJECT (playbin, "posting ASYNC_DONE");
1394 gst_message_new_async_done (GST_OBJECT_CAST (playbin),
1395 GST_CLOCK_TIME_NONE);
1396 GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1399 playbin->async_pending = FALSE;
1403 /* init combiners. The combiner is found by finding the first prefix that
1404 * matches the media. */
1406 init_combiners (GstPlayBin3 * playbin)
1410 /* store the array for the different channels */
1411 for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
1412 playbin->channels[i] = g_ptr_array_new ();
1414 playbin->combiner[PLAYBIN_STREAM_AUDIO].media_type = "audio";
1415 playbin->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
1416 playbin->combiner[PLAYBIN_STREAM_AUDIO].channels = playbin->channels[0];
1417 playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
1418 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1419 playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1;
1421 playbin->combiner[PLAYBIN_STREAM_VIDEO].media_type = "video";
1422 playbin->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
1423 playbin->combiner[PLAYBIN_STREAM_VIDEO].channels = playbin->channels[1];
1424 playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
1425 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1426 playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1;
1428 playbin->combiner[PLAYBIN_STREAM_TEXT].media_type = "text";
1429 playbin->combiner[PLAYBIN_STREAM_TEXT].get_media_caps =
1430 gst_subtitle_overlay_create_factory_caps;
1431 playbin->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
1432 playbin->combiner[PLAYBIN_STREAM_TEXT].channels = playbin->channels[2];
1433 playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
1434 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1435 playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1;
1438 /* Update the combiner information to be in sync with the current collection */
1440 update_combiner_info (GstPlayBin3 * playbin)
1444 if (playbin->collection == NULL)
1447 GST_DEBUG_OBJECT (playbin, "Updating combiner info");
1449 /* Wipe current combiner streams */
1450 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams, TRUE);
1451 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE);
1452 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
1453 playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
1454 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1455 playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1;
1456 playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
1457 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1458 playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1;
1459 playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
1460 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
1461 playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1;
1463 len = gst_stream_collection_get_size (playbin->collection);
1464 for (i = 0; i < len; i++) {
1466 gst_stream_collection_get_stream (playbin->collection, i);
1467 GstStreamType stype = gst_stream_get_stream_type (stream);
1470 case GST_STREAM_TYPE_AUDIO:
1471 g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams,
1472 gst_object_ref (stream));
1474 case GST_STREAM_TYPE_VIDEO:
1475 g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams,
1476 gst_object_ref (stream));
1478 case GST_STREAM_TYPE_TEXT:
1479 g_ptr_array_add (playbin->combiner[PLAYBIN_STREAM_TEXT].streams,
1480 gst_object_ref (stream));
1487 GST_DEBUG_OBJECT (playbin, "There are %d audio streams",
1488 playbin->combiner[PLAYBIN_STREAM_AUDIO].streams->len);
1489 GST_DEBUG_OBJECT (playbin, "There are %d video streams",
1490 playbin->combiner[PLAYBIN_STREAM_VIDEO].streams->len);
1491 GST_DEBUG_OBJECT (playbin, "There are %d text streams",
1492 playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len);
1495 /* Set the given stream as the selected stream */
1497 set_selected_stream (GstPlayBin3 * playbin, GstStream * stream)
1499 GstSourceCombine *combine = NULL;
1501 switch (gst_stream_get_stream_type (stream)) {
1502 case GST_STREAM_TYPE_AUDIO:
1503 combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO];
1505 case GST_STREAM_TYPE_VIDEO:
1506 combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO];
1508 case GST_STREAM_TYPE_TEXT:
1509 combine = &playbin->combiner[PLAYBIN_STREAM_TEXT];
1516 if (combine->combiner == NULL) {
1519 GST_DEBUG_OBJECT (playbin, "Called for %s (%p)",
1520 gst_stream_get_stream_id (stream), combine->combiner);
1522 combine->current_stream = -1;
1523 len = combine->streams->len;
1524 for (i = 0; i < len; i++) {
1525 GstStream *cand = g_ptr_array_index (combine->streams, i);
1526 if (cand == stream) {
1527 GST_DEBUG_OBJECT (playbin, "Setting current to %d", i);
1528 combine->current_stream = i;
1537 init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
1539 g_mutex_init (&group->lock);
1541 group->stream_changed_pending = FALSE;
1542 g_mutex_init (&group->stream_changed_pending_lock);
1544 group->playbin = playbin;
1548 free_group (GstPlayBin3 * playbin, GstSourceGroup * group)
1550 g_free (group->uri);
1551 g_free (group->suburi);
1553 g_mutex_clear (&group->lock);
1554 group->stream_changed_pending = FALSE;
1555 g_mutex_clear (&group->stream_changed_pending_lock);
1557 if (group->pending_buffering_msg)
1558 gst_message_unref (group->pending_buffering_msg);
1559 group->pending_buffering_msg = NULL;
1561 gst_object_replace ((GstObject **) & group->audio_sink, NULL);
1562 gst_object_replace ((GstObject **) & group->video_sink, NULL);
1563 gst_object_replace ((GstObject **) & group->text_sink, NULL);
1567 notify_volume_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin3 * playbin)
1569 g_object_notify (G_OBJECT (playbin), "volume");
1573 notify_mute_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin3 * playbin)
1575 g_object_notify (G_OBJECT (playbin), "mute");
1579 colorbalance_value_changed_cb (GstColorBalance * balance,
1580 GstColorBalanceChannel * channel, gint value, GstPlayBin3 * playbin)
1582 gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
1586 compare_factories_func (gconstpointer p1, gconstpointer p2)
1588 GstPluginFeature *f1, *f2;
1589 gboolean is_sink1, is_sink2;
1590 gboolean is_parser1, is_parser2;
1592 f1 = (GstPluginFeature *) p1;
1593 f2 = (GstPluginFeature *) p2;
1595 is_sink1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1596 GST_ELEMENT_FACTORY_TYPE_SINK);
1597 is_sink2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1598 GST_ELEMENT_FACTORY_TYPE_SINK);
1599 is_parser1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1600 GST_ELEMENT_FACTORY_TYPE_PARSER);
1601 is_parser2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1602 GST_ELEMENT_FACTORY_TYPE_PARSER);
1604 /* First we want all sinks as we prefer a sink if it directly
1605 * supports the current caps */
1606 if (is_sink1 && !is_sink2)
1608 else if (!is_sink1 && is_sink2)
1611 /* Then we want all parsers as we always want to plug parsers
1612 * before decoders */
1613 if (is_parser1 && !is_parser2)
1615 else if (!is_parser1 && is_parser2)
1618 /* And if it's a both a parser or sink we first sort by rank
1619 * and then by factory name */
1620 return gst_plugin_feature_rank_compare_func (p1, p2);
1623 /* Must be called with elements lock! */
1625 gst_play_bin3_update_elements_list (GstPlayBin3 * playbin)
1630 cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
1632 if (!playbin->elements || playbin->elements_cookie != cookie) {
1633 if (playbin->elements)
1634 gst_plugin_feature_list_free (playbin->elements);
1636 gst_element_factory_list_get_elements
1637 (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1639 gst_element_factory_list_get_elements
1640 (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
1641 playbin->elements = g_list_concat (res, tmp);
1642 playbin->elements = g_list_sort (playbin->elements, compare_factories_func);
1645 if (!playbin->aelements || playbin->elements_cookie != cookie) {
1646 if (playbin->aelements)
1647 g_sequence_free (playbin->aelements);
1648 playbin->aelements = avelements_create (playbin, TRUE);
1651 if (!playbin->velements || playbin->elements_cookie != cookie) {
1652 if (playbin->velements)
1653 g_sequence_free (playbin->velements);
1654 playbin->velements = avelements_create (playbin, FALSE);
1657 playbin->elements_cookie = cookie;
1661 gst_play_bin3_init (GstPlayBin3 * playbin)
1663 g_rec_mutex_init (&playbin->lock);
1664 g_mutex_init (&playbin->dyn_lock);
1666 /* assume we can create an input-selector */
1667 playbin->have_selector = TRUE;
1669 playbin->do_stream_selections = DEFAULT_AUTO_SELECT_STREAMS;
1671 init_combiners (playbin);
1674 playbin->curr_group = &playbin->groups[0];
1675 playbin->next_group = &playbin->groups[1];
1676 init_group (playbin, &playbin->groups[0]);
1677 init_group (playbin, &playbin->groups[1]);
1679 /* first filter out the interesting element factories */
1680 g_mutex_init (&playbin->elements_lock);
1684 g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
1686 gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
1687 gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1688 /* Connect to notify::volume and notify::mute signals for proxying */
1689 g_signal_connect (playbin->playsink, "notify::volume",
1690 G_CALLBACK (notify_volume_cb), playbin);
1691 g_signal_connect (playbin->playsink, "notify::mute",
1692 G_CALLBACK (notify_mute_cb), playbin);
1693 g_signal_connect (playbin->playsink, "value-changed",
1694 G_CALLBACK (colorbalance_value_changed_cb), playbin);
1696 playbin->current_video = DEFAULT_CURRENT_VIDEO;
1697 playbin->current_audio = DEFAULT_CURRENT_AUDIO;
1698 playbin->current_text = DEFAULT_CURRENT_TEXT;
1700 playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
1701 playbin->buffer_size = DEFAULT_BUFFER_SIZE;
1702 playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
1704 playbin->force_aspect_ratio = TRUE;
1706 playbin->multiview_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
1707 playbin->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1711 gst_play_bin3_finalize (GObject * object)
1713 GstPlayBin3 *playbin;
1716 playbin = GST_PLAY_BIN3 (object);
1718 free_group (playbin, &playbin->groups[0]);
1719 free_group (playbin, &playbin->groups[1]);
1721 for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
1722 g_ptr_array_free (playbin->channels[i], TRUE);
1724 if (playbin->source)
1725 gst_object_unref (playbin->source);
1727 /* Setting states to NULL is safe here because playsink
1728 * will already be gone and none of these sinks will be
1729 * a child of playsink
1731 if (playbin->video_sink) {
1732 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
1733 gst_object_unref (playbin->video_sink);
1735 if (playbin->audio_sink) {
1736 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
1737 gst_object_unref (playbin->audio_sink);
1739 if (playbin->text_sink) {
1740 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
1741 gst_object_unref (playbin->text_sink);
1744 if (playbin->video_stream_combiner) {
1745 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
1746 gst_object_unref (playbin->video_stream_combiner);
1748 if (playbin->audio_stream_combiner) {
1749 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
1750 gst_object_unref (playbin->audio_stream_combiner);
1752 if (playbin->text_stream_combiner) {
1753 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
1754 gst_object_unref (playbin->text_stream_combiner);
1757 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_AUDIO].streams, TRUE);
1758 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE);
1759 g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
1761 if (playbin->decodebin)
1762 gst_object_unref (playbin->decodebin);
1764 if (playbin->elements)
1765 gst_plugin_feature_list_free (playbin->elements);
1767 if (playbin->aelements)
1768 g_sequence_free (playbin->aelements);
1770 if (playbin->velements)
1771 g_sequence_free (playbin->velements);
1773 g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref);
1775 g_rec_mutex_clear (&playbin->lock);
1776 g_mutex_clear (&playbin->dyn_lock);
1777 g_mutex_clear (&playbin->elements_lock);
1779 G_OBJECT_CLASS (parent_class)->finalize (object);
1783 gst_playbin_uri_is_valid (GstPlayBin3 * playbin, const gchar * uri)
1787 GST_LOG_OBJECT (playbin, "checking uri '%s'", uri);
1789 /* this just checks the protocol */
1790 if (!gst_uri_is_valid (uri))
1793 for (c = uri; *c != '\0'; ++c) {
1794 if (!g_ascii_isprint (*c))
1804 GST_WARNING_OBJECT (playbin, "uri '%s' not valid, character #%u",
1805 uri, (guint) ((guintptr) c - (guintptr) uri));
1811 gst_play_bin3_set_uri (GstPlayBin3 * playbin, const gchar * uri)
1813 GstSourceGroup *group;
1816 g_warning ("cannot set NULL uri");
1820 if (!gst_playbin_uri_is_valid (playbin, uri)) {
1821 if (g_str_has_prefix (uri, "file:")) {
1822 GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
1823 "sure to escape spaces and non-ASCII characters properly and specify "
1824 "an absolute path. Use gst_filename_to_uri() to convert filenames "
1827 /* GST_ERROR_OBJECT (playbin, "malformed URI '%s'", uri); */
1831 GST_PLAY_BIN3_LOCK (playbin);
1832 group = playbin->next_group;
1834 GST_SOURCE_GROUP_LOCK (group);
1835 /* store the uri in the next group we will play */
1836 g_free (group->uri);
1837 group->uri = g_strdup (uri);
1838 group->valid = TRUE;
1839 GST_SOURCE_GROUP_UNLOCK (group);
1841 GST_DEBUG ("set new uri to %s", uri);
1842 GST_PLAY_BIN3_UNLOCK (playbin);
1846 gst_play_bin3_set_suburi (GstPlayBin3 * playbin, const gchar * suburi)
1848 GstSourceGroup *group;
1850 GST_PLAY_BIN3_LOCK (playbin);
1851 group = playbin->next_group;
1853 GST_SOURCE_GROUP_LOCK (group);
1854 g_free (group->suburi);
1855 group->suburi = g_strdup (suburi);
1856 GST_SOURCE_GROUP_UNLOCK (group);
1858 GST_DEBUG ("setting new .sub uri to %s", suburi);
1860 GST_PLAY_BIN3_UNLOCK (playbin);
1864 gst_play_bin3_set_flags (GstPlayBin3 * playbin, GstPlayFlags flags)
1866 GstPlayFlags old_flags;
1867 old_flags = gst_play_sink_get_flags (playbin->playsink);
1869 if (flags != old_flags) {
1870 gst_play_sink_set_flags (playbin->playsink, flags);
1871 gst_play_sink_reconfigure (playbin->playsink);
1876 gst_play_bin3_get_flags (GstPlayBin3 * playbin)
1880 flags = gst_play_sink_get_flags (playbin->playsink);
1885 /* get the currently playing group or if nothing is playing, the next
1886 * group. Must be called with the PLAY_BIN_LOCK. */
1887 static GstSourceGroup *
1888 get_group (GstPlayBin3 * playbin)
1890 GstSourceGroup *result;
1892 if (!(result = playbin->curr_group))
1893 result = playbin->next_group;
1899 gst_play_bin3_get_pad_of_type (GstPlayBin3 * playbin, gint stream_type,
1902 GstPad *sinkpad = NULL;
1904 GST_PLAY_BIN3_LOCK (playbin);
1905 if (playbin->combiner[stream_type].combiner == NULL) {
1906 GST_DEBUG_OBJECT (playbin,
1907 "get-pad of type %d w/o custom-combiner. Returning playsink pad",
1909 sinkpad = playbin->combiner[stream_type].sinkpad;
1911 sinkpad = gst_object_ref (sinkpad);
1915 if (stream < playbin->channels[stream_type]->len) {
1916 sinkpad = g_ptr_array_index (playbin->channels[stream_type], stream);
1917 gst_object_ref (sinkpad);
1921 GST_PLAY_BIN3_UNLOCK (playbin);
1927 gst_play_bin3_get_video_pad (GstPlayBin3 * playbin, gint stream)
1929 return gst_play_bin3_get_pad_of_type (playbin, PLAYBIN_STREAM_VIDEO, stream);
1933 gst_play_bin3_get_audio_pad (GstPlayBin3 * playbin, gint stream)
1935 return gst_play_bin3_get_pad_of_type (playbin, PLAYBIN_STREAM_AUDIO, stream);
1939 gst_play_bin3_get_text_pad (GstPlayBin3 * playbin, gint stream)
1941 return gst_play_bin3_get_pad_of_type (playbin, PLAYBIN_STREAM_TEXT, stream);
1946 get_tags (GstPlayBin3 * playbin, GstStreamType type, gint stream_num)
1948 GstTagList *result = NULL;
1949 gint nb_streams = gst_stream_collection_get_size (playbin->collection);
1950 gint i, cur_idx = 0;
1952 /* Count the streams of the type we want to find the one numbered 'stream' */
1953 for (i = 0; i < nb_streams; i++) {
1955 gst_stream_collection_get_stream (playbin->collection, i);
1956 GstStreamType stream_type = gst_stream_get_stream_type (stream);
1957 if (stream_type != type)
1959 if (cur_idx == stream_num)
1960 return gst_stream_get_tags (stream);
1968 gst_play_bin3_get_video_tags (GstPlayBin3 * playbin, gint stream)
1972 GST_PLAY_BIN3_LOCK (playbin);
1973 result = get_tags (playbin, GST_STREAM_TYPE_VIDEO, stream);
1974 GST_PLAY_BIN3_UNLOCK (playbin);
1980 gst_play_bin3_get_audio_tags (GstPlayBin3 * playbin, gint stream)
1984 GST_PLAY_BIN3_LOCK (playbin);
1985 result = get_tags (playbin, GST_STREAM_TYPE_AUDIO, stream);
1986 GST_PLAY_BIN3_UNLOCK (playbin);
1992 gst_play_bin3_get_text_tags (GstPlayBin3 * playbin, gint stream)
1996 GST_PLAY_BIN3_LOCK (playbin);
1997 result = get_tags (playbin, GST_STREAM_TYPE_TEXT, stream);
1998 GST_PLAY_BIN3_UNLOCK (playbin);
2004 gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps)
2006 return gst_play_sink_convert_sample (playbin->playsink, caps);
2009 /* Returns current stream number, or -1 if none has been selected yet */
2011 get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine,
2012 GPtrArray * channels)
2014 /* Internal API cleanup would make this easier... */
2016 GstPad *pad, *current;
2017 GstObject *combiner = NULL;
2020 if (!combine->has_active_pad) {
2021 GST_WARNING_OBJECT (playbin,
2022 "combiner doesn't have the \"active-pad\" property");
2026 for (i = 0; i < channels->len; i++) {
2027 pad = g_ptr_array_index (channels, i);
2028 if ((combiner = gst_pad_get_parent (pad))) {
2029 g_object_get (combiner, "active-pad", ¤t, NULL);
2030 gst_object_unref (combiner);
2032 if (pad == current) {
2033 gst_object_unref (current);
2039 gst_object_unref (current);
2047 gst_play_bin3_send_custom_event (GstObject * combiner, const gchar * event_name)
2053 gboolean ret = FALSE;
2055 src = gst_element_get_static_pad (GST_ELEMENT_CAST (combiner), "src");
2056 peer = gst_pad_get_peer (src);
2058 s = gst_structure_new_empty (event_name);
2059 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
2060 gst_pad_send_event (peer, event);
2061 gst_object_unref (peer);
2064 gst_object_unref (src);
2069 gst_play_bin3_set_current_stream (GstPlayBin3 * playbin,
2070 gint stream_type, gint * current_value, gint stream,
2071 gboolean * flush_marker)
2073 GstSourceCombine *combine;
2074 GPtrArray *channels;
2077 GST_PLAY_BIN3_LOCK (playbin);
2078 /* This function is only called if the app sets
2079 * one of the current-* properties, which means it doesn't
2080 * handle collections or select-streams yet */
2081 playbin->do_stream_selections = TRUE;
2083 combine = playbin->combiner + stream_type;
2084 channels = playbin->channels[stream_type];
2086 GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
2087 stream_type_names[stream_type], *current_value, stream);
2089 if (combine->combiner == NULL) {
2090 /* FIXME: Check that the current_value is within range */
2091 *current_value = stream;
2092 do_stream_selection (playbin);
2093 GST_PLAY_BIN3_UNLOCK (playbin);
2097 GST_DEBUG_OBJECT (playbin, "Using old style combiner");
2099 if (!combine->has_active_pad)
2101 if (channels == NULL)
2104 if (stream == -1 || channels->len <= stream) {
2107 /* take channel from selected stream */
2108 sinkpad = g_ptr_array_index (channels, stream);
2112 gst_object_ref (sinkpad);
2113 GST_PLAY_BIN3_UNLOCK (playbin);
2116 GstObject *combiner;
2118 if ((combiner = gst_pad_get_parent (sinkpad))) {
2119 GstPad *old_sinkpad;
2121 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
2123 if (old_sinkpad != sinkpad) {
2124 /* FIXME: Is there actually any reason playsink
2125 * needs special names for each type of stream we flush? */
2126 gchar *flush_event_name = g_strdup_printf ("playsink-custom-%s-flush",
2127 stream_type_names[stream_type]);
2128 if (gst_play_bin3_send_custom_event (combiner, flush_event_name))
2129 *flush_marker = TRUE;
2130 g_free (flush_event_name);
2132 /* activate the selected pad */
2133 g_object_set (combiner, "active-pad", sinkpad, NULL);
2137 gst_object_unref (old_sinkpad);
2139 gst_object_unref (combiner);
2141 gst_object_unref (sinkpad);
2147 GST_PLAY_BIN3_UNLOCK (playbin);
2148 GST_WARNING_OBJECT (playbin,
2149 "can't switch %s, the stream combiner's sink pads don't have the \"active-pad\" property",
2150 stream_type_names[stream_type]);
2155 GST_PLAY_BIN3_UNLOCK (playbin);
2156 GST_DEBUG_OBJECT (playbin, "can't switch video, we have no channels");
2162 gst_play_bin3_set_current_video_stream (GstPlayBin3 * playbin, gint stream)
2164 return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_VIDEO,
2165 &playbin->current_video, stream, &playbin->video_pending_flush_finish);
2169 gst_play_bin3_set_current_audio_stream (GstPlayBin3 * playbin, gint stream)
2171 return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_AUDIO,
2172 &playbin->current_audio, stream, &playbin->audio_pending_flush_finish);
2176 gst_play_bin3_set_current_text_stream (GstPlayBin3 * playbin, gint stream)
2178 return gst_play_bin3_set_current_stream (playbin, PLAYBIN_STREAM_TEXT,
2179 &playbin->current_text, stream, &playbin->text_pending_flush_finish);
2183 source_combine_remove_pads (GstPlayBin3 * playbin, GstSourceCombine * combine)
2185 if (combine->sinkpad) {
2186 GST_LOG_OBJECT (playbin, "unlinking from sink");
2187 gst_pad_unlink (combine->srcpad, combine->sinkpad);
2190 GST_LOG_OBJECT (playbin, "release sink pad");
2191 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
2192 gst_object_unref (combine->sinkpad);
2193 combine->sinkpad = NULL;
2195 gst_object_unref (combine->srcpad);
2196 combine->srcpad = NULL;
2199 static GstPadProbeReturn
2200 block_serialized_data_cb (GstPad * pad, GstPadProbeInfo * info,
2203 if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) {
2204 GST_DEBUG_OBJECT (pad, "Letting non-serialized event %s pass",
2205 GST_EVENT_TYPE_NAME (info->data));
2206 return GST_PAD_PROBE_PASS;
2209 return GST_PAD_PROBE_OK;
2213 gst_play_bin3_set_sink (GstPlayBin3 * playbin, GstPlaySinkType type,
2214 const gchar * dbg, GstElement ** elem, GstElement * sink)
2216 GST_INFO_OBJECT (playbin, "Setting %s sink to %" GST_PTR_FORMAT, dbg, sink);
2218 gst_play_sink_set_sink (playbin->playsink, type, sink);
2221 gst_object_unref (*elem);
2222 *elem = sink ? gst_object_ref (sink) : NULL;
2226 gst_play_bin3_set_stream_combiner (GstPlayBin3 * playbin, GstElement ** elem,
2227 const gchar * dbg, GstElement * combiner)
2229 GST_INFO_OBJECT (playbin, "Setting %s stream combiner to %" GST_PTR_FORMAT,
2232 GST_PLAY_BIN3_LOCK (playbin);
2233 if (*elem != combiner) {
2238 gst_object_ref_sink (combiner);
2242 gst_object_unref (old);
2244 GST_LOG_OBJECT (playbin, "%s stream combiner now %" GST_PTR_FORMAT, dbg,
2246 GST_PLAY_BIN3_UNLOCK (playbin);
2250 gst_play_bin3_set_encoding (GstPlayBin3 * playbin, const gchar * encoding)
2254 GST_PLAY_BIN3_LOCK (playbin);
2256 /* set subtitles on decodebin. */
2257 if ((elem = playbin->decodebin))
2258 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2260 gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
2261 GST_PLAY_BIN3_UNLOCK (playbin);
2265 gst_play_bin3_set_property (GObject * object, guint prop_id,
2266 const GValue * value, GParamSpec * pspec)
2268 GstPlayBin3 *playbin = GST_PLAY_BIN3 (object);
2272 gst_play_bin3_set_uri (playbin, g_value_get_string (value));
2275 gst_play_bin3_set_suburi (playbin, g_value_get_string (value));
2278 gst_play_bin3_set_flags (playbin, g_value_get_flags (value));
2279 if (playbin->curr_group) {
2280 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2281 if (playbin->curr_group->urisourcebin) {
2282 g_object_set (playbin->curr_group->urisourcebin, "download",
2283 (g_value_get_flags (value) & GST_PLAY_FLAG_DOWNLOAD) != 0, NULL);
2285 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2288 case PROP_CURRENT_VIDEO:
2289 gst_play_bin3_set_current_video_stream (playbin, g_value_get_int (value));
2291 case PROP_CURRENT_AUDIO:
2292 gst_play_bin3_set_current_audio_stream (playbin, g_value_get_int (value));
2294 case PROP_CURRENT_TEXT:
2295 gst_play_bin3_set_current_text_stream (playbin, g_value_get_int (value));
2297 case PROP_AUTO_SELECT_STREAMS:
2298 GST_PLAY_BIN3_LOCK (playbin);
2299 playbin->do_stream_selections = g_value_get_boolean (value);
2300 GST_PLAY_BIN3_UNLOCK (playbin);
2302 case PROP_SUBTITLE_ENCODING:
2303 gst_play_bin3_set_encoding (playbin, g_value_get_string (value));
2305 case PROP_VIDEO_FILTER:
2306 gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
2307 GST_ELEMENT (g_value_get_object (value)));
2309 case PROP_AUDIO_FILTER:
2310 gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
2311 GST_ELEMENT (g_value_get_object (value)));
2313 case PROP_VIDEO_SINK:
2314 gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_VIDEO, "video",
2315 &playbin->video_sink, g_value_get_object (value));
2317 case PROP_AUDIO_SINK:
2318 gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_AUDIO, "audio",
2319 &playbin->audio_sink, g_value_get_object (value));
2321 case PROP_VIS_PLUGIN:
2322 gst_play_sink_set_vis_plugin (playbin->playsink,
2323 g_value_get_object (value));
2325 case PROP_TEXT_SINK:
2326 gst_play_bin3_set_sink (playbin, GST_PLAY_SINK_TYPE_TEXT, "text",
2327 &playbin->text_sink, g_value_get_object (value));
2329 case PROP_VIDEO_STREAM_COMBINER:
2330 gst_play_bin3_set_stream_combiner (playbin,
2331 &playbin->video_stream_combiner, "video", g_value_get_object (value));
2333 case PROP_AUDIO_STREAM_COMBINER:
2334 gst_play_bin3_set_stream_combiner (playbin,
2335 &playbin->audio_stream_combiner, "audio", g_value_get_object (value));
2337 case PROP_TEXT_STREAM_COMBINER:
2338 gst_play_bin3_set_stream_combiner (playbin,
2339 &playbin->text_stream_combiner, "text", g_value_get_object (value));
2342 gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
2345 gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
2347 case PROP_FONT_DESC:
2348 gst_play_sink_set_font_desc (playbin->playsink,
2349 g_value_get_string (value));
2351 case PROP_CONNECTION_SPEED:
2352 GST_PLAY_BIN3_LOCK (playbin);
2353 playbin->connection_speed = g_value_get_uint64 (value) * 1000;
2354 GST_PLAY_BIN3_UNLOCK (playbin);
2356 case PROP_BUFFER_SIZE:
2357 playbin->buffer_size = g_value_get_int (value);
2359 case PROP_BUFFER_DURATION:
2360 playbin->buffer_duration = g_value_get_int64 (value);
2362 case PROP_AV_OFFSET:
2363 gst_play_sink_set_av_offset (playbin->playsink,
2364 g_value_get_int64 (value));
2366 case PROP_RING_BUFFER_MAX_SIZE:
2367 playbin->ring_buffer_max_size = g_value_get_uint64 (value);
2368 if (playbin->curr_group) {
2369 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2370 if (playbin->curr_group->urisourcebin) {
2371 g_object_set (playbin->curr_group->urisourcebin,
2372 "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
2374 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2377 case PROP_FORCE_ASPECT_RATIO:
2378 g_object_set (playbin->playsink, "force-aspect-ratio",
2379 g_value_get_boolean (value), NULL);
2381 case PROP_MULTIVIEW_MODE:
2382 GST_PLAY_BIN3_LOCK (playbin);
2383 playbin->multiview_mode = g_value_get_enum (value);
2384 GST_PLAY_BIN3_UNLOCK (playbin);
2386 case PROP_MULTIVIEW_FLAGS:
2387 GST_PLAY_BIN3_LOCK (playbin);
2388 playbin->multiview_flags = g_value_get_flags (value);
2389 GST_PLAY_BIN3_UNLOCK (playbin);
2392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2398 gst_play_bin3_get_current_sink (GstPlayBin3 * playbin, GstElement ** elem,
2399 const gchar * dbg, GstPlaySinkType type)
2401 GstElement *sink = gst_play_sink_get_sink (playbin->playsink, type);
2403 GST_LOG_OBJECT (playbin, "play_sink_get_sink() returned %s sink %"
2404 GST_PTR_FORMAT ", the originally set %s sink is %" GST_PTR_FORMAT,
2405 dbg, sink, dbg, *elem);
2408 GST_PLAY_BIN3_LOCK (playbin);
2410 gst_object_ref (sink);
2411 GST_PLAY_BIN3_UNLOCK (playbin);
2418 gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin,
2419 GstElement ** elem, const gchar * dbg, int stream_type)
2421 GstElement *combiner;
2423 GST_PLAY_BIN3_LOCK (playbin);
2424 if ((combiner = playbin->combiner[stream_type].combiner))
2425 gst_object_ref (combiner);
2426 else if ((combiner = *elem))
2427 gst_object_ref (combiner);
2428 GST_PLAY_BIN3_UNLOCK (playbin);
2434 gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value,
2437 GstPlayBin3 *playbin = GST_PLAY_BIN3 (object);
2442 GstSourceGroup *group;
2444 GST_PLAY_BIN3_LOCK (playbin);
2445 group = playbin->next_group;
2446 g_value_set_string (value, group->uri);
2447 GST_PLAY_BIN3_UNLOCK (playbin);
2450 case PROP_CURRENT_URI:
2452 GstSourceGroup *group;
2454 GST_PLAY_BIN3_LOCK (playbin);
2455 group = get_group (playbin);
2456 g_value_set_string (value, group->uri);
2457 GST_PLAY_BIN3_UNLOCK (playbin);
2462 GstSourceGroup *group;
2464 GST_PLAY_BIN3_LOCK (playbin);
2465 group = playbin->next_group;
2466 g_value_set_string (value, group->suburi);
2467 GST_PLAY_BIN3_UNLOCK (playbin);
2470 case PROP_CURRENT_SUBURI:
2472 GstSourceGroup *group;
2474 GST_PLAY_BIN3_LOCK (playbin);
2475 group = get_group (playbin);
2476 g_value_set_string (value, group->suburi);
2477 GST_PLAY_BIN3_UNLOCK (playbin);
2482 GST_OBJECT_LOCK (playbin);
2483 g_value_set_object (value, playbin->source);
2484 GST_OBJECT_UNLOCK (playbin);
2488 g_value_set_flags (value, gst_play_bin3_get_flags (playbin));
2494 GST_PLAY_BIN3_LOCK (playbin);
2496 playbin->combiner[PLAYBIN_STREAM_VIDEO].
2497 streams ? playbin->combiner[PLAYBIN_STREAM_VIDEO].streams->len : 0;
2498 GST_PLAY_BIN3_UNLOCK (playbin);
2499 g_value_set_int (value, n_video);
2502 case PROP_CURRENT_VIDEO:
2503 GST_PLAY_BIN3_LOCK (playbin);
2504 if (playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream != -1)
2505 g_value_set_int (value,
2506 playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream);
2508 g_value_set_int (value, playbin->current_video);
2509 GST_PLAY_BIN3_UNLOCK (playbin);
2515 GST_PLAY_BIN3_LOCK (playbin);
2517 playbin->combiner[PLAYBIN_STREAM_AUDIO].
2518 streams ? playbin->combiner[PLAYBIN_STREAM_AUDIO].streams->len : 0;
2519 GST_PLAY_BIN3_UNLOCK (playbin);
2521 g_value_set_int (value, n_audio);
2524 case PROP_CURRENT_AUDIO:
2525 GST_PLAY_BIN3_LOCK (playbin);
2526 if (playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream != -1)
2527 g_value_set_int (value,
2528 playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream);
2530 g_value_set_int (value, playbin->current_audio);
2531 GST_PLAY_BIN3_UNLOCK (playbin);
2537 GST_PLAY_BIN3_LOCK (playbin);
2539 playbin->combiner[PLAYBIN_STREAM_TEXT].
2540 streams ? playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len : 0;
2541 GST_PLAY_BIN3_UNLOCK (playbin);
2542 g_value_set_int (value, n_text);
2545 case PROP_CURRENT_TEXT:
2546 GST_PLAY_BIN3_LOCK (playbin);
2547 if (playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream != -1)
2548 g_value_set_int (value,
2549 playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream);
2551 g_value_set_int (value, playbin->current_text);
2552 GST_PLAY_BIN3_UNLOCK (playbin);
2554 case PROP_AUTO_SELECT_STREAMS:
2555 GST_PLAY_BIN3_LOCK (playbin);
2556 g_value_set_boolean (value, playbin->do_stream_selections);
2557 GST_PLAY_BIN3_UNLOCK (playbin);
2559 case PROP_SUBTITLE_ENCODING:
2560 GST_PLAY_BIN3_LOCK (playbin);
2561 g_value_take_string (value,
2562 gst_play_sink_get_subtitle_encoding (playbin->playsink));
2563 GST_PLAY_BIN3_UNLOCK (playbin);
2565 case PROP_VIDEO_FILTER:
2566 g_value_take_object (value,
2567 gst_play_sink_get_filter (playbin->playsink,
2568 GST_PLAY_SINK_TYPE_VIDEO));
2570 case PROP_AUDIO_FILTER:
2571 g_value_take_object (value,
2572 gst_play_sink_get_filter (playbin->playsink,
2573 GST_PLAY_SINK_TYPE_AUDIO));
2575 case PROP_VIDEO_SINK:
2576 g_value_take_object (value,
2577 gst_play_bin3_get_current_sink (playbin, &playbin->video_sink,
2578 "video", GST_PLAY_SINK_TYPE_VIDEO));
2580 case PROP_AUDIO_SINK:
2581 g_value_take_object (value,
2582 gst_play_bin3_get_current_sink (playbin, &playbin->audio_sink,
2583 "audio", GST_PLAY_SINK_TYPE_AUDIO));
2585 case PROP_VIS_PLUGIN:
2586 g_value_take_object (value,
2587 gst_play_sink_get_vis_plugin (playbin->playsink));
2589 case PROP_TEXT_SINK:
2590 g_value_take_object (value,
2591 gst_play_bin3_get_current_sink (playbin, &playbin->text_sink,
2592 "text", GST_PLAY_SINK_TYPE_TEXT));
2594 case PROP_VIDEO_STREAM_COMBINER:
2595 g_value_take_object (value,
2596 gst_play_bin3_get_current_stream_combiner (playbin,
2597 &playbin->video_stream_combiner, "video", PLAYBIN_STREAM_VIDEO));
2599 case PROP_AUDIO_STREAM_COMBINER:
2600 g_value_take_object (value,
2601 gst_play_bin3_get_current_stream_combiner (playbin,
2602 &playbin->audio_stream_combiner, "audio", PLAYBIN_STREAM_AUDIO));
2604 case PROP_TEXT_STREAM_COMBINER:
2605 g_value_take_object (value,
2606 gst_play_bin3_get_current_stream_combiner (playbin,
2607 &playbin->text_stream_combiner, "text", PLAYBIN_STREAM_TEXT));
2610 g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
2613 g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
2616 gst_value_take_sample (value,
2617 gst_play_sink_get_last_sample (playbin->playsink));
2619 case PROP_FONT_DESC:
2620 g_value_take_string (value,
2621 gst_play_sink_get_font_desc (playbin->playsink));
2623 case PROP_CONNECTION_SPEED:
2624 GST_PLAY_BIN3_LOCK (playbin);
2625 g_value_set_uint64 (value, playbin->connection_speed / 1000);
2626 GST_PLAY_BIN3_UNLOCK (playbin);
2628 case PROP_BUFFER_SIZE:
2629 GST_OBJECT_LOCK (playbin);
2630 g_value_set_int (value, playbin->buffer_size);
2631 GST_OBJECT_UNLOCK (playbin);
2633 case PROP_BUFFER_DURATION:
2634 GST_OBJECT_LOCK (playbin);
2635 g_value_set_int64 (value, playbin->buffer_duration);
2636 GST_OBJECT_UNLOCK (playbin);
2638 case PROP_AV_OFFSET:
2639 g_value_set_int64 (value,
2640 gst_play_sink_get_av_offset (playbin->playsink));
2642 case PROP_RING_BUFFER_MAX_SIZE:
2643 g_value_set_uint64 (value, playbin->ring_buffer_max_size);
2645 case PROP_FORCE_ASPECT_RATIO:{
2648 g_object_get (playbin->playsink, "force-aspect-ratio", &v, NULL);
2649 g_value_set_boolean (value, v);
2652 case PROP_MULTIVIEW_MODE:
2653 GST_OBJECT_LOCK (playbin);
2654 g_value_set_enum (value, playbin->multiview_mode);
2655 GST_OBJECT_UNLOCK (playbin);
2657 case PROP_MULTIVIEW_FLAGS:
2658 GST_OBJECT_LOCK (playbin);
2659 g_value_set_flags (value, playbin->multiview_flags);
2660 GST_OBJECT_UNLOCK (playbin);
2663 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2669 gst_play_bin3_update_cached_duration_from_query (GstPlayBin3 * playbin,
2670 gboolean valid, GstQuery * query)
2676 GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
2677 gst_query_parse_duration (query, &fmt, &duration);
2679 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2680 if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
2681 playbin->duration[i].valid = valid;
2682 playbin->duration[i].format = fmt;
2683 playbin->duration[i].duration = valid ? duration : -1;
2690 gst_play_bin3_update_cached_duration (GstPlayBin3 * playbin)
2692 const GstFormat formats[] =
2693 { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
2698 GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
2699 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
2700 query = gst_query_new_duration (formats[i]);
2702 GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
2704 gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
2705 gst_query_unref (query);
2710 gst_play_bin3_query (GstElement * element, GstQuery * query)
2712 GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
2715 /* During a group switch we shouldn't allow duration queries
2716 * because it's not clear if the old or new group's duration
2717 * is returned and if the sinks are already playing new data
2718 * or old data. See bug #585969
2720 * While we're at it, also don't do any other queries during
2721 * a group switch or any other event that causes topology changes
2722 * by taking the playbin lock in any case.
2724 GST_PLAY_BIN3_LOCK (playbin);
2726 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
2727 GstSourceGroup *group = playbin->curr_group;
2730 GST_SOURCE_GROUP_LOCK (group);
2732 pending = group->pending || group->stream_changed_pending;
2739 gst_query_parse_duration (query, &fmt, NULL);
2740 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2741 if (fmt == playbin->duration[i].format) {
2742 ret = playbin->duration[i].valid;
2743 gst_query_set_duration (query, fmt,
2744 (ret ? playbin->duration[i].duration : -1));
2748 /* if nothing cached yet, we might as well request duration,
2749 * such as during initial startup */
2751 GST_DEBUG_OBJECT (playbin,
2752 "Taking cached duration because of pending group switch: %d", ret);
2753 GST_SOURCE_GROUP_UNLOCK (group);
2754 GST_PLAY_BIN3_UNLOCK (playbin);
2758 GST_SOURCE_GROUP_UNLOCK (group);
2761 ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
2763 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
2764 gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
2765 GST_PLAY_BIN3_UNLOCK (playbin);
2771 get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine,
2777 for (i = 0; combine->streams->len; i++) {
2778 GstStream *stream = (GstStream *) g_ptr_array_index (combine->streams, i);
2779 const gchar *sid = gst_stream_get_stream_id (stream);
2780 for (tmp = full_list; tmp; tmp = tmp->next) {
2781 gchar *orig = (gchar *) tmp->data;
2782 if (!g_strcmp0 (orig, sid))
2792 extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype,
2800 nb = gst_stream_collection_get_size (playbin->collection);
2801 for (i = 0; i < nb; i++) {
2803 gst_stream_collection_get_stream (playbin->collection, i);
2804 GstStreamType curtype = gst_stream_get_stream_type (stream);
2805 if (stype == curtype) {
2806 gboolean already_there = FALSE;
2807 const gchar *sid = gst_stream_get_stream_id (stream);
2808 for (tmp = res; tmp; tmp = tmp->next) {
2809 const gchar *other = (const gchar *) tmp->data;
2810 if (!g_strcmp0 (sid, other)) {
2811 already_there = TRUE;
2815 if (!already_there) {
2816 GST_DEBUG_OBJECT (playbin, "Adding stream %s", sid);
2817 res = g_list_append (res, g_strdup (sid));
2826 update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event)
2828 GList *streams = NULL;
2832 if (!playbin->audio_stream_combiner && !playbin->video_stream_combiner &&
2833 !playbin->text_stream_combiner) {
2835 GST_DEBUG_OBJECT (playbin,
2836 "No custom combiners, no need to modify SELECT_STREAMS event");
2840 gst_event_parse_select_streams (event, &streams);
2841 to_use = g_list_copy_deep (streams, (GCopyFunc) g_strdup, NULL);
2843 /* For each combiner, we want to add all streams of that type to the
2845 if (playbin->audio_stream_combiner) {
2846 to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use);
2848 get_combiner_stream_id (playbin,
2849 &playbin->combiner[PLAYBIN_STREAM_AUDIO], streams);
2850 if (combine_id != -1)
2851 gst_play_bin3_set_current_audio_stream (playbin, combine_id);
2853 if (playbin->video_stream_combiner) {
2854 to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use);
2856 get_combiner_stream_id (playbin,
2857 &playbin->combiner[PLAYBIN_STREAM_VIDEO], streams);
2858 if (combine_id != -1)
2859 gst_play_bin3_set_current_video_stream (playbin, combine_id);
2861 if (playbin->text_stream_combiner) {
2862 to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use);
2864 get_combiner_stream_id (playbin,
2865 &playbin->combiner[PLAYBIN_STREAM_TEXT], streams);
2866 if (combine_id != -1)
2867 gst_play_bin3_set_current_text_stream (playbin, combine_id);
2870 gst_event_unref (event);
2871 return gst_event_new_select_streams (to_use);
2875 gst_play_bin3_send_event (GstElement * element, GstEvent * event)
2877 GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
2879 if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) {
2882 GST_PLAY_BIN3_LOCK (playbin);
2883 GST_LOG_OBJECT (playbin,
2884 "App sent select-streams, we won't do anything ourselves now");
2885 /* This is probably already false, but it doesn't hurt to be sure */
2886 playbin->do_stream_selections = FALSE;
2888 /* If we have custom combiners, we need to extend the selection with
2889 * the list of all streams for that given type since we will be handling
2890 * the selection with that combiner */
2891 event = update_select_streams_event (playbin, event);
2893 /* Send this event directly to decodebin, so it works even
2894 * if decodebin didn't add any pads yet */
2895 res = gst_element_send_event (playbin->decodebin, event);
2896 GST_PLAY_BIN3_UNLOCK (playbin);
2901 /* Send event directly to playsink instead of letting GstBin iterate
2902 * over all sink elements. The latter might send the event multiple times
2903 * in case the SEEK causes a reconfiguration of the pipeline, as can easily
2904 * happen with adaptive streaming demuxers.
2906 * What would then happen is that the iterator would be reset, we send the
2907 * event again, and on the second time it will fail in the majority of cases
2908 * because the pipeline is still being reconfigured
2910 if (GST_EVENT_IS_UPSTREAM (event)) {
2911 return gst_element_send_event (GST_ELEMENT_CAST (playbin->playsink), event);
2914 return GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
2917 /* Called with playbin lock held */
2919 do_stream_selection (GstPlayBin3 * playbin)
2921 GstStreamCollection *collection;
2922 guint i, nb_streams;
2923 GList *streams = NULL;
2924 gint nb_video = 0, nb_audio = 0, nb_text = 0;
2925 GstStreamType chosen_stream_types = 0;
2927 collection = playbin->collection;
2928 if (collection == NULL) {
2929 GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select");
2933 nb_streams = gst_stream_collection_get_size (collection);
2934 if (nb_streams == 0) {
2935 GST_INFO_OBJECT (playbin, "Empty collection received! Ignoring");
2938 /* Iterate the collection and choose the streams that match
2939 * either the current-* setting, or all streams of a type if there's
2940 * a combiner for that type */
2941 for (i = 0; i < nb_streams; i++) {
2942 GstStream *stream = gst_stream_collection_get_stream (collection, i);
2943 GstStreamType stream_type = gst_stream_get_stream_type (stream);
2944 const gchar *stream_id = gst_stream_get_stream_id (stream);
2945 gint pb_stream_type = -1;
2946 gboolean select_this = FALSE;
2948 switch (stream_type) {
2949 case GST_STREAM_TYPE_AUDIO:
2950 pb_stream_type = PLAYBIN_STREAM_AUDIO;
2951 /* Select the stream if it's the current one or if there's a custom selector */
2953 (nb_audio == playbin->current_audio ||
2954 (playbin->current_audio == -1 && nb_audio == 0) ||
2955 playbin->audio_stream_combiner != NULL);
2958 case GST_STREAM_TYPE_VIDEO:
2959 pb_stream_type = PLAYBIN_STREAM_AUDIO;
2961 (nb_video == playbin->current_video ||
2962 (playbin->current_video == -1 && nb_video == 0) ||
2963 playbin->video_stream_combiner != NULL);
2966 case GST_STREAM_TYPE_TEXT:
2967 pb_stream_type = PLAYBIN_STREAM_TEXT;
2969 (nb_text == playbin->current_text ||
2970 (playbin->current_text == -1 && nb_text == 0) ||
2971 playbin->text_stream_combiner != NULL);
2977 if (pb_stream_type < 0) {
2978 GST_DEBUG_OBJECT (playbin,
2979 "Stream %d (id %s) of unhandled type %s. Ignoring", i, stream_id,
2980 gst_stream_type_get_name (stream_type));
2984 GST_DEBUG_OBJECT (playbin, "Selecting stream %s of type %s",
2985 stream_id, gst_stream_type_get_name (stream_type));
2986 /* Don't build the list if we're not in charge of stream selection */
2987 if (playbin->do_stream_selections)
2988 streams = g_list_append (streams, (gpointer) stream_id);
2989 chosen_stream_types |= stream_type;
2994 GstEvent *ev = gst_event_new_select_streams (streams);
2995 gst_element_send_event (playbin->decodebin, ev);
2996 g_list_free (streams);
2998 playbin->selected_stream_types = chosen_stream_types;
3001 /* mime types we are not handling on purpose right now, don't post a
3002 * missing-plugin message for these */
3003 static const gchar *blacklisted_mimes[] = {
3008 notify_all_streams (GstPlayBin3 * playbin, GstStreamCollection * collection)
3011 nb_streams = gst_stream_collection_get_size (collection);
3012 for (i = 0; i < nb_streams; i++)
3013 notify_tags_for_stream (playbin, collection,
3014 gst_stream_collection_get_stream (collection, i));
3018 gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
3020 GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
3022 if (gst_is_missing_plugin_message (msg)) {
3026 detail = gst_missing_plugin_message_get_installer_detail (msg);
3027 for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
3028 if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
3029 GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
3030 gst_message_unref (msg);
3036 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
3037 GstSourceGroup *new_group = playbin->curr_group;
3038 GstMessage *buffering_msg = NULL;
3040 GST_SOURCE_GROUP_LOCK (new_group);
3041 new_group->stream_changed_pending = FALSE;
3042 if (new_group->pending_buffering_msg) {
3043 buffering_msg = new_group->pending_buffering_msg;
3044 new_group->pending_buffering_msg = NULL;
3046 GST_SOURCE_GROUP_UNLOCK (new_group);
3048 GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group);
3050 if (buffering_msg) {
3051 GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %"
3052 GST_PTR_FORMAT, buffering_msg);
3053 GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
3056 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
3057 GstSourceGroup *group = playbin->curr_group;
3060 /* drop buffering messages from child queues while we are switching
3061 * groups (because the application set a new uri in about-to-finish)
3062 * if the playsink queue still has buffers to play */
3064 GST_SOURCE_GROUP_LOCK (group);
3065 pending = group->stream_changed_pending;
3068 GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group "
3069 "%p %" GST_PTR_FORMAT, group, msg);
3070 gst_message_replace (&group->pending_buffering_msg, msg);
3071 gst_message_unref (msg);
3074 GST_SOURCE_GROUP_UNLOCK (group);
3075 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) {
3076 GstStreamCollection *collection = NULL;
3077 GstObject *src = GST_MESSAGE_SRC (msg);
3078 gboolean pstate = playbin->do_stream_selections;
3080 gst_message_parse_stream_collection (msg, &collection);
3083 GST_PLAY_BIN3_LOCK (playbin);
3084 GST_DEBUG_OBJECT (playbin,
3085 "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, src);
3086 if (playbin->collection && playbin->collection_notify_id) {
3087 g_signal_handler_disconnect (playbin->collection,
3088 playbin->collection_notify_id);
3089 playbin->collection_notify_id = 0;
3091 gst_object_replace ((GstObject **) & playbin->collection,
3092 (GstObject *) collection);
3093 playbin->collection_notify_id =
3094 g_signal_connect (collection, "stream-notify::tags",
3095 (GCallback) notify_tags_cb, playbin);
3096 update_combiner_info (playbin);
3098 playbin->do_stream_selections = FALSE;
3099 do_stream_selection (playbin);
3101 playbin->do_stream_selections = TRUE;
3102 GST_PLAY_BIN3_UNLOCK (playbin);
3104 notify_all_streams (playbin, collection);
3105 gst_object_unref (collection);
3107 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAMS_SELECTED) {
3108 GstStreamCollection *collection = NULL;
3109 GstObject *src = GST_MESSAGE_SRC (msg);
3110 gboolean pstate = playbin->do_stream_selections;
3112 gst_message_parse_streams_selected (msg, &collection);
3115 GST_PLAY_BIN3_LOCK (playbin);
3116 GST_DEBUG_OBJECT (playbin,
3117 "STREAMS_SELECTED: Got a collection from %" GST_PTR_FORMAT, src);
3118 if (playbin->collection && playbin->collection_notify_id) {
3119 g_signal_handler_disconnect (playbin->collection,
3120 playbin->collection_notify_id);
3121 playbin->collection_notify_id = 0;
3123 gst_object_replace ((GstObject **) & playbin->collection,
3124 (GstObject *) collection);
3125 playbin->collection_notify_id =
3126 g_signal_connect (collection, "stream-notify::tags",
3127 (GCallback) notify_tags_cb, playbin);
3128 update_combiner_info (playbin);
3129 len = gst_message_streams_selected_get_size (msg);
3130 for (i = 0; i < len; i++) {
3131 set_selected_stream (playbin,
3132 gst_message_streams_selected_get_stream (msg, i));
3135 playbin->do_stream_selections = FALSE;
3136 do_stream_selection (playbin);
3138 playbin->do_stream_selections = TRUE;
3139 GST_PLAY_BIN3_UNLOCK (playbin);
3141 notify_all_streams (playbin, collection);
3142 gst_object_unref (collection);
3147 GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
3151 combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
3152 GstPlayBin3 * playbin)
3154 const gchar *property;
3155 GstSourceCombine *combine = NULL;
3156 GPtrArray *channels = NULL;
3159 GST_PLAY_BIN3_LOCK (playbin);
3161 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3162 if (combiner == G_OBJECT (playbin->combiner[i].combiner)) {
3163 combine = &playbin->combiner[i];
3164 channels = playbin->channels[i];
3168 /* We got a pad-change after our group got switched out; no need to notify */
3170 GST_PLAY_BIN3_UNLOCK (playbin);
3174 switch (combine->type) {
3175 case GST_PLAY_SINK_TYPE_VIDEO:
3176 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3177 property = "current-video";
3178 playbin->current_video = get_current_stream_number (playbin,
3181 if (playbin->video_pending_flush_finish) {
3182 playbin->video_pending_flush_finish = FALSE;
3183 GST_PLAY_BIN3_UNLOCK (playbin);
3184 gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
3185 "playsink-custom-video-flush-finish");
3189 case GST_PLAY_SINK_TYPE_AUDIO:
3190 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3191 property = "current-audio";
3192 playbin->current_audio = get_current_stream_number (playbin,
3195 if (playbin->audio_pending_flush_finish) {
3196 playbin->audio_pending_flush_finish = FALSE;
3197 GST_PLAY_BIN3_UNLOCK (playbin);
3198 gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
3199 "playsink-custom-audio-flush-finish");
3203 case GST_PLAY_SINK_TYPE_TEXT:
3204 property = "current-text";
3205 playbin->current_text = get_current_stream_number (playbin,
3208 if (playbin->text_pending_flush_finish) {
3209 playbin->text_pending_flush_finish = FALSE;
3210 GST_PLAY_BIN3_UNLOCK (playbin);
3211 gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
3212 "playsink-custom-subtitle-flush-finish");
3219 GST_PLAY_BIN3_UNLOCK (playbin);
3223 g_object_notify (G_OBJECT (playbin), property);
3227 update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps)
3229 GstVideoMultiviewMode mv_mode;
3230 GstVideoMultiviewMode cur_mv_mode;
3231 GstVideoMultiviewFlags mv_flags, cur_mv_flags;
3233 const gchar *mview_mode_str;
3236 GST_OBJECT_LOCK (playbin);
3237 mv_mode = (GstVideoMultiviewMode) playbin->multiview_mode;
3238 mv_flags = playbin->multiview_flags;
3239 GST_OBJECT_UNLOCK (playbin);
3241 if (mv_mode == GST_VIDEO_MULTIVIEW_MODE_NONE)
3244 cur_mv_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
3245 cur_mv_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
3247 s = gst_caps_get_structure (caps, 0);
3249 gst_structure_get_flagset (s, "multiview-flags", &cur_mv_flags, NULL);
3250 if ((mview_mode_str = gst_structure_get_string (s, "multiview-mode")))
3251 cur_mv_mode = gst_video_multiview_mode_from_caps_string (mview_mode_str);
3253 /* We can't override an existing annotated multiview mode, except
3254 * maybe (in the future) we could change some flags. */
3255 if ((gint) cur_mv_mode > GST_VIDEO_MULTIVIEW_MAX_FRAME_PACKING) {
3256 GST_INFO_OBJECT (playbin, "Cannot override existing multiview mode");
3260 mview_mode_str = gst_video_multiview_mode_to_caps_string (mv_mode);
3261 g_assert (mview_mode_str != NULL);
3262 out_caps = gst_caps_copy (caps);
3263 s = gst_caps_get_structure (out_caps, 0);
3265 gst_structure_set (s, "multiview-mode", G_TYPE_STRING, mview_mode_str,
3266 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, mv_flags,
3267 GST_FLAG_SET_MASK_EXACT, NULL);
3272 static GstPadProbeReturn
3273 _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
3275 GstPadProbeReturn ret = GST_PAD_PROBE_OK;
3276 GstPlayBin3 *playbin = (GstPlayBin3 *) udata;
3277 GstEvent *event = GST_PAD_PROBE_INFO_DATA (info);
3279 switch (GST_EVENT_TYPE (event)) {
3280 case GST_EVENT_CAPS:{
3281 GstCaps *caps = NULL;
3282 const GstStructure *s;
3285 gst_event_parse_caps (event, &caps);
3286 /* If video caps, check if we should override multiview flags */
3287 s = gst_caps_get_structure (caps, 0);
3288 name = gst_structure_get_name (s);
3289 if (g_str_has_prefix (name, "video/")) {
3290 caps = update_video_multiview_caps (playbin, caps);
3292 gst_event_unref (event);
3293 event = gst_event_new_caps (caps);
3294 GST_PAD_PROBE_INFO_DATA (info) = event;
3295 gst_caps_unref (caps);
3308 find_index_for_stream_by_type (GstStreamCollection * collection,
3311 gint nb_streams = gst_stream_collection_get_size (collection);
3312 gint i, cur_idx = 0;
3313 GstStreamType target_type = gst_stream_get_stream_type (stream);
3315 /* Count the streams of the type we want to find the index of the one we want */
3316 for (i = 0; i < nb_streams; i++) {
3317 GstStream *cur_stream = gst_stream_collection_get_stream (collection, i);
3318 if (stream == cur_stream)
3320 if (gst_stream_get_stream_type (cur_stream) == target_type)
3328 notify_tags_for_stream (GstPlayBin3 * playbin, GstStreamCollection * collection,
3331 GstStreamType stream_type = gst_stream_get_stream_type (stream);
3332 gint stream_idx = find_index_for_stream_by_type (collection, stream);
3335 GST_DEBUG_OBJECT (playbin, "Tags on stream %" GST_PTR_FORMAT
3336 " with stream idx %d and type %d have changed",
3337 stream, stream_idx, stream_type);
3339 switch (stream_type) {
3340 case GST_STREAM_TYPE_VIDEO:
3341 signal = SIGNAL_VIDEO_TAGS_CHANGED;
3343 case GST_STREAM_TYPE_AUDIO:
3344 signal = SIGNAL_AUDIO_TAGS_CHANGED;
3346 case GST_STREAM_TYPE_TEXT:
3347 signal = SIGNAL_TEXT_TAGS_CHANGED;
3355 g_signal_emit (G_OBJECT (playbin), gst_play_bin3_signals[signal], 0,
3360 notify_tags_cb (GstStreamCollection * collection, GstStream * stream,
3361 GParamSpec * pspec, GstPlayBin3 * playbin)
3363 notify_tags_for_stream (playbin, collection, stream);
3366 /* this function is called when a new pad is added to decodebin. We check the
3367 * type of the pad and add it to the combiner element
3370 pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
3373 GstPadLinkReturn res;
3374 GstSourceCombine *combine = NULL;
3375 GstStreamType stream_type;
3376 gint pb_stream_type = -1;
3377 gboolean changed = FALSE;
3378 GstElement *custom_combiner = NULL;
3379 gulong event_probe_handler;
3382 GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
3384 pad_name = gst_object_get_name (GST_OBJECT (pad));
3386 GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
3387 GST_DEBUG_PAD_NAME (pad));
3389 /* major type of the pad, this determines the combiner to use,
3390 try exact match first */
3391 if (g_str_has_prefix (pad_name, "video")) {
3392 stream_type = GST_STREAM_TYPE_VIDEO;
3393 pb_stream_type = PLAYBIN_STREAM_VIDEO;
3394 custom_combiner = playbin->video_stream_combiner;
3395 } else if (g_str_has_prefix (pad_name, "audio")) {
3396 stream_type = GST_STREAM_TYPE_AUDIO;
3397 pb_stream_type = PLAYBIN_STREAM_AUDIO;
3398 custom_combiner = playbin->audio_stream_combiner;
3399 } else if (g_str_has_prefix (pad_name, "text")) {
3400 stream_type = GST_STREAM_TYPE_TEXT;
3401 pb_stream_type = PLAYBIN_STREAM_TEXT;
3402 custom_combiner = playbin->text_stream_combiner;
3407 /* no stream type found for the media type, don't bother linking it to a
3408 * combiner. This will leave the pad unlinked and thus ignored. */
3409 if (pb_stream_type < 0) {
3410 GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
3414 combine = &playbin->combiner[pb_stream_type];
3416 if (custom_combiner && combine->combiner == NULL) {
3417 combine->combiner = custom_combiner;
3418 /* find out which properties the stream combiner supports */
3419 combine->has_active_pad =
3420 g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
3421 "active-pad") != NULL;
3423 if (!custom_combiner) {
3424 /* sync-mode=1, use clock */
3425 if (combine->type == GST_PLAY_SINK_TYPE_TEXT)
3426 g_object_set (combine->combiner, "sync-streams", TRUE,
3427 "sync-mode", 1, "cache-buffers", TRUE, NULL);
3429 g_object_set (combine->combiner, "sync-streams", TRUE, NULL);
3432 if (combine->has_active_pad)
3433 g_signal_connect (combine->combiner, "notify::active-pad",
3434 G_CALLBACK (combiner_active_pad_changed), playbin);
3436 GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p",
3438 gst_element_set_state (combine->combiner, GST_STATE_PAUSED);
3439 gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
3442 GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
3444 if (combine->srcpad == NULL) {
3445 if (combine->combiner) {
3446 /* save source pad of the combiner */
3447 combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
3449 /* no combiner, use the pad as the source pad then */
3450 combine->srcpad = gst_object_ref (pad);
3453 /* block the combiner srcpad. It's possible that multiple source elements
3454 * pushing data into the combiners before we have a chance to collect all
3455 * streams and connect the sinks, resulting in not-linked errors. After we
3456 * configure the sinks we will unblock them all. */
3457 GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad);
3459 gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3460 block_serialized_data_cb, NULL, NULL);
3463 /* get sinkpad for the new stream */
3464 if (combine->combiner) {
3465 if ((sinkpad = gst_element_get_request_pad (combine->combiner, "sink_%u"))) {
3466 GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner",
3467 GST_DEBUG_PAD_NAME (sinkpad));
3469 /* find out which properties the sink pad supports */
3470 combine->has_always_ok =
3471 g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
3472 "always-ok") != NULL;
3474 /* store the combiner for the pad */
3475 g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine);
3477 /* store the pad in the array */
3478 GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
3479 g_ptr_array_add (combine->channels, sinkpad);
3481 res = gst_pad_link (pad, sinkpad);
3482 if (GST_PAD_LINK_FAILED (res))
3485 /* store combiner pad so we can release it */
3486 g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
3489 GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p",
3490 GST_DEBUG_PAD_NAME (pad), combine->combiner);
3492 goto request_pad_failed;
3495 /* no combiner, don't configure anything, we'll link the new pad directly to
3500 /* store the combiner for the pad */
3501 g_object_set_data (G_OBJECT (pad), "playbin.combine", combine);
3504 event_probe_handler =
3505 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
3506 _decodebin_event_probe, playbin, NULL);
3507 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id",
3508 ULONG_TO_POINTER (event_probe_handler));
3513 switch (combine->type) {
3514 case GST_PLAY_SINK_TYPE_VIDEO:
3515 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3516 signal = SIGNAL_VIDEO_CHANGED;
3518 case GST_PLAY_SINK_TYPE_AUDIO:
3519 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3520 signal = SIGNAL_AUDIO_CHANGED;
3522 case GST_PLAY_SINK_TYPE_TEXT:
3523 signal = SIGNAL_TEXT_CHANGED;
3530 g_signal_emit (G_OBJECT (playbin), gst_play_bin3_signals[signal], 0,
3535 playbin->active_stream_types |= stream_type;
3537 /* If we're expecting either audio or video,
3538 * wait for them to appear before configuring playsink */
3539 if ((playbin->selected_stream_types & ~playbin->active_stream_types &
3540 (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO))
3542 no_more_pads_cb (decodebin, playbin);
3544 GST_LOG_OBJECT (playbin, "Active stream types 0x%x, want 0x%x. Waiting",
3545 playbin->active_stream_types, playbin->selected_stream_types);
3552 GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
3557 GST_ERROR_OBJECT (playbin,
3558 "failed to link pad %s:%s to combiner, reason %s (%d)",
3559 GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
3563 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3564 ("Internal playbin error."),
3565 ("Failed to get request pad from combiner %p.", combine->combiner));
3569 GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
3570 /* not going to done as we didn't request the caps */
3575 /* called when a pad is removed from the decodebin. We unlink the pad from
3576 * the combiner. This will make the combiner select a new pad. */
3578 pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
3581 GstElement *combiner;
3582 GstSourceCombine *combine;
3584 gulong event_probe_handler;
3586 GST_DEBUG_OBJECT (playbin,
3587 "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
3589 GST_PLAY_BIN3_LOCK (playbin);
3591 if ((event_probe_handler =
3592 POINTER_TO_ULONG (g_object_get_data (G_OBJECT (pad),
3593 "playbin.event_probe_id")))) {
3594 gst_pad_remove_probe (pad, event_probe_handler);
3595 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", NULL);
3598 if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) {
3599 g_assert (combine->combiner == NULL);
3600 g_assert (combine->srcpad == pad);
3601 source_combine_remove_pads (playbin, combine);
3605 /* get the combiner sinkpad */
3606 if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad")))
3609 /* unlink the pad now (can fail, the pad is unlinked before it's removed) */
3610 gst_pad_unlink (pad, peer);
3613 combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer));
3614 g_assert (combiner != NULL);
3616 if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) {
3617 /* remove the pad from the array */
3618 g_ptr_array_remove (combine->channels, peer);
3619 GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer);
3621 /* get the correct type-changed signal */
3622 switch (combine->type) {
3623 case GST_PLAY_SINK_TYPE_VIDEO:
3624 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3625 signal = SIGNAL_VIDEO_CHANGED;
3627 case GST_PLAY_SINK_TYPE_AUDIO:
3628 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3629 signal = SIGNAL_AUDIO_CHANGED;
3631 case GST_PLAY_SINK_TYPE_TEXT:
3632 signal = SIGNAL_TEXT_CHANGED;
3638 if (!combine->channels->len && combine->combiner) {
3639 GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed");
3640 GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner);
3641 source_combine_remove_pads (playbin, combine);
3642 gst_element_set_state (combine->combiner, GST_STATE_NULL);
3643 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
3644 combine->combiner = NULL;
3648 /* release the pad to the combiner, this will make the combiner choose a new
3650 gst_element_release_request_pad (combiner, peer);
3651 gst_object_unref (peer);
3653 gst_object_unref (combiner);
3655 GST_PLAY_BIN3_UNLOCK (playbin);
3658 g_signal_emit (G_OBJECT (playbin), gst_play_bin3_signals[signal], 0, NULL);
3665 GST_DEBUG_OBJECT (playbin, "pad not linked");
3672 select_stream_cb (GstElement * decodebin, GstStreamCollection * collection,
3673 GstStream * stream, GstPlayBin3 * playbin)
3675 GstStreamType stype = gst_stream_get_stream_type (stream);
3676 GstElement *combiner = NULL;
3679 case GST_STREAM_TYPE_AUDIO:
3680 combiner = playbin->audio_stream_combiner;
3682 case GST_STREAM_TYPE_VIDEO:
3683 combiner = playbin->video_stream_combiner;
3685 case GST_STREAM_TYPE_TEXT:
3686 combiner = playbin->text_stream_combiner;
3693 GST_DEBUG_OBJECT (playbin, "Got a combiner, requesting stream activation");
3697 /* Let decodebin3 decide otherwise */
3701 /* we get called when all pads are available and we must connect the sinks to
3703 * The main purpose of the code is to see if we have video/audio and subtitles
3704 * and pick the right pipelines to display them.
3706 * The combiners installed on the group tell us about the presence of
3707 * audio/video and subtitle streams. This allows us to see if we need
3708 * visualisation, video or/and audio.
3711 no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin)
3713 GstSourceGroup *group;
3714 GstPadLinkReturn res;
3718 GST_DEBUG_OBJECT (playbin, "no more pads");
3720 GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
3722 GST_PLAY_BIN3_LOCK (playbin);
3723 group = playbin->curr_group;
3725 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3726 GstSourceCombine *combine = &playbin->combiner[i];
3728 /* check if the specific media type was detected and thus has a combiner
3729 * created for it. If there is the media type, get a sinkpad from the sink
3730 * and link it. We only do this if we have not yet requested the sinkpad
3732 if (combine->srcpad && combine->sinkpad == NULL) {
3733 GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type);
3735 gst_play_sink_request_pad (playbin->playsink, combine->type);
3736 gst_object_ref (combine->sinkpad);
3737 } else if (combine->srcpad && combine->sinkpad) {
3738 GST_DEBUG_OBJECT (playbin, "re-using sink pad %d", combine->type);
3739 } else if (combine->sinkpad && combine->srcpad == NULL) {
3740 GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type);
3741 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
3742 gst_object_unref (combine->sinkpad);
3743 combine->sinkpad = NULL;
3745 if (combine->sinkpad && combine->srcpad &&
3746 !gst_pad_is_linked (combine->srcpad)) {
3747 res = gst_pad_link (combine->srcpad, combine->sinkpad);
3748 GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
3749 combine->media_type, res);
3750 if (res != GST_PAD_LINK_OK) {
3751 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3752 ("Internal playbin error."),
3753 ("Failed to link combiner to sink. Error %d", res));
3757 GST_PLAY_BIN3_UNLOCK (playbin);
3759 GST_SOURCE_GROUP_LOCK (group);
3760 GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
3761 group->pending - 1);
3763 if (group->pending > 0)
3766 if (group->pending == 0) {
3767 /* we are the last group to complete, we will configure the output and then
3768 * signal the other waiters. */
3769 GST_LOG_OBJECT (playbin, "last group complete");
3772 GST_LOG_OBJECT (playbin, "have more pending groups");
3775 GST_SOURCE_GROUP_UNLOCK (group);
3778 /* if we have custom sinks, configure them now */
3779 GST_SOURCE_GROUP_LOCK (group);
3781 if (group->audio_sink) {
3782 GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
3784 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
3788 if (group->video_sink) {
3789 GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
3791 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
3795 if (group->text_sink) {
3796 GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
3798 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
3802 GST_SOURCE_GROUP_UNLOCK (group);
3804 /* signal the other combiners that they can continue now. */
3805 GST_PLAY_BIN3_LOCK (playbin);
3806 /* unblock all combiners */
3807 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3808 GstSourceCombine *combine = &playbin->combiner[i];
3810 if (combine->srcpad) {
3811 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
3813 if (combine->block_id) {
3814 gst_pad_remove_probe (combine->srcpad, combine->block_id);
3815 combine->block_id = 0;
3819 GST_PLAY_BIN3_UNLOCK (playbin);
3820 gst_play_sink_reconfigure (playbin->playsink);
3823 GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
3826 do_async_done (playbin);
3833 GST_DEBUG ("ignoring, we are shutting down");
3834 /* Request a flushing pad from playsink that we then link to the combiner.
3835 * Then we unblock the combiners so that they stop with a WRONG_STATE
3836 * instead of a NOT_LINKED error.
3838 GST_PLAY_BIN3_LOCK (playbin);
3839 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3840 GstSourceCombine *combine = &playbin->combiner[i];
3842 if (combine->srcpad) {
3843 if (combine->sinkpad == NULL) {
3844 GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad");
3846 gst_play_sink_request_pad (playbin->playsink,
3847 GST_PLAY_SINK_TYPE_FLUSHING);
3848 gst_object_ref (combine->sinkpad);
3849 res = gst_pad_link (combine->srcpad, combine->sinkpad);
3850 GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res);
3852 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
3854 if (combine->block_id) {
3855 gst_pad_remove_probe (combine->srcpad, combine->block_id);
3856 combine->block_id = 0;
3860 GST_PLAY_BIN3_UNLOCK (playbin);
3867 drained_cb (GstElement * decodebin, GstSourceGroup * group)
3869 GstPlayBin3 *playbin;
3871 playbin = group->playbin;
3873 GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
3875 /* after this call, we should have a next group to activate or we EOS */
3876 g_signal_emit (G_OBJECT (playbin),
3877 gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
3879 /* now activate the next group. If the app did not set a uri, this will
3880 * fail and we can do EOS */
3881 setup_next_source (playbin, GST_STATE_PAUSED);
3885 /* Like gst_element_factory_can_sink_any_caps() but doesn't
3886 * allow ANY caps on the sinkpad template */
3888 _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
3890 const GList *templs;
3892 templs = gst_element_factory_get_static_pad_templates (factory);
3895 GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data;
3897 if (templ->direction == GST_PAD_SINK) {
3898 GstCaps *templcaps = gst_static_caps_get (&templ->static_caps);
3900 if (!gst_caps_is_any (templcaps)
3901 && gst_caps_is_subset (caps, templcaps)) {
3902 gst_caps_unref (templcaps);
3905 gst_caps_unref (templcaps);
3907 templs = g_list_next (templs);
3914 avelements_free (gpointer avelement)
3916 GstAVElement *elm = (GstAVElement *) avelement;
3919 gst_object_unref (elm->dec);
3921 gst_object_unref (elm->sink);
3922 g_slice_free (GstAVElement, elm);
3926 avelement_compare_decoder (gconstpointer p1, gconstpointer p2,
3929 GstAVElement *v1, *v2;
3931 v1 = (GstAVElement *) p1;
3932 v2 = (GstAVElement *) p2;
3934 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (v2->dec));
3938 avelement_lookup_decoder (gconstpointer p1, gconstpointer p2,
3942 GstElementFactory *f2;
3944 v1 = (GstAVElement *) p1;
3945 f2 = (GstElementFactory *) p2;
3947 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (f2));
3951 avelement_compare (gconstpointer p1, gconstpointer p2)
3953 GstAVElement *v1, *v2;
3954 GstPluginFeature *fd1, *fd2, *fs1, *fs2;
3955 gint64 diff, v1_rank, v2_rank;
3957 v1 = (GstAVElement *) p1;
3958 v2 = (GstAVElement *) p2;
3960 fd1 = (GstPluginFeature *) v1->dec;
3961 fd2 = (GstPluginFeature *) v2->dec;
3963 /* If both have a sink, we also compare their ranks */
3964 if (v1->sink && v2->sink) {
3965 fs1 = (GstPluginFeature *) v1->sink;
3966 fs2 = (GstPluginFeature *) v2->sink;
3968 gst_plugin_feature_get_rank (fd1) * gst_plugin_feature_get_rank (fs1);
3970 gst_plugin_feature_get_rank (fd2) * gst_plugin_feature_get_rank (fs2);
3972 v1_rank = gst_plugin_feature_get_rank (fd1);
3973 v2_rank = gst_plugin_feature_get_rank (fd2);
3977 /* comparison based on the rank */
3978 diff = v2_rank - v1_rank;
3984 /* comparison based on number of common caps features */
3985 diff = v2->n_comm_cf - v1->n_comm_cf;
3990 /* comparison based on the name of sink elements */
3991 diff = strcmp (GST_OBJECT_NAME (fs1), GST_OBJECT_NAME (fs2));
3996 /* comparison based on the name of decoder elements */
3997 return strcmp (GST_OBJECT_NAME (fd1), GST_OBJECT_NAME (fd2));
4001 avelements_create (GstPlayBin3 * playbin, gboolean isaudioelement)
4003 GstElementFactory *d_factory, *s_factory;
4004 GList *dec_list, *sink_list, *dl, *sl;
4005 GSequence *ave_seq = NULL;
4007 guint n_common_cf = 0;
4009 if (isaudioelement) {
4010 sink_list = gst_element_factory_list_get_elements
4011 (GST_ELEMENT_FACTORY_TYPE_SINK |
4012 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
4014 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
4015 | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
4017 sink_list = gst_element_factory_list_get_elements
4018 (GST_ELEMENT_FACTORY_TYPE_SINK |
4019 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4020 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
4023 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
4024 | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4025 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
4028 /* create a list of audio/video elements. Each element in the list
4029 * is holding an audio/video decoder and an audio/video sink in which
4030 * the decoders srcpad template caps and sink element's sinkpad template
4031 * caps are compatible */
4035 ave_seq = g_sequence_new ((GDestroyNotify) avelements_free);
4037 for (; dl; dl = dl->next) {
4038 d_factory = (GstElementFactory *) dl->data;
4039 for (; sl; sl = sl->next) {
4040 s_factory = (GstElementFactory *) sl->data;
4043 gst_playback_utils_get_n_common_capsfeatures (d_factory, s_factory,
4044 gst_play_bin3_get_flags (playbin), isaudioelement);
4045 if (n_common_cf < 1)
4048 ave = g_slice_new (GstAVElement);
4049 ave->dec = gst_object_ref (d_factory);
4050 ave->sink = gst_object_ref (s_factory);
4051 ave->n_comm_cf = n_common_cf;
4052 g_sequence_append (ave_seq, ave);
4056 g_sequence_sort (ave_seq, (GCompareDataFunc) avelement_compare_decoder, NULL);
4058 gst_plugin_feature_list_free (dec_list);
4059 gst_plugin_feature_list_free (sink_list);
4065 avelement_iter_is_equal (GSequenceIter * iter, GstElementFactory * factory)
4072 ave = g_sequence_get (iter);
4076 return strcmp (GST_OBJECT_NAME (ave->dec), GST_OBJECT_NAME (factory)) == 0;
4080 create_decoders_list (GList * factory_list, GSequence * avelements)
4082 GList *dec_list = NULL, *tmp;
4083 GList *ave_list = NULL;
4084 GList *ave_free_list = NULL;
4085 GstAVElement *ave, *best_ave;
4087 g_return_val_if_fail (factory_list != NULL, NULL);
4088 g_return_val_if_fail (avelements != NULL, NULL);
4090 for (tmp = factory_list; tmp; tmp = tmp->next) {
4091 GstElementFactory *factory = (GstElementFactory *) tmp->data;
4093 /* if there are parsers or sink elements, add them first */
4094 if (gst_element_factory_list_is_type (factory,
4095 GST_ELEMENT_FACTORY_TYPE_PARSER) ||
4096 gst_element_factory_list_is_type (factory,
4097 GST_ELEMENT_FACTORY_TYPE_SINK)) {
4098 dec_list = g_list_prepend (dec_list, gst_object_ref (factory));
4100 GSequenceIter *seq_iter;
4103 g_sequence_lookup (avelements, factory,
4104 (GCompareDataFunc) avelement_lookup_decoder, NULL);
4106 GstAVElement *ave = g_slice_new0 (GstAVElement);
4110 /* There's at least raw */
4113 ave_list = g_list_prepend (ave_list, ave);
4115 /* We need to free these later */
4116 ave_free_list = g_list_prepend (ave_free_list, ave);
4120 /* Go to first iter with that decoder */
4122 GSequenceIter *tmp_seq_iter;
4124 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
4125 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
4127 seq_iter = tmp_seq_iter;
4128 } while (!g_sequence_iter_is_begin (seq_iter));
4130 /* Get the best ranked GstAVElement for that factory */
4132 while (!g_sequence_iter_is_end (seq_iter)
4133 && avelement_iter_is_equal (seq_iter, factory)) {
4134 ave = g_sequence_get (seq_iter);
4136 if (!best_ave || avelement_compare (ave, best_ave) < 0)
4139 seq_iter = g_sequence_iter_next (seq_iter);
4141 ave_list = g_list_prepend (ave_list, best_ave);
4145 /* Sort all GstAVElements by their relative ranks and insert
4146 * into the decoders list */
4147 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
4148 for (tmp = ave_list; tmp; tmp = tmp->next) {
4149 ave = (GstAVElement *) tmp->data;
4150 dec_list = g_list_prepend (dec_list, gst_object_ref (ave->dec));
4152 g_list_free (ave_list);
4153 gst_plugin_feature_list_free (factory_list);
4155 for (tmp = ave_free_list; tmp; tmp = tmp->next)
4156 g_slice_free (GstAVElement, tmp->data);
4157 g_list_free (ave_free_list);
4159 dec_list = g_list_reverse (dec_list);
4164 /* Called when we must provide a list of factories to plug to @pad with @caps.
4165 * We first check if we have a sink that can handle the format and if we do, we
4166 * return NULL, to expose the pad. If we have no sink (or the sink does not
4167 * work), we return the list of elements that can connect. */
4168 static GValueArray *
4169 autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
4170 GstCaps * caps, GstSourceGroup * group)
4172 GstPlayBin3 *playbin;
4173 GList *factory_list, *tmp;
4174 GValueArray *result;
4175 gboolean unref_caps = FALSE;
4176 gboolean isaudiodeclist = FALSE;
4177 gboolean isvideodeclist = FALSE;
4180 caps = gst_caps_new_any ();
4184 playbin = group->playbin;
4186 GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
4187 group, GST_DEBUG_PAD_NAME (pad), caps);
4189 /* filter out the elements based on the caps. */
4190 g_mutex_lock (&playbin->elements_lock);
4191 gst_play_bin3_update_elements_list (playbin);
4193 gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
4194 gst_caps_is_fixed (caps));
4195 g_mutex_unlock (&playbin->elements_lock);
4197 GST_DEBUG_OBJECT (playbin, "found factories %p", factory_list);
4198 GST_PLUGIN_FEATURE_LIST_DEBUG (factory_list);
4200 /* check whether the caps are asking for a list of audio/video decoders */
4202 if (!gst_caps_is_any (caps)) {
4203 for (; tmp; tmp = tmp->next) {
4204 GstElementFactory *factory = (GstElementFactory *) tmp->data;
4206 isvideodeclist = gst_element_factory_list_is_type (factory,
4207 GST_ELEMENT_FACTORY_TYPE_DECODER |
4208 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4209 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
4210 isaudiodeclist = gst_element_factory_list_is_type (factory,
4211 GST_ELEMENT_FACTORY_TYPE_DECODER |
4212 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
4214 if (isaudiodeclist || isvideodeclist)
4219 if (isaudiodeclist || isvideodeclist) {
4220 GSequence **ave_list;
4222 ave_list = &playbin->aelements;
4224 ave_list = &playbin->velements;
4226 g_mutex_lock (&playbin->elements_lock);
4227 /* sort factory_list based on the GstAVElement list priority */
4228 factory_list = create_decoders_list (factory_list, *ave_list);
4229 g_mutex_unlock (&playbin->elements_lock);
4232 /* 2 additional elements for the already set audio/video sinks */
4233 result = g_value_array_new (g_list_length (factory_list) + 2);
4235 /* Check if we already have an audio/video sink and if this is the case
4236 * put it as the first element of the array */
4237 if (group->audio_sink) {
4238 GstElementFactory *factory = gst_element_get_factory (group->audio_sink);
4240 if (factory && _factory_can_sink_caps (factory, caps)) {
4241 GValue val = { 0, };
4243 g_value_init (&val, G_TYPE_OBJECT);
4244 g_value_set_object (&val, factory);
4245 result = g_value_array_append (result, &val);
4246 g_value_unset (&val);
4250 if (group->video_sink) {
4251 GstElementFactory *factory = gst_element_get_factory (group->video_sink);
4253 if (factory && _factory_can_sink_caps (factory, caps)) {
4254 GValue val = { 0, };
4256 g_value_init (&val, G_TYPE_OBJECT);
4257 g_value_set_object (&val, factory);
4258 result = g_value_array_append (result, &val);
4259 g_value_unset (&val);
4263 for (tmp = factory_list; tmp; tmp = tmp->next) {
4264 GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
4265 GValue val = { 0, };
4267 if (group->audio_sink && gst_element_factory_list_is_type (factory,
4268 GST_ELEMENT_FACTORY_TYPE_SINK |
4269 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4272 if (group->video_sink && gst_element_factory_list_is_type (factory,
4273 GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO
4274 | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4278 g_value_init (&val, G_TYPE_OBJECT);
4279 g_value_set_object (&val, factory);
4280 g_value_array_append (result, &val);
4281 g_value_unset (&val);
4283 gst_plugin_feature_list_free (factory_list);
4286 gst_caps_unref (caps);
4292 gst_play_bin3_set_context (GstElement * element, GstContext * context)
4294 GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
4296 /* Proxy contexts to the sinks, they might not be in playsink yet */
4297 GST_PLAY_BIN3_LOCK (playbin);
4298 if (playbin->audio_sink)
4299 gst_element_set_context (playbin->audio_sink, context);
4300 if (playbin->video_sink)
4301 gst_element_set_context (playbin->video_sink, context);
4302 if (playbin->text_sink)
4303 gst_element_set_context (playbin->text_sink, context);
4305 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
4307 if (playbin->curr_group->audio_sink)
4308 gst_element_set_context (playbin->curr_group->audio_sink, context);
4309 if (playbin->curr_group->video_sink)
4310 gst_element_set_context (playbin->curr_group->video_sink, context);
4311 if (playbin->curr_group->text_sink)
4312 gst_element_set_context (playbin->curr_group->text_sink, context);
4314 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4315 GST_PLAY_BIN3_UNLOCK (playbin);
4317 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
4320 /* Pass sink messages to the application, e.g. NEED_CONTEXT messages */
4322 gst_play_bin3_update_context (GstPlayBin3 * playbin, GstContext * context)
4325 const gchar *context_type;
4327 GST_OBJECT_LOCK (playbin);
4328 context_type = gst_context_get_context_type (context);
4329 for (l = playbin->contexts; l; l = l->next) {
4330 GstContext *tmp = l->data;
4331 const gchar *tmp_type = gst_context_get_context_type (tmp);
4333 /* Always store newest context but never replace
4334 * a persistent one by a non-persistent one */
4335 if (strcmp (context_type, tmp_type) == 0 &&
4336 (gst_context_is_persistent (context) ||
4337 !gst_context_is_persistent (tmp))) {
4338 gst_context_replace ((GstContext **) & l->data, context);
4342 /* Not found? Add */
4345 g_list_prepend (playbin->contexts, gst_context_ref (context));
4346 GST_OBJECT_UNLOCK (playbin);
4349 static GstBusSyncReply
4350 activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
4351 GstPlayBin3 * playbin)
4353 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
4354 /* Only proxy errors from a fixed sink. If that fails we can just error out
4355 * early as stuff will fail later anyway */
4356 if (playbin->audio_sink
4357 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4358 GST_OBJECT_CAST (playbin->audio_sink)))
4359 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4360 else if (playbin->video_sink
4361 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4362 GST_OBJECT_CAST (playbin->video_sink)))
4363 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4364 else if (playbin->text_sink
4365 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4366 GST_OBJECT_CAST (playbin->text_sink)))
4367 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4369 gst_message_unref (msg);
4370 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
4371 const gchar *context_type;
4374 gst_message_parse_context_type (msg, &context_type);
4375 GST_OBJECT_LOCK (playbin);
4376 for (l = playbin->contexts; l; l = l->next) {
4377 GstContext *tmp = l->data;
4378 const gchar *tmp_type = gst_context_get_context_type (tmp);
4380 if (strcmp (context_type, tmp_type) == 0) {
4381 gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
4385 GST_OBJECT_UNLOCK (playbin);
4387 /* Forward if we couldn't answer the message */
4389 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4391 gst_message_unref (msg);
4393 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
4394 GstContext *context;
4396 gst_message_parse_have_context (msg, &context);
4397 gst_play_bin3_update_context (playbin, context);
4398 gst_context_unref (context);
4400 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4402 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4405 /* Doesn't really matter, nothing is using this bus */
4406 return GST_BUS_DROP;
4410 activate_sink (GstPlayBin3 * playbin, GstElement * sink, gboolean * activated)
4414 GstStateChangeReturn sret;
4415 gboolean ret = FALSE;
4420 GST_OBJECT_LOCK (sink);
4421 state = GST_STATE (sink);
4422 GST_OBJECT_UNLOCK (sink);
4423 if (state >= GST_STATE_READY) {
4428 if (!GST_OBJECT_PARENT (sink)) {
4429 bus = gst_bus_new ();
4430 gst_bus_set_sync_handler (bus,
4431 (GstBusSyncHandler) activate_sink_bus_handler, playbin, NULL);
4432 gst_element_set_bus (sink, bus);
4435 sret = gst_element_set_state (sink, GST_STATE_READY);
4436 if (sret == GST_STATE_CHANGE_FAILURE)
4445 gst_element_set_bus (sink, NULL);
4446 gst_object_unref (bus);
4452 /* autoplug-continue decides, if a pad has raw caps that can be exposed
4453 * directly or if further decoding is necessary. We use this to expose
4454 * supported subtitles directly */
4456 /* FIXME 0.11: Remove the checks for ANY caps, a sink should specify
4457 * explicitly the caps it supports and if it claims to support ANY
4458 * caps it really should support everything */
4460 autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps,
4461 GstSourceGroup * group)
4463 gboolean ret = TRUE;
4464 GstPad *sinkpad = NULL;
4465 gboolean activated_sink;
4467 GST_SOURCE_GROUP_LOCK (group);
4469 if (group->text_sink &&
4470 activate_sink (group->playbin, group->text_sink, &activated_sink)) {
4471 sinkpad = gst_element_get_static_pad (group->text_sink, "sink");
4475 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4476 if (!gst_caps_is_any (sinkcaps))
4477 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4478 gst_caps_unref (sinkcaps);
4479 gst_object_unref (sinkpad);
4482 gst_element_set_state (group->text_sink, GST_STATE_NULL);
4484 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4485 ret = !gst_caps_is_subset (caps, subcaps);
4486 gst_caps_unref (subcaps);
4488 /* If autoplugging can stop don't do additional checks */
4492 if (group->audio_sink &&
4493 activate_sink (group->playbin, group->audio_sink, &activated_sink)) {
4495 sinkpad = gst_element_get_static_pad (group->audio_sink, "sink");
4499 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4500 if (!gst_caps_is_any (sinkcaps))
4501 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4502 gst_caps_unref (sinkcaps);
4503 gst_object_unref (sinkpad);
4506 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
4511 if (group->video_sink
4512 && activate_sink (group->playbin, group->video_sink, &activated_sink)) {
4513 sinkpad = gst_element_get_static_pad (group->video_sink, "sink");
4517 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4518 if (!gst_caps_is_any (sinkcaps))
4519 ret = !gst_pad_query_accept_caps (sinkpad, caps);
4520 gst_caps_unref (sinkcaps);
4521 gst_object_unref (sinkpad);
4524 gst_element_set_state (group->video_sink, GST_STATE_NULL);
4528 GST_SOURCE_GROUP_UNLOCK (group);
4530 GST_DEBUG_OBJECT (group->playbin,
4531 "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d",
4532 group, GST_DEBUG_PAD_NAME (pad), caps, ret);
4538 sink_accepts_caps (GstPlayBin3 * playbin, GstElement * sink, GstCaps * caps)
4542 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4543 /* Got the sink pad, now let's see if the element actually does accept the
4544 * caps that we have */
4545 if (!gst_pad_query_accept_caps (sinkpad, caps)) {
4546 gst_object_unref (sinkpad);
4549 gst_object_unref (sinkpad);
4555 /* We are asked to select an element. See if the next element to check
4556 * is a sink. If this is the case, we see if the sink works by setting it to
4557 * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
4558 * expose the raw pad so that we can setup the mixers. */
4559 static GstAutoplugSelectResult
4560 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
4561 GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
4563 GstPlayBin3 *playbin;
4564 GstElement *element;
4566 GstPlaySinkType type;
4568 GList *ave_list = NULL, *l;
4569 GstAVElement *ave = NULL;
4570 GSequence *ave_seq = NULL;
4571 GSequenceIter *seq_iter;
4573 playbin = group->playbin;
4575 GST_DEBUG_OBJECT (playbin, "select group %p for %s:%s, %" GST_PTR_FORMAT,
4576 group, GST_DEBUG_PAD_NAME (pad), caps);
4578 GST_DEBUG_OBJECT (playbin, "checking factory %s", GST_OBJECT_NAME (factory));
4580 /* if it's not a sink, we make sure the element is compatible with
4582 if (!gst_element_factory_list_is_type (factory,
4583 GST_ELEMENT_FACTORY_TYPE_SINK)) {
4584 gboolean isvideodec = gst_element_factory_list_is_type (factory,
4585 GST_ELEMENT_FACTORY_TYPE_DECODER |
4586 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4587 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
4588 gboolean isaudiodec = gst_element_factory_list_is_type (factory,
4589 GST_ELEMENT_FACTORY_TYPE_DECODER |
4590 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
4592 if (!isvideodec && !isaudiodec)
4593 return GST_AUTOPLUG_SELECT_TRY;
4595 GST_SOURCE_GROUP_LOCK (group);
4596 g_mutex_lock (&playbin->elements_lock);
4599 ave_seq = playbin->aelements;
4600 sinkp = &group->audio_sink;
4602 ave_seq = playbin->velements;
4603 sinkp = &group->video_sink;
4607 g_sequence_lookup (ave_seq, factory,
4608 (GCompareDataFunc) avelement_lookup_decoder, NULL);
4610 /* Go to first iter with that decoder */
4612 GSequenceIter *tmp_seq_iter;
4614 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
4615 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
4617 seq_iter = tmp_seq_iter;
4618 } while (!g_sequence_iter_is_begin (seq_iter));
4620 while (!g_sequence_iter_is_end (seq_iter)
4621 && avelement_iter_is_equal (seq_iter, factory)) {
4622 ave = g_sequence_get (seq_iter);
4623 ave_list = g_list_prepend (ave_list, ave);
4624 seq_iter = g_sequence_iter_next (seq_iter);
4627 /* Sort all GstAVElements by their relative ranks and insert
4628 * into the decoders list */
4629 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
4631 ave_list = g_list_prepend (ave_list, NULL);
4634 /* if it is a decoder and we don't have a fixed sink, then find out
4635 * the matching audio/video sink from GstAVElements list */
4636 for (l = ave_list; l; l = l->next) {
4637 gboolean created_sink = FALSE;
4639 ave = (GstAVElement *) l->data;
4641 if (((isaudiodec && !group->audio_sink) ||
4642 (isvideodec && !group->video_sink))) {
4643 if (ave && ave->sink) {
4644 GST_DEBUG_OBJECT (playbin,
4645 "Trying to create sink '%s' for decoder '%s'",
4646 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)),
4647 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4648 if ((*sinkp = gst_element_factory_create (ave->sink, NULL)) == NULL) {
4649 GST_WARNING_OBJECT (playbin,
4650 "Could not create an element from %s",
4651 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4654 if (!activate_sink (playbin, *sinkp, NULL)) {
4655 gst_object_unref (*sinkp);
4657 GST_WARNING_OBJECT (playbin,
4658 "Could not activate sink %s",
4659 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4662 gst_object_ref_sink (*sinkp);
4663 created_sink = TRUE;
4668 /* If it is a decoder and we have a fixed sink for the media
4669 * type it outputs, check that the decoder is compatible with this sink */
4670 if ((isaudiodec && group->audio_sink) || (isvideodec
4671 && group->video_sink)) {
4672 gboolean compatible = FALSE;
4679 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4680 GstPlayFlags flags = gst_play_bin3_get_flags (playbin);
4682 (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
4683 gst_static_caps_get (&raw_video_caps);
4685 caps = gst_pad_query_caps (sinkpad, NULL);
4687 /* If the sink supports raw audio/video, we first check
4688 * if the decoder could output any raw audio/video format
4689 * and assume it is compatible with the sink then. We don't
4690 * do a complete compatibility check here if converters
4691 * are plugged between the decoder and the sink because
4692 * the converters will convert between raw formats and
4693 * even if the decoder format is not supported by the decoder
4694 * a converter will convert it.
4696 * We assume here that the converters can convert between
4699 if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
4700 && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
4701 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
4702 && gst_caps_can_intersect (caps, raw_caps))) {
4704 gst_element_factory_can_src_any_caps (factory, raw_caps)
4705 || gst_element_factory_can_src_any_caps (factory, caps);
4707 compatible = gst_element_factory_can_src_any_caps (factory, caps);
4710 gst_object_unref (sinkpad);
4711 gst_caps_unref (caps);
4717 GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
4718 GST_OBJECT_NAME (factory));
4720 /* If it is not compatible, either continue with the next possible
4721 * sink or if we have a fixed sink, skip the decoder */
4723 gst_element_set_state (*sinkp, GST_STATE_NULL);
4724 gst_object_unref (*sinkp);
4727 g_mutex_unlock (&playbin->elements_lock);
4728 GST_SOURCE_GROUP_UNLOCK (group);
4729 return GST_AUTOPLUG_SELECT_SKIP;
4733 g_list_free (ave_list);
4734 g_mutex_unlock (&playbin->elements_lock);
4735 GST_SOURCE_GROUP_UNLOCK (group);
4736 return GST_AUTOPLUG_SELECT_TRY;
4739 /* it's a sink, see if an instance of it actually works */
4740 GST_DEBUG_OBJECT (playbin, "we found a sink '%s'", GST_OBJECT_NAME (factory));
4743 gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
4745 /* figure out the klass */
4746 if (strstr (klass, "Audio")) {
4747 GST_DEBUG_OBJECT (playbin, "we found an audio sink");
4748 type = GST_PLAY_SINK_TYPE_AUDIO;
4749 sinkp = &group->audio_sink;
4750 } else if (strstr (klass, "Video")) {
4751 GST_DEBUG_OBJECT (playbin, "we found a video sink");
4752 type = GST_PLAY_SINK_TYPE_VIDEO;
4753 sinkp = &group->video_sink;
4755 /* unknown klass, skip this element */
4756 GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
4757 return GST_AUTOPLUG_SELECT_SKIP;
4760 /* if we are asked to do visualisations and it's an audio sink, skip the
4761 * element. We can only do visualisations with raw sinks */
4762 if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
4763 if (type == GST_PLAY_SINK_TYPE_AUDIO) {
4764 GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
4765 return GST_AUTOPLUG_SELECT_SKIP;
4769 /* now see if we already have a sink element */
4770 GST_SOURCE_GROUP_LOCK (group);
4771 if (*sinkp && GST_STATE (*sinkp) >= GST_STATE_READY) {
4772 GstElement *sink = gst_object_ref (*sinkp);
4774 if (sink_accepts_caps (playbin, sink, caps)) {
4775 GST_DEBUG_OBJECT (playbin,
4776 "Existing sink '%s' accepts caps: %" GST_PTR_FORMAT,
4777 GST_ELEMENT_NAME (sink), caps);
4778 gst_object_unref (sink);
4779 GST_SOURCE_GROUP_UNLOCK (group);
4780 return GST_AUTOPLUG_SELECT_EXPOSE;
4782 GST_DEBUG_OBJECT (playbin,
4783 "Existing sink '%s' does not accept caps: %" GST_PTR_FORMAT,
4784 GST_ELEMENT_NAME (sink), caps);
4785 gst_object_unref (sink);
4786 GST_SOURCE_GROUP_UNLOCK (group);
4787 return GST_AUTOPLUG_SELECT_SKIP;
4790 GST_DEBUG_OBJECT (playbin, "we have no pending sink, try to create '%s'",
4791 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4793 if ((*sinkp = gst_element_factory_create (factory, NULL)) == NULL) {
4794 GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
4795 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4796 GST_SOURCE_GROUP_UNLOCK (group);
4797 return GST_AUTOPLUG_SELECT_SKIP;
4802 if (!activate_sink (playbin, element, NULL)) {
4803 GST_WARNING_OBJECT (playbin, "Could not activate sink %s",
4804 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4806 gst_object_unref (element);
4807 GST_SOURCE_GROUP_UNLOCK (group);
4808 return GST_AUTOPLUG_SELECT_SKIP;
4811 /* Check if the selected sink actually supports the
4812 * caps and can be set to READY*/
4813 if (!sink_accepts_caps (playbin, element, caps)) {
4815 gst_element_set_state (element, GST_STATE_NULL);
4816 gst_object_unref (element);
4817 GST_SOURCE_GROUP_UNLOCK (group);
4818 return GST_AUTOPLUG_SELECT_SKIP;
4821 /* remember the sink in the group now, the element is floating, we take
4824 * store the sink in the group, we will configure it later when we
4825 * reconfigure the sink */
4826 GST_DEBUG_OBJECT (playbin, "remember sink");
4827 gst_object_ref_sink (element);
4828 GST_SOURCE_GROUP_UNLOCK (group);
4830 /* tell decodebin to expose the pad because we are going to use this
4832 GST_DEBUG_OBJECT (playbin, "we found a working sink, expose pad");
4834 return GST_AUTOPLUG_SELECT_EXPOSE;
4837 #define GST_PLAY_BIN3_FILTER_CAPS(filter,caps) G_STMT_START { \
4839 GstCaps *intersection = \
4840 gst_caps_intersect_full ((filter), (caps), GST_CAPS_INTERSECT_FIRST); \
4841 gst_caps_unref ((caps)); \
4842 (caps) = intersection; \
4847 autoplug_query_caps (GstElement * uridecodebin, GstPad * pad,
4848 GstElement * element, GstQuery * query, GstSourceGroup * group)
4850 GstCaps *filter, *result = NULL;
4852 GstPad *sinkpad = NULL;
4853 GstElementFactory *factory;
4854 GstElementFactoryListType factory_type;
4855 gboolean have_sink = FALSE;
4857 GST_SOURCE_GROUP_LOCK (group);
4858 gst_query_parse_caps (query, &filter);
4860 factory = gst_element_get_factory (element);
4864 if (gst_element_factory_list_is_type (factory,
4865 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4866 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4868 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4869 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE;
4871 if ((sink = group->video_sink)) {
4872 sinkpad = gst_element_get_static_pad (sink, "sink");
4876 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4877 if (!gst_caps_is_any (sinkcaps)) {
4881 result = gst_caps_merge (result, sinkcaps);
4883 gst_caps_unref (sinkcaps);
4885 gst_object_unref (sinkpad);
4889 } else if (gst_element_factory_list_is_type (factory,
4890 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4891 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
4893 if ((sink = group->audio_sink)) {
4894 sinkpad = gst_element_get_static_pad (sink, "sink");
4898 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4899 if (!gst_caps_is_any (sinkcaps)) {
4903 result = gst_caps_merge (result, sinkcaps);
4905 gst_caps_unref (sinkcaps);
4907 gst_object_unref (sinkpad);
4911 } else if (gst_element_factory_list_is_type (factory,
4912 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
4913 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE;
4915 if ((sink = group->playbin->text_sink)) {
4916 sinkpad = gst_element_get_static_pad (sink, "sink");
4920 sinkcaps = gst_pad_query_caps (sinkpad, filter);
4921 if (!gst_caps_is_any (sinkcaps)) {
4925 result = gst_caps_merge (result, sinkcaps);
4927 gst_caps_unref (sinkcaps);
4929 gst_object_unref (sinkpad);
4933 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4934 GST_PLAY_BIN3_FILTER_CAPS (filter, subcaps);
4938 result = gst_caps_merge (result, subcaps);
4945 GValueArray *factories;
4948 factories = autoplug_factories_cb (uridecodebin, pad, NULL, group);
4949 n = factories->n_values;
4950 for (i = 0; i < n; i++) {
4951 GValue *v = g_value_array_get_nth (factories, i);
4952 GstElementFactory *f = g_value_get_object (v);
4953 const GList *templates;
4955 GstCaps *templ_caps;
4957 if (!gst_element_factory_list_is_type (f, factory_type))
4960 templates = gst_element_factory_get_static_pad_templates (f);
4962 for (l = templates; l; l = l->next) {
4963 templ_caps = gst_static_pad_template_get_caps (l->data);
4965 if (!gst_caps_is_any (templ_caps)) {
4966 GST_PLAY_BIN3_FILTER_CAPS (filter, templ_caps);
4968 result = templ_caps;
4970 result = gst_caps_merge (result, templ_caps);
4972 gst_caps_unref (templ_caps);
4976 g_value_array_free (factories);
4980 GST_SOURCE_GROUP_UNLOCK (group);
4985 /* Add the actual decoder/parser/etc caps at the very end to
4986 * make sure we don't cause empty caps to be returned, e.g.
4987 * if a parser asks us but a decoder is required after it
4988 * because no sink can handle the format directly.
4991 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4994 GstCaps *target_caps = gst_pad_get_pad_template_caps (target);
4995 GST_PLAY_BIN3_FILTER_CAPS (filter, target_caps);
4996 result = gst_caps_merge (result, target_caps);
4997 gst_object_unref (target);
5002 gst_query_set_caps_result (query, result);
5003 gst_caps_unref (result);
5009 autoplug_query_context (GstElement * uridecodebin, GstPad * pad,
5010 GstElement * element, GstQuery * query, GstSourceGroup * group)
5013 GstPad *sinkpad = NULL;
5014 GstElementFactory *factory;
5015 gboolean res = FALSE;
5017 GST_SOURCE_GROUP_LOCK (group);
5019 factory = gst_element_get_factory (element);
5023 if (gst_element_factory_list_is_type (factory,
5024 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
5025 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
5026 if ((sink = group->video_sink)) {
5027 sinkpad = gst_element_get_static_pad (sink, "sink");
5029 res = gst_pad_query (sinkpad, query);
5030 gst_object_unref (sinkpad);
5033 } else if (gst_element_factory_list_is_type (factory,
5034 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
5035 if ((sink = group->audio_sink)) {
5036 sinkpad = gst_element_get_static_pad (sink, "sink");
5038 res = gst_pad_query (sinkpad, query);
5039 gst_object_unref (sinkpad);
5042 } else if (gst_element_factory_list_is_type (factory,
5043 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
5044 if ((sink = group->playbin->text_sink)) {
5045 sinkpad = gst_element_get_static_pad (sink, "sink");
5047 res = gst_pad_query (sinkpad, query);
5048 gst_object_unref (sinkpad);
5056 GST_SOURCE_GROUP_UNLOCK (group);
5062 autoplug_query_cb (GstElement * uridecodebin, GstPad * pad,
5063 GstElement * element, GstQuery * query, GstSourceGroup * group)
5066 switch (GST_QUERY_TYPE (query)) {
5067 case GST_QUERY_CAPS:
5068 return autoplug_query_caps (uridecodebin, pad, element, query, group);
5069 case GST_QUERY_CONTEXT:
5070 return autoplug_query_context (uridecodebin, pad, element, query, group);
5077 notify_source_cb (GstElement * urisourcebin, GParamSpec * pspec,
5078 GstSourceGroup * group)
5080 GstPlayBin3 *playbin;
5083 playbin = group->playbin;
5085 g_object_get (urisourcebin, "source", &source, NULL);
5087 GST_OBJECT_LOCK (playbin);
5088 if (playbin->source)
5089 gst_object_unref (playbin->source);
5090 playbin->source = source;
5091 GST_OBJECT_UNLOCK (playbin);
5093 g_object_notify (G_OBJECT (playbin), "source");
5095 g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP],
5096 0, playbin->source);
5099 /* must be called with the group lock */
5101 group_set_locked_state_unlocked (GstPlayBin3 * playbin, GstSourceGroup * group,
5104 GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
5106 if (group->urisourcebin)
5107 gst_element_set_locked_state (group->urisourcebin, locked);
5108 if (group->suburisourcebin)
5109 gst_element_set_locked_state (group->suburisourcebin, locked);
5115 make_or_reuse_element (GstPlayBin3 * playbin, const gchar * name,
5119 GST_DEBUG_OBJECT (playbin, "reusing existing %s", name);
5120 gst_element_set_state (*elem, GST_STATE_READY);
5121 /* no need to take extra ref, we already have one
5122 * and the bin will add one since it is no longer floating,
5123 * as we added a non-floating ref when removing it from the
5126 GstElement *new_elem;
5127 GST_DEBUG_OBJECT (playbin, "making new %s", name);
5128 new_elem = gst_element_factory_make (name, NULL);
5131 *elem = gst_object_ref (new_elem);
5134 if (GST_OBJECT_PARENT (*elem) != GST_OBJECT_CAST (playbin))
5135 gst_bin_add (GST_BIN_CAST (playbin), *elem);
5140 urisrc_pad_added (GstElement * urisrc, GstPad * pad, GstSourceGroup * group)
5142 GstPadLinkReturn res;
5143 GstPad *sinkpad = NULL;
5144 GstPlayBin3 *playbin;
5146 GST_SOURCE_GROUP_LOCK (group);
5147 playbin = group->playbin;
5148 if (urisrc == group->urisourcebin) {
5149 /* Primary stream, link to the main pad of decodebin3 */
5150 sinkpad = gst_element_get_static_pad (playbin->decodebin, "sink");
5151 if (gst_pad_is_linked (sinkpad)) {
5152 gst_object_unref (GST_OBJECT (sinkpad));
5157 if (sinkpad == NULL) {
5158 /* Auxiliary stream, request a new pad from decodebin */
5159 if ((sinkpad = gst_element_get_request_pad (playbin->decodebin, "sink_%u"))) {
5160 g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
5164 GST_DEBUG_OBJECT (playbin, "New pad %" GST_PTR_FORMAT
5165 " from urisourcebin %" GST_PTR_FORMAT " linking to %"
5166 GST_PTR_FORMAT, pad, urisrc, sinkpad);
5168 res = gst_pad_link (pad, sinkpad);
5169 gst_object_unref (sinkpad);
5171 if (GST_PAD_LINK_FAILED (res))
5174 GST_SOURCE_GROUP_UNLOCK (group);
5179 GST_ERROR_OBJECT (playbin,
5180 "failed to link pad %s:%s to decodebin, reason %s (%d)",
5181 GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
5182 GST_SOURCE_GROUP_UNLOCK (group);
5188 urisrc_pad_removed_cb (GstElement * urisrc, GstPad * pad,
5189 GstSourceGroup * group)
5193 /* must be called with PLAY_BIN_LOCK */
5194 static GstStateChangeReturn
5195 activate_decodebin (GstPlayBin3 * playbin, GstState target)
5197 GstStateChangeReturn state_ret;
5198 GstElement *decodebin = NULL;
5200 if (playbin->decodebin_active)
5201 return GST_STATE_CHANGE_SUCCESS;
5203 GST_LOG_OBJECT (playbin, "Adding and activating decodebin");
5205 if (!make_or_reuse_element (playbin, "decodebin3", &playbin->decodebin))
5207 decodebin = playbin->decodebin;
5209 /* connect pads and other things */
5210 playbin->db_pad_added_id = g_signal_connect (decodebin, "pad-added",
5211 G_CALLBACK (pad_added_cb), playbin);
5212 playbin->db_pad_removed_id = g_signal_connect (decodebin, "pad-removed",
5213 G_CALLBACK (pad_removed_cb), playbin);
5214 playbin->db_no_more_pads_id = g_signal_connect (decodebin, "no-more-pads",
5215 G_CALLBACK (no_more_pads_cb), playbin);
5216 playbin->db_select_stream_id = g_signal_connect (decodebin, "select-stream",
5217 G_CALLBACK (select_stream_cb), playbin);
5218 /* is called when the decodebin is out of data and we can switch to the
5221 /* FIXME: Re-enable if/when decodebin3 supports 'drained' */
5222 playbin->db_drained_id =
5223 g_signal_connect (decodebin, "drained", G_CALLBACK (drained_cb), playbin);
5226 gst_element_set_locked_state (decodebin, TRUE);
5228 gst_element_set_state (decodebin,
5229 target)) == GST_STATE_CHANGE_FAILURE)
5230 goto decodebin_failure;
5231 gst_element_set_locked_state (decodebin, FALSE);
5233 playbin->decodebin_active = TRUE;
5243 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
5245 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
5247 GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
5248 (_("Could not create \"decodebin3\" element.")), (NULL));
5254 GST_DEBUG_OBJECT (playbin, "failed state change of decodebin");
5259 REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id);
5260 REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id);
5261 REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id);
5262 REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id);
5263 REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id);
5264 gst_element_set_state (decodebin, GST_STATE_NULL);
5265 gst_bin_remove (GST_BIN_CAST (playbin), decodebin);
5267 return GST_STATE_CHANGE_FAILURE;
5271 /* must be called with PLAY_BIN_LOCK */
5273 deactivate_decodebin (GstPlayBin3 * playbin)
5275 if (playbin->decodebin) {
5276 GST_LOG_OBJECT (playbin, "Deactivating and removing decodebin");
5277 REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id);
5278 REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id);
5279 REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id);
5280 REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id);
5281 REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id);
5282 gst_bin_remove (GST_BIN_CAST (playbin), playbin->decodebin);
5283 playbin->decodebin_active = FALSE;
5284 playbin->active_stream_types = 0;
5288 /* must be called with PLAY_BIN_LOCK */
5289 static GstStateChangeReturn
5290 activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target)
5292 GstElement *urisrcbin = NULL;
5293 GstElement *suburisrcbin = NULL;
5295 gboolean audio_sink_activated = FALSE;
5296 gboolean video_sink_activated = FALSE;
5297 gboolean text_sink_activated = FALSE;
5298 GstStateChangeReturn state_ret;
5300 g_return_val_if_fail (group->valid, GST_STATE_CHANGE_FAILURE);
5301 g_return_val_if_fail (!group->active, GST_STATE_CHANGE_FAILURE);
5303 GST_DEBUG_OBJECT (playbin, "activating group %p", group);
5305 GST_SOURCE_GROUP_LOCK (group);
5307 /* First set up the custom sinks */
5308 if (playbin->audio_sink)
5309 group->audio_sink = gst_object_ref (playbin->audio_sink);
5312 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO);
5314 if (group->audio_sink) {
5315 if (!activate_sink (playbin, group->audio_sink, &audio_sink_activated)) {
5316 if (group->audio_sink == playbin->audio_sink) {
5319 gst_object_unref (group->audio_sink);
5320 group->audio_sink = NULL;
5325 if (playbin->video_sink)
5326 group->video_sink = gst_object_ref (playbin->video_sink);
5329 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO);
5331 if (group->video_sink) {
5332 if (!activate_sink (playbin, group->video_sink, &video_sink_activated)) {
5333 if (group->video_sink == playbin->video_sink) {
5336 gst_object_unref (group->video_sink);
5337 group->video_sink = NULL;
5342 if (playbin->text_sink)
5343 group->text_sink = gst_object_ref (playbin->text_sink);
5346 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT);
5348 if (group->text_sink) {
5349 if (!activate_sink (playbin, group->text_sink, &text_sink_activated)) {
5350 if (group->text_sink == playbin->text_sink) {
5353 gst_object_unref (group->text_sink);
5354 group->text_sink = NULL;
5360 if (!make_or_reuse_element (playbin, "urisourcebin", &group->urisourcebin))
5362 urisrcbin = group->urisourcebin;
5364 flags = gst_play_sink_get_flags (playbin->playsink);
5366 g_object_set (urisrcbin,
5367 /* configure connection speed */
5368 "connection-speed", playbin->connection_speed / 1000,
5371 /* configure download buffering */
5372 "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
5373 /* configure buffering of demuxed/parsed data */
5374 "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
5375 /* configure buffering parameters */
5376 "buffer-duration", playbin->buffer_duration,
5377 "buffer-size", playbin->buffer_size,
5378 "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
5380 /* we have 1 pending no-more-pads */
5383 group->notify_source_id = g_signal_connect (urisrcbin, "notify::source",
5384 G_CALLBACK (notify_source_cb), group);
5386 /* will be called when a new media type is found. We return a list of decoders
5387 * including sinks for decodebin to try */
5388 group->autoplug_factories_id =
5389 g_signal_connect (urisrcbin, "autoplug-factories",
5390 G_CALLBACK (autoplug_factories_cb), group);
5391 group->autoplug_select_id =
5392 g_signal_connect (urisrcbin, "autoplug-select",
5393 G_CALLBACK (autoplug_select_cb), group);
5394 group->autoplug_continue_id =
5395 g_signal_connect (urisrcbin, "autoplug-continue",
5396 G_CALLBACK (autoplug_continue_cb), group);
5397 group->autoplug_query_id =
5398 g_signal_connect (urisrcbin, "autoplug-query",
5399 G_CALLBACK (autoplug_query_cb), group);
5401 group->urisrc_pad_added_id = g_signal_connect (urisrcbin, "pad-added",
5402 G_CALLBACK (urisrc_pad_added), group);
5403 group->urisrc_pad_removed_id = g_signal_connect (urisrcbin,
5404 "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group);
5406 if (group->suburi) {
5408 if (!make_or_reuse_element (playbin, "urisourcebin",
5409 &group->suburisourcebin))
5411 suburisrcbin = group->suburisourcebin;
5413 g_object_set (suburisrcbin,
5414 /* configure connection speed */
5415 "connection-speed", playbin->connection_speed,
5417 "uri", group->suburi, NULL);
5419 /* connect pads and other things */
5420 group->sub_pad_added_id = g_signal_connect (suburisrcbin, "pad-added",
5421 G_CALLBACK (urisrc_pad_added), group);
5422 group->sub_pad_removed_id = g_signal_connect (suburisrcbin,
5423 "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group);
5425 group->sub_autoplug_continue_id =
5426 g_signal_connect (suburisrcbin, "autoplug-continue",
5427 G_CALLBACK (autoplug_continue_cb), group);
5429 group->sub_autoplug_query_id =
5430 g_signal_connect (suburisrcbin, "autoplug-query",
5431 G_CALLBACK (autoplug_query_cb), group);
5433 /* we have 2 pending no-more-pads */
5435 group->sub_pending = TRUE;
5437 group->sub_pending = FALSE;
5440 /* release the group lock before setting the state of the source bins, they
5441 * might fire signals in this thread that we need to handle with the
5442 * group_lock taken. */
5443 GST_SOURCE_GROUP_UNLOCK (group);
5446 if (gst_element_set_state (suburisrcbin,
5447 target) == GST_STATE_CHANGE_FAILURE) {
5448 GST_DEBUG_OBJECT (playbin,
5449 "failed state change of subtitle urisourcebin");
5450 GST_SOURCE_GROUP_LOCK (group);
5452 REMOVE_SIGNAL (suburisrcbin, group->sub_pad_added_id);
5453 REMOVE_SIGNAL (suburisrcbin, group->sub_pad_removed_id);
5454 REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_continue_id);
5455 REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_query_id);
5456 /* Might already be removed because of an error message */
5457 if (GST_OBJECT_PARENT (suburisrcbin) == GST_OBJECT_CAST (playbin))
5458 gst_bin_remove (GST_BIN_CAST (playbin), suburisrcbin);
5459 if (group->sub_pending) {
5461 group->sub_pending = FALSE;
5463 gst_element_set_state (suburisrcbin, GST_STATE_READY);
5464 g_free (group->suburi);
5465 group->suburi = NULL;
5466 GST_SOURCE_GROUP_UNLOCK (group);
5470 gst_element_set_state (urisrcbin,
5471 target)) == GST_STATE_CHANGE_FAILURE)
5472 goto urisrcbin_failure;
5474 GST_SOURCE_GROUP_LOCK (group);
5475 /* allow state changes of the playbin affect the group elements now */
5476 group_set_locked_state_unlocked (playbin, group, FALSE);
5477 group->active = TRUE;
5478 GST_SOURCE_GROUP_UNLOCK (group);
5487 GST_SOURCE_GROUP_UNLOCK (group);
5489 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
5491 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
5493 GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
5494 (_("Could not create \"urisourcebin\" element.")), (NULL));
5496 GST_SOURCE_GROUP_LOCK (group);
5502 GST_DEBUG_OBJECT (playbin, "failed state change of urisrcbin");
5503 GST_SOURCE_GROUP_LOCK (group);
5508 GST_ERROR_OBJECT (playbin, "failed to activate sinks");
5514 /* delete any custom sinks we might have */
5515 if (group->audio_sink) {
5516 /* If this is a automatically created sink set it to NULL */
5517 if (audio_sink_activated)
5518 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5519 gst_object_unref (group->audio_sink);
5521 group->audio_sink = NULL;
5523 if (group->video_sink) {
5524 /* If this is a automatically created sink set it to NULL */
5525 if (video_sink_activated)
5526 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5527 gst_object_unref (group->video_sink);
5529 group->video_sink = NULL;
5531 if (group->text_sink) {
5532 /* If this is a automatically created sink set it to NULL */
5533 if (text_sink_activated)
5534 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5535 gst_object_unref (group->text_sink);
5537 group->text_sink = NULL;
5540 REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id);
5541 REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id);
5542 REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id);
5543 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
5544 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
5545 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
5546 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
5548 gst_element_set_state (urisrcbin, GST_STATE_NULL);
5549 gst_bin_remove (GST_BIN_CAST (playbin), urisrcbin);
5552 GST_SOURCE_GROUP_UNLOCK (group);
5554 return GST_STATE_CHANGE_FAILURE;
5558 /* unlink a group of urisrcbin from the decodebin.
5559 * must be called with PLAY_BIN_LOCK */
5561 deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
5565 g_return_val_if_fail (group->active, FALSE);
5566 g_return_val_if_fail (group->valid, FALSE);
5568 GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
5570 GST_SOURCE_GROUP_LOCK (group);
5571 group->active = FALSE;
5572 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
5573 GstSourceCombine *combine = &playbin->combiner[i];
5575 GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_type);
5577 if (combine->srcpad) {
5578 source_combine_remove_pads (playbin, combine);
5581 if (combine->combiner) {
5584 /* release and unref requests pad from the combiner */
5585 for (n = 0; n < combine->channels->len; n++) {
5586 GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
5588 gst_element_release_request_pad (combine->combiner, sinkpad);
5589 gst_object_unref (sinkpad);
5591 g_ptr_array_set_size (combine->channels, 0);
5593 gst_element_set_state (combine->combiner, GST_STATE_NULL);
5594 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
5595 combine->combiner = NULL;
5599 /* delete any custom sinks we might have.
5600 * conditionally set them to null if they aren't inside playsink yet */
5601 if (group->audio_sink) {
5602 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->audio_sink),
5603 GST_OBJECT_CAST (playbin->playsink))) {
5604 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5606 gst_object_unref (group->audio_sink);
5608 group->audio_sink = NULL;
5609 if (group->video_sink) {
5610 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->video_sink),
5611 GST_OBJECT_CAST (playbin->playsink))) {
5612 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5614 gst_object_unref (group->video_sink);
5616 group->video_sink = NULL;
5617 if (group->text_sink) {
5618 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->text_sink),
5619 GST_OBJECT_CAST (playbin->playsink))) {
5620 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5622 gst_object_unref (group->text_sink);
5624 group->text_sink = NULL;
5627 if (group->urisourcebin) {
5628 REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id);
5629 REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id);
5630 REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id);
5631 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
5632 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
5633 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
5634 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
5635 gst_bin_remove (GST_BIN_CAST (playbin), group->urisourcebin);
5638 if (group->suburisourcebin) {
5639 REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_added_id);
5640 REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_removed_id);
5641 REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_continue_id);
5642 REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_query_id);
5644 /* Might already be removed because of errors */
5645 if (GST_OBJECT_PARENT (group->suburisourcebin) == GST_OBJECT_CAST (playbin))
5646 gst_bin_remove (GST_BIN_CAST (playbin), group->suburisourcebin);
5649 GST_SOURCE_GROUP_UNLOCK (group);
5654 /* setup the next group to play, this assumes the next_group is valid and
5655 * configured. It swaps out the current_group and activates the valid
5657 static GstStateChangeReturn
5658 setup_next_source (GstPlayBin3 * playbin, GstState target)
5660 GstSourceGroup *new_group, *old_group;
5661 GstStateChangeReturn state_ret;
5663 GST_DEBUG_OBJECT (playbin, "setup sources");
5665 /* see if there is a next group */
5666 GST_PLAY_BIN3_LOCK (playbin);
5667 new_group = playbin->next_group;
5668 if (!new_group || !new_group->valid)
5671 /* first unlink the current source, if any */
5672 old_group = playbin->curr_group;
5673 if (old_group && old_group->valid && old_group->active) {
5674 new_group->stream_changed_pending = TRUE;
5676 gst_play_bin3_update_cached_duration (playbin);
5677 /* unlink our pads with the sink */
5678 deactivate_group (playbin, old_group);
5679 old_group->valid = FALSE;
5682 /* swap old and new */
5683 playbin->curr_group = new_group;
5684 playbin->next_group = old_group;
5686 /* Get decodebin ready now */
5688 activate_decodebin (playbin, target)) == GST_STATE_CHANGE_FAILURE)
5689 goto activate_failed;
5691 /* activate the new group */
5693 activate_group (playbin, new_group,
5694 target)) == GST_STATE_CHANGE_FAILURE)
5695 goto activate_failed;
5697 GST_PLAY_BIN3_UNLOCK (playbin);
5704 GST_DEBUG_OBJECT (playbin, "no next group");
5705 if (target == GST_STATE_READY && new_group && new_group->uri == NULL)
5706 GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL));
5707 GST_PLAY_BIN3_UNLOCK (playbin);
5708 return GST_STATE_CHANGE_FAILURE;
5712 new_group->stream_changed_pending = FALSE;
5713 GST_DEBUG_OBJECT (playbin, "activate failed");
5714 new_group->valid = FALSE;
5715 GST_PLAY_BIN3_UNLOCK (playbin);
5716 return GST_STATE_CHANGE_FAILURE;
5720 /* The group that is currently playing is copied again to the
5721 * next_group so that it will start playing the next time.
5724 save_current_group (GstPlayBin3 * playbin)
5726 GstSourceGroup *curr_group;
5728 GST_DEBUG_OBJECT (playbin, "save current group");
5730 /* see if there is a current group */
5731 GST_PLAY_BIN3_LOCK (playbin);
5732 curr_group = playbin->curr_group;
5733 if (curr_group && curr_group->valid && curr_group->active) {
5734 /* unlink our pads with the sink */
5735 deactivate_group (playbin, curr_group);
5737 /* swap old and new */
5738 playbin->curr_group = playbin->next_group;
5739 playbin->next_group = curr_group;
5740 GST_PLAY_BIN3_UNLOCK (playbin);
5745 /* clear the locked state from all groups. This function is called before a
5746 * state change to NULL is performed on them. */
5748 groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked)
5750 GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
5753 GST_PLAY_BIN3_LOCK (playbin);
5754 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
5755 group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
5756 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
5757 GST_SOURCE_GROUP_LOCK (playbin->next_group);
5758 group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
5759 GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
5760 GST_PLAY_BIN3_UNLOCK (playbin);
5765 static GstStateChangeReturn
5766 gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
5768 GstStateChangeReturn ret;
5769 GstPlayBin3 *playbin;
5770 gboolean do_save = FALSE;
5772 playbin = GST_PLAY_BIN3 (element);
5774 switch (transition) {
5775 case GST_STATE_CHANGE_NULL_TO_READY:
5776 memset (&playbin->duration, 0, sizeof (playbin->duration));
5778 case GST_STATE_CHANGE_READY_TO_PAUSED:
5779 GST_LOG_OBJECT (playbin, "clearing shutdown flag");
5780 memset (&playbin->duration, 0, sizeof (playbin->duration));
5781 g_atomic_int_set (&playbin->shutdown, 0);
5782 do_async_start (playbin);
5784 case GST_STATE_CHANGE_PAUSED_TO_READY:
5786 /* FIXME unlock our waiting groups */
5787 GST_LOG_OBJECT (playbin, "setting shutdown flag");
5788 g_atomic_int_set (&playbin->shutdown, 1);
5789 memset (&playbin->duration, 0, sizeof (playbin->duration));
5791 /* wait for all callbacks to end by taking the lock.
5792 * No dynamic (critical) new callbacks will
5793 * be able to happen as we set the shutdown flag. */
5794 GST_PLAY_BIN3_DYN_LOCK (playbin);
5795 GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
5796 GST_PLAY_BIN3_DYN_UNLOCK (playbin);
5799 case GST_STATE_CHANGE_READY_TO_NULL:
5800 /* we go async to PAUSED, so if that fails, we never make it to PAUSED
5801 * and no state change PAUSED to READY passes here,
5802 * though it is a nice-to-have ... */
5803 if (!g_atomic_int_get (&playbin->shutdown)) {
5807 memset (&playbin->duration, 0, sizeof (playbin->duration));
5809 /* unlock so that all groups go to NULL */
5810 groups_set_locked_state (playbin, FALSE);
5816 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5817 if (ret == GST_STATE_CHANGE_FAILURE)
5820 switch (transition) {
5821 case GST_STATE_CHANGE_READY_TO_PAUSED:
5823 setup_next_source (playbin,
5824 GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
5826 if (ret == GST_STATE_CHANGE_SUCCESS)
5827 ret = GST_STATE_CHANGE_ASYNC;
5830 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5831 do_async_done (playbin);
5832 /* FIXME Release audio device when we implement that */
5834 case GST_STATE_CHANGE_PAUSED_TO_READY:
5835 save_current_group (playbin);
5837 case GST_STATE_CHANGE_READY_TO_NULL:
5842 /* also do missed state change down to READY */
5844 save_current_group (playbin);
5845 /* Deactive the groups, set the urisrcbins to NULL
5848 for (i = 0; i < 2; i++) {
5849 if (playbin->groups[i].active && playbin->groups[i].valid) {
5850 deactivate_group (playbin, &playbin->groups[i]);
5851 playbin->groups[i].valid = FALSE;
5854 if (playbin->groups[i].urisourcebin) {
5855 gst_element_set_state (playbin->groups[i].urisourcebin,
5857 gst_object_unref (playbin->groups[i].urisourcebin);
5858 playbin->groups[i].urisourcebin = NULL;
5861 if (playbin->groups[i].suburisourcebin) {
5862 gst_element_set_state (playbin->groups[i].suburisourcebin,
5864 gst_object_unref (playbin->groups[i].suburisourcebin);
5865 playbin->groups[i].suburisourcebin = NULL;
5869 deactivate_decodebin (playbin);
5870 if (playbin->decodebin) {
5871 gst_object_unref (playbin->decodebin);
5872 playbin->decodebin = NULL;
5873 playbin->decodebin_active = FALSE;
5876 /* Set our sinks back to NULL, they might not be child of playbin */
5877 if (playbin->audio_sink)
5878 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
5879 if (playbin->video_sink)
5880 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
5881 if (playbin->text_sink)
5882 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
5884 if (playbin->video_stream_combiner)
5885 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
5886 if (playbin->audio_stream_combiner)
5887 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
5888 if (playbin->text_stream_combiner)
5889 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
5891 /* make sure the groups don't perform a state change anymore until we
5892 * enable them again */
5893 groups_set_locked_state (playbin, TRUE);
5895 /* Remove all non-persistent contexts */
5896 GST_OBJECT_LOCK (playbin);
5897 for (l = playbin->contexts; l;) {
5898 GstContext *context = l->data;
5900 if (!gst_context_is_persistent (context)) {
5903 gst_context_unref (context);
5906 playbin->contexts = g_list_delete_link (playbin->contexts, l);
5913 if (playbin->source) {
5914 gst_object_unref (playbin->source);
5915 playbin->source = NULL;
5918 GST_OBJECT_UNLOCK (playbin);
5925 if (ret == GST_STATE_CHANGE_NO_PREROLL)
5926 do_async_done (playbin);
5933 do_async_done (playbin);
5935 if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
5936 GstSourceGroup *curr_group;
5938 curr_group = playbin->curr_group;
5940 if (curr_group->active && curr_group->valid) {
5941 /* unlink our pads with the sink */
5942 deactivate_group (playbin, curr_group);
5944 curr_group->valid = FALSE;
5947 /* Swap current and next group back */
5948 playbin->curr_group = playbin->next_group;
5949 playbin->next_group = curr_group;
5956 gst_play_bin3_overlay_expose (GstVideoOverlay * overlay)
5958 GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5960 gst_video_overlay_expose (GST_VIDEO_OVERLAY (playbin->playsink));
5964 gst_play_bin3_overlay_handle_events (GstVideoOverlay * overlay,
5965 gboolean handle_events)
5967 GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5969 gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (playbin->playsink),
5974 gst_play_bin3_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
5975 gint y, gint width, gint height)
5977 GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5979 gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (playbin->playsink),
5980 x, y, width, height);
5984 gst_play_bin3_overlay_set_window_handle (GstVideoOverlay * overlay,
5987 GstPlayBin3 *playbin = GST_PLAY_BIN3 (overlay);
5989 gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (playbin->playsink),
5994 gst_play_bin3_overlay_init (gpointer g_iface, gpointer g_iface_data)
5996 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
5997 iface->expose = gst_play_bin3_overlay_expose;
5998 iface->handle_events = gst_play_bin3_overlay_handle_events;
5999 iface->set_render_rectangle = gst_play_bin3_overlay_set_render_rectangle;
6000 iface->set_window_handle = gst_play_bin3_overlay_set_window_handle;
6004 gst_play_bin3_navigation_send_event (GstNavigation * navigation,
6005 GstStructure * structure)
6007 GstPlayBin3 *playbin = GST_PLAY_BIN3 (navigation);
6009 gst_navigation_send_event (GST_NAVIGATION (playbin->playsink), structure);
6013 gst_play_bin3_navigation_init (gpointer g_iface, gpointer g_iface_data)
6015 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
6017 iface->send_event = gst_play_bin3_navigation_send_event;
6020 static const GList *
6021 gst_play_bin3_colorbalance_list_channels (GstColorBalance * balance)
6023 GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
6026 gst_color_balance_list_channels (GST_COLOR_BALANCE (playbin->playsink));
6030 gst_play_bin3_colorbalance_set_value (GstColorBalance * balance,
6031 GstColorBalanceChannel * channel, gint value)
6033 GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
6035 gst_color_balance_set_value (GST_COLOR_BALANCE (playbin->playsink), channel,
6040 gst_play_bin3_colorbalance_get_value (GstColorBalance * balance,
6041 GstColorBalanceChannel * channel)
6043 GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
6045 return gst_color_balance_get_value (GST_COLOR_BALANCE (playbin->playsink),
6049 static GstColorBalanceType
6050 gst_play_bin3_colorbalance_get_balance_type (GstColorBalance * balance)
6052 GstPlayBin3 *playbin = GST_PLAY_BIN3 (balance);
6055 gst_color_balance_get_balance_type (GST_COLOR_BALANCE
6056 (playbin->playsink));
6060 gst_play_bin3_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
6062 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
6064 iface->list_channels = gst_play_bin3_colorbalance_list_channels;
6065 iface->set_value = gst_play_bin3_colorbalance_set_value;
6066 iface->get_value = gst_play_bin3_colorbalance_get_value;
6067 iface->get_balance_type = gst_play_bin3_colorbalance_get_balance_type;
6071 gst_play_bin3_plugin_init (GstPlugin * plugin, gboolean as_playbin)
6073 GST_DEBUG_CATEGORY_INIT (gst_play_bin3_debug, "playbin3", 0, "play bin");
6076 return gst_element_register (plugin, "playbin", GST_RANK_NONE,
6079 return gst_element_register (plugin, "playbin3", GST_RANK_NONE,