From b9e73d057572d4ba2584aa65b488e8b31b2e1017 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Thu, 9 Nov 2017 11:17:13 +0100 Subject: [PATCH] playbin3: Use uridecodebin3 and link/reconfigure immediately Apologies for the big commit, but it wasn't really possible to split it in anything smaller. * Switch to uridecodebin3 instead of managing urisourcebin and decodebin3 ourselves. No major architectural change with this. * Reconfigure sinks/outputs when needed. This is possible thanks to the various streams-related API. Instead of blocking new pads and waiting for a (fake) no-more-pads to decide what to connect, we instead reconfigure playsink and the combiners to whatever types are currently selected. All of this is done in reconfigure_output(). New pads are immediately connected to (combiners and) sinks, allowing immediate negotiation and usage. * Since elements are always connected, the "cached-duration" feature is gone and queries can reach the target elements. * The auto-plugging related code is currently disabled entirely until we get the new proper API. * Store collections at the GstSourceGroup level and not globally * And more comments a bit everywhere NOTE: gapless is still not functional, but this opens the way to be able to handle it in a streams-aware fashion (where several uridecodebin3 can be active at the same time). --- gst/playback/gstplaybin3.c | 1595 +++++++++++++++--------------------- 1 file changed, 650 insertions(+), 945 deletions(-) diff --git a/gst/playback/gstplaybin3.c b/gst/playback/gstplaybin3.c index 2a8528feee..de479ea702 100644 --- a/gst/playback/gstplaybin3.c +++ b/gst/playback/gstplaybin3.c @@ -20,7 +20,6 @@ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ - /** * SECTION:element-playbin3 * @title: playbin3 @@ -200,10 +199,6 @@ * */ -/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray - * with newer GLib versions (>= 2.31.0) */ -#define GLIB_DISABLE_DEPRECATION_WARNINGS - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -243,26 +238,30 @@ typedef struct _GstPlayBin3 GstPlayBin3; typedef struct _GstPlayBin3Class GstPlayBin3Class; typedef struct _GstSourceGroup GstSourceGroup; typedef struct _GstSourceCombine GstSourceCombine; +typedef struct _SourcePad SourcePad; typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void); -/* has the info for a combiner and provides the link to the sink */ +/* GstSourceCombine controls all the information regarding a certain + * media type. + * + * It can control a custom combiner element (by default none) + */ struct _GstSourceCombine { const gchar *media_type; /* the media type for the combiner */ SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */ GstPlaySinkType type; /* the sink pad type of the combiner */ + GstStreamType stream_type; /* The GstStreamType of the combiner */ GstElement *combiner; /* the combiner */ - GPtrArray *channels; + GPtrArray *channels; /* Array of GstPad ? */ + GstPad *srcpad; /* the source pad of the combiner */ GstPad *sinkpad; /* the sinkpad of the sink when the combiner - * is linked - */ - gulong block_id; + * is linked */ GPtrArray *streams; /* Sorted array of GstStream for the given type */ - gint current_stream; /* Currently selected GstStream */ gboolean has_active_pad; /* stream combiner has the "active-pad" property */ @@ -286,6 +285,14 @@ static const gchar *stream_type_names[] = { "audio", "video", "text" }; + +#define STREAM_TYPES_FORMAT "s%s%s" +#define STREAM_TYPES_ARGS(s) (s) & GST_STREAM_TYPE_AUDIO ? "audio " : "", \ + (s) & GST_STREAM_TYPE_VIDEO ? "video " : "", \ + (s) & GST_STREAM_TYPE_TEXT ? "text " : "" + + + #if 0 /* AUTOPLUG DISABLED */ static void avelements_free (gpointer data); static GSequence *avelements_create (GstPlayBin3 * playbin, @@ -302,6 +309,13 @@ typedef struct guint n_comm_cf; /* number of common caps features */ } GstAVElement; +/* a structure to hold information about a uridecodebin pad */ +struct _SourcePad +{ + GstPad *pad; /* The controlled pad */ + gulong event_probe_id; +}; + /* a structure to hold the objects for decoding a uri and the subtitle uri */ struct _GstSourceGroup @@ -316,12 +330,12 @@ struct _GstSourceGroup /* properties */ gchar *uri; gchar *suburi; - GValueArray *streaminfo; - /* urisourcebins for uri and subtitle uri */ - /* FIXME: Just make this an array of uris */ - GstElement *urisourcebin; - GstElement *suburisourcebin; + /* Bit-wise set of stream types we have requested from uridecodebin3 */ + GstStreamType selected_stream_types; + + /* uridecodebin to handle uri and suburi */ + GstElement *uridecodebin; /* Active sinks for each media type. These are initialized with * the configured or currently used sink, otherwise @@ -331,13 +345,16 @@ struct _GstSourceGroup GstElement *video_sink; GstElement *text_sink; - gint pending; - gboolean sub_pending; + /* List of source pads */ + GList *source_pads; + + /* uridecodebin signals */ + gulong pad_added_id; + gulong pad_removed_id; + gulong select_stream_id; + gulong source_setup_id; + gulong about_to_finish_id; - /* primary uri signals */ - gulong urisrc_pad_added_id; - gulong urisrc_pad_removed_id; - gulong urisrc_source_setup_id; #if 0 /* AUTOPLUG DISABLED */ gulong autoplug_factories_id; gulong autoplug_select_id; @@ -345,18 +362,12 @@ struct _GstSourceGroup gulong autoplug_query_id; #endif - /* subtitle uri signals */ - gulong sub_pad_added_id; - gulong sub_pad_removed_id; -#if 0 /* AUTOPLUG DISABLED */ - gulong sub_autoplug_continue_id; - gulong sub_autoplug_query_id; -#endif - - gulong block_id; - gboolean stream_changed_pending; + /* Active stream collection */ + GstStreamCollection *collection; + + /* buffering message stored for after switching */ GstMessage *pending_buffering_msg; }; @@ -370,20 +381,20 @@ struct _GstSourceGroup #define GST_PLAY_BIN3_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock) /* lock for shutdown */ -#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \ -G_STMT_START { \ - if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \ - goto label; \ - GST_PLAY_BIN3_DYN_LOCK (bin); \ - if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \ - GST_PLAY_BIN3_DYN_UNLOCK (bin); \ - goto label; \ - } \ -} G_STMT_END +#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \ + G_STMT_START { \ + if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \ + goto label; \ + GST_PLAY_BIN3_DYN_LOCK (bin); \ + if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \ + GST_PLAY_BIN3_DYN_UNLOCK (bin); \ + goto label; \ + } \ + } G_STMT_END /* unlock for shutdown */ -#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \ - GST_PLAY_BIN3_DYN_UNLOCK (bin); \ +#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \ + GST_PLAY_BIN3_DYN_UNLOCK (bin); \ /** * GstPlayBin3: @@ -401,24 +412,20 @@ struct _GstPlayBin3 GstSourceGroup *curr_group; /* pointer to the currently playing group */ GstSourceGroup *next_group; /* pointer to the next group */ - /* combiners for different streams */ + /* Array of GstPad controlled by each combiner */ GPtrArray *channels[PLAYBIN_STREAM_LAST]; /* links to combiner pads */ + + /* combiners for different streams */ GstSourceCombine combiner[PLAYBIN_STREAM_LAST]; - /* A global decodebin3 that's used to actually do decoding */ - gboolean decodebin_active; - GstElement *decodebin; - /* Bit-wise set of stream types we have - * requested from decodebin vs stream types - * decodebin has provided */ + /* Bit-wise set of stream types we have requested from uridecodebin3. + * Calculated as the combination of the 'selected_stream_types' of + * each sourcegroup */ GstStreamType selected_stream_types; - GstStreamType active_stream_types; - /* Decodebin signals */ - gulong db_pad_added_id; - gulong db_pad_removed_id; - gulong db_drained_id; - gulong db_select_stream_id; + /* Bit-wise set of configured output stream types (i.e. active + playsink inputs and combiners) */ + GstStreamType active_stream_types; /* properties */ guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */ @@ -477,9 +484,6 @@ struct _GstPlayBin3 GSequence *velements; /* a list of GstAVElements for video stream */ guint64 ring_buffer_max_size; /* 0 means disabled */ - - /* Active stream collection */ - GstStreamCollection *collection; }; struct _GstPlayBin3Class @@ -585,28 +589,27 @@ static gboolean gst_play_bin3_send_event (GstElement * element, static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps); -static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin, - GstState target); +static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin); -static void no_more_pads (GstPlayBin3 * playbin); +static void reconfigure_output (GstPlayBin3 * playbin); static void pad_removed_cb (GstElement * decodebin, GstPad * pad, - GstPlayBin3 * playbin); + GstSourceGroup * group); static gint select_stream_cb (GstElement * decodebin, GstStreamCollection * collection, GstStream * stream, - GstPlayBin3 * playbin); + GstSourceGroup * group); -static void do_stream_selection (GstPlayBin3 * playbin); +static void do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group); static GstElementClass *parent_class; static guint gst_play_bin3_signals[LAST_SIGNAL] = { 0 }; -#define REMOVE_SIGNAL(obj,id) \ -if (id) { \ - g_signal_handler_disconnect (obj, id); \ - id = 0; \ -} +#define REMOVE_SIGNAL(obj,id) \ + if (id) { \ + g_signal_handler_disconnect (obj, id); \ + id = 0; \ + } static void gst_play_bin3_overlay_init (gpointer g_iface, gpointer g_iface_data); @@ -1072,35 +1075,38 @@ init_combiners (GstPlayBin3 * playbin) playbin->combiner[PLAYBIN_STREAM_AUDIO].media_type = "audio"; playbin->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO; + playbin->combiner[PLAYBIN_STREAM_AUDIO].stream_type = GST_STREAM_TYPE_AUDIO; playbin->combiner[PLAYBIN_STREAM_AUDIO].channels = playbin->channels[0]; playbin->combiner[PLAYBIN_STREAM_AUDIO].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1; playbin->combiner[PLAYBIN_STREAM_VIDEO].media_type = "video"; playbin->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO; + playbin->combiner[PLAYBIN_STREAM_VIDEO].stream_type = GST_STREAM_TYPE_VIDEO; playbin->combiner[PLAYBIN_STREAM_VIDEO].channels = playbin->channels[1]; playbin->combiner[PLAYBIN_STREAM_VIDEO].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1; playbin->combiner[PLAYBIN_STREAM_TEXT].media_type = "text"; playbin->combiner[PLAYBIN_STREAM_TEXT].get_media_caps = gst_subtitle_overlay_create_factory_caps; playbin->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT; + playbin->combiner[PLAYBIN_STREAM_TEXT].stream_type = GST_STREAM_TYPE_TEXT; playbin->combiner[PLAYBIN_STREAM_TEXT].channels = playbin->channels[2]; playbin->combiner[PLAYBIN_STREAM_TEXT].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1; } -/* Update the combiner information to be in sync with the current collection */ +/* Update the combiner information to be in sync with the current collection + * + * FIXME : "current" collection doesn't mean anything until we have a "combined" + * collection of all groups */ static void -update_combiner_info (GstPlayBin3 * playbin) +update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection) { guint i, len; - if (playbin->collection == NULL) + if (collection == NULL) return; GST_DEBUG_OBJECT (playbin, "Updating combiner info"); @@ -1111,18 +1117,14 @@ update_combiner_info (GstPlayBin3 * playbin) g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE); playbin->combiner[PLAYBIN_STREAM_AUDIO].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1; playbin->combiner[PLAYBIN_STREAM_VIDEO].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1; playbin->combiner[PLAYBIN_STREAM_TEXT].streams = g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); - playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1; - len = gst_stream_collection_get_size (playbin->collection); + len = gst_stream_collection_get_size (collection); for (i = 0; i < len; i++) { - GstStream *stream = - gst_stream_collection_get_stream (playbin->collection, i); + GstStream *stream = gst_stream_collection_get_stream (collection, i); GstStreamType stype = gst_stream_get_stream_type (stream); if (stype & GST_STREAM_TYPE_AUDIO) { @@ -1145,41 +1147,6 @@ update_combiner_info (GstPlayBin3 * playbin) playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len); } -/* Set the given stream as the selected stream */ -static void -set_selected_stream (GstPlayBin3 * playbin, GstStream * stream) -{ - GstSourceCombine *combine = NULL; - GstStreamType stype = gst_stream_get_stream_type (stream); - - if (stype & GST_STREAM_TYPE_AUDIO) - combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO]; - else if (stype & GST_STREAM_TYPE_VIDEO) - combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO]; - else if (stype & GST_STREAM_TYPE_TEXT) - combine = &playbin->combiner[PLAYBIN_STREAM_TEXT]; - - if (combine) { - if (combine->combiner == NULL) { - guint i, len; - - GST_DEBUG_OBJECT (playbin, "Called for %s (%p)", - gst_stream_get_stream_id (stream), combine->combiner); - - combine->current_stream = -1; - len = combine->streams->len; - for (i = 0; i < len; i++) { - GstStream *cand = g_ptr_array_index (combine->streams, i); - if (cand == stream) { - GST_DEBUG_OBJECT (playbin, "Setting current to %d", i); - combine->current_stream = i; - break; - } - } - } - } -} - static void init_group (GstPlayBin3 * playbin, GstSourceGroup * group) { @@ -1203,6 +1170,8 @@ free_group (GstPlayBin3 * playbin, GstSourceGroup * group) gst_message_unref (group->pending_buffering_msg); group->pending_buffering_msg = NULL; + gst_object_replace ((GstObject **) & group->collection, NULL); + gst_object_replace ((GstObject **) & group->audio_sink, NULL); gst_object_replace ((GstObject **) & group->video_sink, NULL); gst_object_replace ((GstObject **) & group->text_sink, NULL); @@ -1400,9 +1369,6 @@ gst_play_bin3_finalize (GObject * object) g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE); g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE); - if (playbin->decodebin) - gst_object_unref (playbin->decodebin); - if (playbin->elements) gst_plugin_feature_list_free (playbin->elements); @@ -1412,9 +1378,6 @@ gst_play_bin3_finalize (GObject * object) if (playbin->velements) g_sequence_free (playbin->velements); - if (playbin->collection) - gst_object_unref (playbin->collection); - g_rec_mutex_clear (&playbin->lock); g_mutex_clear (&playbin->dyn_lock); g_mutex_clear (&playbin->elements_lock); @@ -1545,43 +1508,6 @@ gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps) return gst_play_sink_convert_sample (playbin->playsink, caps); } -/* Returns current stream number, or -1 if none has been selected yet */ -static int -get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine, - GPtrArray * channels) -{ - /* Internal API cleanup would make this easier... */ - int i; - GstPad *pad, *current; - GstObject *combiner = NULL; - int ret = -1; - - if (!combine->has_active_pad) { - GST_WARNING_OBJECT (playbin, - "combiner doesn't have the \"active-pad\" property"); - return ret; - } - - for (i = 0; i < channels->len; i++) { - pad = g_ptr_array_index (channels, i); - if ((combiner = gst_pad_get_parent (pad))) { - g_object_get (combiner, "active-pad", ¤t, NULL); - gst_object_unref (combiner); - - if (pad == current) { - gst_object_unref (current); - ret = i; - break; - } - - if (current) - gst_object_unref (current); - } - } - - return ret; -} - static gboolean gst_play_bin3_send_custom_event (GstObject * combiner, const gchar * event_name) { @@ -1628,7 +1554,7 @@ gst_play_bin3_set_current_stream (GstPlayBin3 * playbin, if (combine->combiner == NULL) { /* FIXME: Check that the current_value is within range */ *current_value = stream; - do_stream_selection (playbin); + do_stream_selection (playbin, playbin->curr_group); GST_PLAY_BIN3_UNLOCK (playbin); return TRUE; } @@ -1718,35 +1644,6 @@ gst_play_bin3_set_current_text_stream (GstPlayBin3 * playbin, gint stream) &playbin->current_text, stream, &playbin->text_pending_flush_finish); } -static void -source_combine_remove_pads (GstPlayBin3 * playbin, GstSourceCombine * combine) -{ - if (combine->sinkpad) { - GST_LOG_OBJECT (playbin, "unlinking from sink"); - gst_pad_unlink (combine->srcpad, combine->sinkpad); - - /* release back */ - GST_LOG_OBJECT (playbin, "release sink pad"); - gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); - gst_object_unref (combine->sinkpad); - combine->sinkpad = NULL; - } - gst_object_unref (combine->srcpad); - combine->srcpad = NULL; -} - -static GstPadProbeReturn -block_serialized_data_cb (GstPad * pad, GstPadProbeInfo * info, - gpointer user_data) -{ - if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) { - GST_DEBUG_OBJECT (pad, "Letting non-serialized event %s pass", - GST_EVENT_TYPE_NAME (info->data)); - return GST_PAD_PROBE_PASS; - } - - return GST_PAD_PROBE_OK; -} static void gst_play_bin3_set_sink (GstPlayBin3 * playbin, GstPlaySinkType type, @@ -1810,8 +1707,8 @@ gst_play_bin3_set_property (GObject * object, guint prop_id, gst_play_bin3_set_flags (playbin, g_value_get_flags (value)); if (playbin->curr_group) { GST_SOURCE_GROUP_LOCK (playbin->curr_group); - if (playbin->curr_group->urisourcebin) { - g_object_set (playbin->curr_group->urisourcebin, "download", + if (playbin->curr_group->uridecodebin) { + g_object_set (playbin->curr_group->uridecodebin, "download", (g_value_get_flags (value) & GST_PLAY_FLAG_DOWNLOAD) != 0, NULL); } GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); @@ -1885,8 +1782,8 @@ gst_play_bin3_set_property (GObject * object, guint prop_id, playbin->ring_buffer_max_size = g_value_get_uint64 (value); if (playbin->curr_group) { GST_SOURCE_GROUP_LOCK (playbin->curr_group); - if (playbin->curr_group->urisourcebin) { - g_object_set (playbin->curr_group->urisourcebin, + if (playbin->curr_group->uridecodebin) { + g_object_set (playbin->curr_group->uridecodebin, "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL); } GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); @@ -2130,17 +2027,16 @@ get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine, static GList * extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype, - GList * list) + GList * list, GstStreamCollection * collection) { GList *tmp, *res; gint i, nb; res = list; - nb = gst_stream_collection_get_size (playbin->collection); + nb = gst_stream_collection_get_size (collection); for (i = 0; i < nb; i++) { - GstStream *stream = - gst_stream_collection_get_stream (playbin->collection, i); + GstStream *stream = gst_stream_collection_get_stream (collection, i); GstStreamType curtype = gst_stream_get_stream_type (stream); if (stype == curtype) { gboolean already_there = FALSE; @@ -2163,7 +2059,8 @@ extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype, } static GstEvent * -update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) +update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event, + GstSourceGroup * group) { GList *streams = NULL; GList *to_use; @@ -2177,13 +2074,21 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) return event; } + if (!group->collection) { + GST_DEBUG_OBJECT (playbin, + "No stream collection for group, no need to modify SELECT_STREAMS event"); + return event; + } + gst_event_parse_select_streams (event, &streams); to_use = g_list_copy_deep (streams, (GCopyFunc) g_strdup, NULL); /* For each combiner, we want to add all streams of that type to the * selection */ if (playbin->audio_stream_combiner) { - to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use); + to_use = + extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use, + group->collection); combine_id = get_combiner_stream_id (playbin, &playbin->combiner[PLAYBIN_STREAM_AUDIO], streams); @@ -2191,7 +2096,9 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) gst_play_bin3_set_current_audio_stream (playbin, combine_id); } if (playbin->video_stream_combiner) { - to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use); + to_use = + extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use, + group->collection); combine_id = get_combiner_stream_id (playbin, &playbin->combiner[PLAYBIN_STREAM_VIDEO], streams); @@ -2199,7 +2106,9 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) gst_play_bin3_set_current_video_stream (playbin, combine_id); } if (playbin->text_stream_combiner) { - to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use); + to_use = + extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use, + group->collection); combine_id = get_combiner_stream_id (playbin, &playbin->combiner[PLAYBIN_STREAM_TEXT], streams); @@ -2218,6 +2127,82 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) return event; } +/* Returns TRUE if the given list of streams belongs to the stream collection */ +static gboolean +gst_streams_belong_to_collection (GList * streams, + GstStreamCollection * collection) +{ + GList *tmp; + guint i, nb; + + if (streams == NULL || collection == NULL) + return FALSE; + nb = gst_stream_collection_get_size (collection); + if (nb == 0) + return FALSE; + + for (tmp = streams; tmp; tmp = tmp->next) { + const gchar *cand = (const gchar *) tmp->data; + gboolean found = FALSE; + + for (i = 0; i < nb; i++) { + GstStream *stream = gst_stream_collection_get_stream (collection, i); + if (!g_strcmp0 (cand, gst_stream_get_stream_id (stream))) { + found = TRUE; + break; + } + } + if (!found) + return FALSE; + } + return TRUE; +} + +static GstSourceGroup * +get_source_group_for_streams (GstPlayBin3 * playbin, GstEvent * event) +{ + GList *streams; + GstSourceGroup *res = NULL; + + gst_event_parse_select_streams (event, &streams); + if (playbin->curr_group->collection && + gst_streams_belong_to_collection (streams, + playbin->curr_group->collection)) + res = playbin->curr_group; + else if (playbin->next_group->collection && + gst_streams_belong_to_collection (streams, + playbin->next_group->collection)) + res = playbin->next_group; + g_list_free_full (streams, g_free); + + return res; +} + +static GstStreamType +get_stream_type_for_event (GstStreamCollection * collection, GstEvent * event) +{ + GList *stream_list = NULL; + GList *tmp; + GstStreamType res = 0; + guint i, len; + + gst_event_parse_select_streams (event, &stream_list); + len = gst_stream_collection_get_size (collection); + for (tmp = stream_list; tmp; tmp = tmp->next) { + gchar *stid = (gchar *) tmp->data; + + for (i = 0; i < len; i++) { + GstStream *stream = gst_stream_collection_get_stream (collection, i); + if (!g_strcmp0 (stid, gst_stream_get_stream_id (stream))) { + res |= gst_stream_get_stream_type (stream); + } + } + } + g_list_free_full (stream_list, g_free); + + return res; +} + static gboolean gst_play_bin3_send_event (GstElement * element, GstEvent * event) { @@ -2225,6 +2210,7 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) { gboolean res; + GstSourceGroup *group; GST_PLAY_BIN3_LOCK (playbin); GST_LOG_OBJECT (playbin, @@ -2232,14 +2218,32 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) /* This is probably already false, but it doesn't hurt to be sure */ playbin->do_stream_selections = FALSE; + group = get_source_group_for_streams (playbin, event); + if (group == NULL) { + GST_WARNING_OBJECT (playbin, + "Can't figure out to which uridecodebin the select-streams event should be sent to"); + GST_PLAY_BIN3_UNLOCK (playbin); + return FALSE; + } + /* If we have custom combiners, we need to extend the selection with * the list of all streams for that given type since we will be handling * the selection with that combiner */ - event = update_select_streams_event (playbin, event); + event = update_select_streams_event (playbin, event, group); - /* Send this event directly to decodebin, so it works even - * if decodebin didn't add any pads yet */ - res = gst_element_send_event (playbin->decodebin, event); + if (group->collection) { + group->selected_stream_types = + get_stream_type_for_event (group->collection, event); + playbin->selected_stream_types = + playbin->groups[0].selected_stream_types | playbin-> + groups[1].selected_stream_types; + if (playbin->active_stream_types != playbin->selected_stream_types) + reconfigure_output (playbin); + } + + /* Send this event directly to uridecodebin, so it works even + * if uridecodebin didn't add any pads yet */ + res = gst_element_send_event (group->uridecodebin, event); GST_PLAY_BIN3_UNLOCK (playbin); return res; @@ -2263,7 +2267,7 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) /* Called with playbin lock held */ static void -do_stream_selection (GstPlayBin3 * playbin) +do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group) { GstStreamCollection *collection; guint i, nb_streams; @@ -2271,7 +2275,10 @@ do_stream_selection (GstPlayBin3 * playbin) gint nb_video = 0, nb_audio = 0, nb_text = 0; GstStreamType chosen_stream_types = 0; - collection = playbin->collection; + if (group == NULL) + return; + + collection = group->collection; if (collection == NULL) { GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select"); return; @@ -2282,6 +2289,9 @@ do_stream_selection (GstPlayBin3 * playbin) GST_INFO_OBJECT (playbin, "Empty collection received! Ignoring"); } + GST_DEBUG_OBJECT (playbin, "Doing selection on collection with %d streams", + nb_streams); + /* Iterate the collection and choose the streams that match * either the current-* setting, or all streams of a type if there's * a combiner for that type */ @@ -2292,6 +2302,8 @@ do_stream_selection (GstPlayBin3 * playbin) gint pb_stream_type = -1; gboolean select_this = FALSE; + GST_LOG_OBJECT (playbin, "Looking at stream #%d : %s", i, stream_id); + if (stream_type & GST_STREAM_TYPE_AUDIO) { pb_stream_type = PLAYBIN_STREAM_AUDIO; /* Select the stream if it's the current one or if there's a custom selector */ @@ -2332,11 +2344,36 @@ do_stream_selection (GstPlayBin3 * playbin) } if (streams) { - GstEvent *ev = gst_event_new_select_streams (streams); - gst_element_send_event (playbin->decodebin, ev); + if (group->uridecodebin) { + GstEvent *ev = gst_event_new_select_streams (streams); + gst_element_send_event (group->uridecodebin, ev); + } g_list_free (streams); } - playbin->selected_stream_types = chosen_stream_types; + + group->selected_stream_types = chosen_stream_types; + /* Update global selected_stream_types */ + playbin->selected_stream_types = + playbin->groups[0].selected_stream_types | playbin-> + groups[1].selected_stream_types; + if (playbin->active_stream_types != playbin->selected_stream_types) + reconfigure_output (playbin); +} + +/* Return the GstSourceGroup to which this element belongs + * Can be NULL (if it belongs to playsink for example) */ +static GstSourceGroup * +find_source_group_owner (GstPlayBin3 * playbin, GstObject * element) +{ + if (playbin->curr_group->uridecodebin + && gst_object_has_as_ancestor (element, + GST_OBJECT_CAST (playbin->curr_group->uridecodebin))) + return playbin->curr_group; + if (playbin->next_group->uridecodebin + && gst_object_has_as_ancestor (element, + GST_OBJECT_CAST (playbin->next_group->uridecodebin))) + return playbin->next_group; + return NULL; } static void @@ -2389,67 +2426,33 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg) #endif if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) { GstStreamCollection *collection = NULL; - GstObject *src = GST_MESSAGE_SRC (msg); - gboolean pstate = playbin->do_stream_selections; gst_message_parse_stream_collection (msg, &collection); if (collection) { + gboolean pstate = playbin->do_stream_selections; + GstSourceGroup *target_group = NULL; + GST_PLAY_BIN3_LOCK (playbin); GST_DEBUG_OBJECT (playbin, - "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, src); - gst_object_replace ((GstObject **) & playbin->collection, - (GstObject *) collection); - update_combiner_info (playbin); + "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, + msg->src); + target_group = find_source_group_owner (playbin, msg->src); + if (target_group) + gst_object_replace ((GstObject **) & target_group->collection, + (GstObject *) collection); + /* FIXME: Only do the following if it's the current group? */ + if (target_group == playbin->curr_group) + update_combiner_info (playbin, target_group->collection); if (pstate) playbin->do_stream_selections = FALSE; - do_stream_selection (playbin); + do_stream_selection (playbin, target_group); if (pstate) playbin->do_stream_selections = TRUE; GST_PLAY_BIN3_UNLOCK (playbin); gst_object_unref (collection); } - } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAMS_SELECTED) { - GstStreamCollection *collection = NULL; - GstObject *src = GST_MESSAGE_SRC (msg); - GstStreamType chosen_stream_types = 0; - gboolean reconfigure_playsink = FALSE; - - gst_message_parse_streams_selected (msg, &collection); - if (collection) { - guint i, len; - GST_PLAY_BIN3_LOCK (playbin); - GST_DEBUG_OBJECT (playbin, - "STREAMS_SELECTED: Got a collection from %" GST_PTR_FORMAT, src); - gst_object_replace ((GstObject **) & playbin->collection, - (GstObject *) collection); - update_combiner_info (playbin); - len = gst_message_streams_selected_get_size (msg); - for (i = 0; i < len; i++) { - GstStream *stream; - - stream = gst_message_streams_selected_get_stream (msg, i); - set_selected_stream (playbin, stream); - chosen_stream_types |= gst_stream_get_stream_type (stream); - gst_object_unref (stream); - } - - if (playbin->selected_stream_types != chosen_stream_types) { - GST_DEBUG_OBJECT (playbin, "Chosen stream types are changed"); - reconfigure_playsink = TRUE; - } - playbin->selected_stream_types = chosen_stream_types; - GST_PLAY_BIN3_UNLOCK (playbin); - - gst_object_unref (collection); - - if (reconfigure_playsink && - (playbin->selected_stream_types & ~playbin->active_stream_types & - (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO)) == 0) { - no_more_pads_cb (playbin->decodebin, playbin); - } - } } if (msg) @@ -2617,11 +2620,25 @@ update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps) return out_caps; } +static SourcePad * +find_source_pad (GstSourceGroup * group, GstPad * target) +{ + GList *tmp; + + for (tmp = group->source_pads; tmp; tmp = tmp->next) { + SourcePad *res = (SourcePad *) tmp->data; + if (res->pad == target) + return res; + } + return NULL; +} + static GstPadProbeReturn _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) { GstPadProbeReturn ret = GST_PAD_PROBE_OK; - GstPlayBin3 *playbin = (GstPlayBin3 *) udata; + GstSourceGroup *group = (GstSourceGroup *) udata; + GstPlayBin3 *playbin = group->playbin; GstEvent *event = GST_PAD_PROBE_INFO_DATA (info); switch (GST_EVENT_TYPE (event)) { @@ -2652,20 +2669,219 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) return ret; } +static void +control_source_pad (GstSourceGroup * group, GstPad * pad) +{ + SourcePad *sourcepad = g_slice_new0 (SourcePad); + + sourcepad->pad = pad; + sourcepad->event_probe_id = + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + _decodebin_event_probe, group, NULL); + group->source_pads = g_list_append (group->source_pads, sourcepad); +} + +static void +remove_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine) +{ + gint n; + + if (combine->combiner == NULL) { + GST_DEBUG_OBJECT (playbin, "No combiner element to remove"); + return; + } + + /* Go over all sink pads and release them ! */ + for (n = 0; n < combine->channels->len; n++) { + GstPad *sinkpad = g_ptr_array_index (combine->channels, n); + + gst_element_release_request_pad (combine->combiner, sinkpad); + gst_object_unref (sinkpad); + } + g_ptr_array_set_size (combine->channels, 0); + + gst_element_set_state (combine->combiner, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); + combine->combiner = NULL; + +} + +/* Create the combiner element if needed for the given combine */ +static void +create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine) +{ + GstElement *custom_combiner = NULL; + + if (combine->combiner) { + GST_WARNING_OBJECT (playbin, "Combiner element already exists!"); + return; + } + + if (combine->stream_type == GST_STREAM_TYPE_VIDEO) + custom_combiner = playbin->video_stream_combiner; + else if (combine->stream_type == GST_STREAM_TYPE_AUDIO) + custom_combiner = playbin->audio_stream_combiner; + else if (combine->stream_type == GST_STREAM_TYPE_TEXT) + custom_combiner = playbin->text_stream_combiner; + + combine->combiner = custom_combiner; + + if (!combine->combiner) { + GST_DEBUG_OBJECT (playbin, "No custom combiner requested"); + return; + } + + combine->srcpad = gst_element_get_static_pad (combine->combiner, "src"); + + combine->has_active_pad = + g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner), + "active-pad") != NULL; + + if (combine->has_active_pad) + g_signal_connect (combine->combiner, "notify::active-pad", + G_CALLBACK (combiner_active_pad_changed), playbin); + + GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT, + combine->combiner); + gst_bin_add (GST_BIN_CAST (playbin), combine->combiner); + gst_element_sync_state_with_parent (combine->combiner); +} + +static gboolean +combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine, + GstPad * srcpad) +{ + GstPadLinkReturn res; + + GST_DEBUG_OBJECT (playbin, "srcpad %" GST_PTR_FORMAT, srcpad); + + if (combine->combiner) { + GstPad *sinkpad = + gst_element_get_request_pad (combine->combiner, "sink_%u"); + + if (sinkpad == NULL) + goto request_pad_failed; + + GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT, + sinkpad); + + /* find out which properties the sink pad supports */ + combine->has_always_ok = + g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad), + "always-ok") != NULL; + + /* store the pad in the array */ + GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array", + sinkpad); + g_ptr_array_add (combine->channels, sinkpad); + + res = gst_pad_link (srcpad, sinkpad); + if (GST_PAD_LINK_FAILED (res)) + goto failed_combiner_link; + + GST_DEBUG_OBJECT (playbin, + "linked pad %" GST_PTR_FORMAT " to combiner %" GST_PTR_FORMAT, srcpad, + combine->combiner); + + } else { + GST_LOG_OBJECT (playbin, "combine->sinkpad:%" GST_PTR_FORMAT, + combine->sinkpad); + g_assert (combine->sinkpad != NULL); + /* Connect directly to playsink */ + if (gst_pad_is_linked (combine->sinkpad)) + goto sinkpad_already_linked; + + GST_DEBUG_OBJECT (playbin, "Linking new pad straight to playsink"); + res = gst_pad_link (srcpad, combine->sinkpad); + + if (res != GST_PAD_LINK_OK) + goto failed_sinkpad_link; + } + + return TRUE; + + /* Failure cases */ +request_pad_failed: + GST_ELEMENT_ERROR (playbin, CORE, PAD, + ("Internal playbin error."), + ("Failed to get request pad from combiner %p.", combine->combiner)); + return FALSE; + + +sinkpad_already_linked: + GST_ELEMENT_ERROR (playbin, CORE, PAD, + ("Internal playbin error."), ("playsink pad already used !")); + return FALSE; + +failed_sinkpad_link: + GST_ELEMENT_ERROR (playbin, CORE, PAD, + ("Internal playbin error."), + ("Failed to link pad to sink. Error %d", res)); + return FALSE; + +failed_combiner_link: + GST_ELEMENT_ERROR (playbin, CORE, PAD, + ("Internal playbin error."), + ("Failed to link pad to combiner. Error %d", res)); + return FALSE; +} + +static void +combiner_release_pad (GstPlayBin3 * playbin, GstSourceCombine * combine, + GstPad * pad) +{ + if (combine->combiner) { + GstPad *peer = gst_pad_get_peer (pad); + + if (peer) { + GST_DEBUG_OBJECT (playbin, "Removing combiner pad %" GST_PTR_FORMAT, + peer); + g_ptr_array_remove (combine->channels, peer); + + gst_element_release_request_pad (combine->combiner, peer); + gst_object_unref (peer); + } + } else { + /* Release direct link if present */ + if (combine->sinkpad) { + GST_DEBUG_OBJECT (playbin, "Unlinking pad from playsink sinkpad"); + gst_pad_unlink (pad, combine->sinkpad); + } + } +} + +/* Call after pad was unlinked from (potential) combiner */ +static void +release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad) +{ + SourcePad *sourcepad; + + sourcepad = find_source_pad (group, pad); + if (!sourcepad) { + GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?"); + return; + } + + if (sourcepad->event_probe_id) { + gst_pad_remove_probe (pad, sourcepad->event_probe_id); + sourcepad->event_probe_id = 0; + } + + /* Remove from list of controlled pads and check again for EOS status */ + group->source_pads = g_list_remove (group->source_pads, sourcepad); + g_slice_free (SourcePad, sourcepad); +} + /* this function is called when a new pad is added to decodebin. We check the * type of the pad and add it to the combiner element */ static void -pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) +pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstSourceGroup * group) { - GstPad *sinkpad; - GstPadLinkReturn res; GstSourceCombine *combine = NULL; - GstStreamType stream_type; gint pb_stream_type = -1; - GstElement *custom_combiner = NULL; - gulong event_probe_handler; gchar *pad_name; + GstPlayBin3 *playbin = group->playbin; GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown); @@ -2677,17 +2893,11 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) /* major type of the pad, this determines the combiner to use, try exact match first */ if (g_str_has_prefix (pad_name, "video")) { - stream_type = GST_STREAM_TYPE_VIDEO; pb_stream_type = PLAYBIN_STREAM_VIDEO; - custom_combiner = playbin->video_stream_combiner; } else if (g_str_has_prefix (pad_name, "audio")) { - stream_type = GST_STREAM_TYPE_AUDIO; pb_stream_type = PLAYBIN_STREAM_AUDIO; - custom_combiner = playbin->audio_stream_combiner; } else if (g_str_has_prefix (pad_name, "text")) { - stream_type = GST_STREAM_TYPE_TEXT; pb_stream_type = PLAYBIN_STREAM_TEXT; - custom_combiner = playbin->text_stream_combiner; } g_free (pad_name); @@ -2701,105 +2911,12 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) combine = &playbin->combiner[pb_stream_type]; - if (custom_combiner && combine->combiner == NULL) { - combine->combiner = custom_combiner; - /* find out which properties the stream combiner supports */ - combine->has_active_pad = - g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner), - "active-pad") != NULL; + combiner_control_pad (playbin, combine, pad); - if (combine->has_active_pad) - g_signal_connect (combine->combiner, "notify::active-pad", - G_CALLBACK (combiner_active_pad_changed), playbin); - - GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p", - combine->combiner); - gst_element_set_state (combine->combiner, GST_STATE_PAUSED); - gst_bin_add (GST_BIN_CAST (playbin), combine->combiner); - } + control_source_pad (group, pad); GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin); - if (combine->srcpad == NULL) { - if (combine->combiner) { - /* save source pad of the combiner */ - combine->srcpad = gst_element_get_static_pad (combine->combiner, "src"); - } else { - /* no combiner, use the pad as the source pad then */ - combine->srcpad = gst_object_ref (pad); - } - - /* block the combiner srcpad. It's possible that multiple source elements - * pushing data into the combiners before we have a chance to collect all - * streams and connect the sinks, resulting in not-linked errors. After we - * configure the sinks we will unblock them all. */ - GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad); - combine->block_id = - gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, - block_serialized_data_cb, NULL, NULL); - } - - /* get sinkpad for the new stream */ - if (combine->combiner) { - if ((sinkpad = gst_element_get_request_pad (combine->combiner, "sink_%u"))) { - GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner", - GST_DEBUG_PAD_NAME (sinkpad)); - - /* find out which properties the sink pad supports */ - combine->has_always_ok = - g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad), - "always-ok") != NULL; - - /* store the combiner for the pad */ - g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine); - - /* store the pad in the array */ - GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad); - g_ptr_array_add (combine->channels, sinkpad); - - res = gst_pad_link (pad, sinkpad); - if (GST_PAD_LINK_FAILED (res)) - goto link_failed; - - /* store combiner pad so we can release it */ - g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad); - - GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p", - GST_DEBUG_PAD_NAME (pad), combine->combiner); - } else { - goto request_pad_failed; - } - } else { - /* no combiner, don't configure anything, we'll link the new pad directly to - * the sink. */ - sinkpad = NULL; - - /* store the combiner for the pad */ - g_object_set_data (G_OBJECT (pad), "playbin.combine", combine); - } - - event_probe_handler = - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, - _decodebin_event_probe, playbin, NULL); - g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", - ULONG_TO_POINTER (event_probe_handler)); - - playbin->active_stream_types |= stream_type; - - /* If we're expecting either audio or video, - * wait for them to appear before configuring playsink */ - if ((playbin->active_stream_types & GST_STREAM_TYPE_TEXT) && - !(playbin->active_stream_types & GST_STREAM_TYPE_VIDEO)) { - GST_LOG_OBJECT (playbin, "Have text stream but no video yet, Wating"); - } else if ((playbin->selected_stream_types & ~playbin->active_stream_types & - (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO)) - == 0) { - no_more_pads (playbin); - } else { - GST_LOG_OBJECT (playbin, "Active stream types 0x%x, want 0x%x. Waiting", - playbin->active_stream_types, playbin->selected_stream_types); - } - return; /* ERRORS */ @@ -2807,18 +2924,6 @@ unknown_type: GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type"); return; -link_failed: - { - GST_ERROR_OBJECT (playbin, - "failed to link pad %s:%s to combiner, reason %s (%d)", - GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res); - return; - } -request_pad_failed: - GST_ELEMENT_ERROR (playbin, CORE, PAD, - ("Internal playbin error."), - ("Failed to get request pad from combiner %p.", combine->combiner)); - return; shutdown: { GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked"); @@ -2830,109 +2935,40 @@ shutdown: /* called when a pad is removed from the decodebin. We unlink the pad from * the combiner. This will make the combiner select a new pad. */ static void -pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) +pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group) { - GstPad *peer; - GstElement *combiner; GstSourceCombine *combine; - gulong event_probe_handler; - GstStreamType stream_type = GST_STREAM_TYPE_UNKNOWN; - gchar *pad_name; - - if (GST_PAD_IS_SINK (pad)) - return; + GstPlayBin3 *playbin = group->playbin; GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad)); GST_PLAY_BIN3_LOCK (playbin); - if ((event_probe_handler = - POINTER_TO_ULONG (g_object_get_data (G_OBJECT (pad), - "playbin.event_probe_id")))) { - gst_pad_remove_probe (pad, event_probe_handler); - g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", NULL); - } + /* Get combiner for pad */ + if (g_str_has_prefix (GST_PAD_NAME (pad), "video")) + combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO]; + else if (g_str_has_prefix (GST_PAD_NAME (pad), "audio")) + combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO]; + else if (g_str_has_prefix (GST_PAD_NAME (pad), "text")) + combine = &playbin->combiner[PLAYBIN_STREAM_TEXT]; + else + return; - pad_name = gst_object_get_name (GST_OBJECT (pad)); + combiner_release_pad (playbin, combine, pad); + release_source_pad (playbin, group, pad); - if (g_str_has_prefix (pad_name, "video")) { - stream_type = GST_STREAM_TYPE_VIDEO; - } else if (g_str_has_prefix (pad_name, "audio")) { - stream_type = GST_STREAM_TYPE_AUDIO; - } else if (g_str_has_prefix (pad_name, "text")) { - stream_type = GST_STREAM_TYPE_TEXT; - } - - g_free (pad_name); - - if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) { - g_assert (combine->combiner == NULL); - g_assert (combine->srcpad == pad); - source_combine_remove_pads (playbin, combine); - playbin->active_stream_types &= ~stream_type; - goto exit; - } - - /* get the combiner sinkpad */ - if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad"))) - goto not_linked; - - /* unlink the pad now (can fail, the pad is unlinked before it's removed) */ - gst_pad_unlink (pad, peer); - - /* get combiner */ - combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer)); - g_assert (combiner != NULL); - - if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) { - /* remove the pad from the array */ - g_ptr_array_remove (combine->channels, peer); - GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer); - - if (!combine->channels->len && combine->combiner) { - GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed"); - GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner); - source_combine_remove_pads (playbin, combine); - gst_element_set_state (combine->combiner, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); - combine->combiner = NULL; - playbin->active_stream_types &= ~stream_type; - } - } - - /* release the pad to the combiner, this will make the combiner choose a new - * pad. */ - gst_element_release_request_pad (combiner, peer); - gst_object_unref (peer); - - gst_object_unref (combiner); -exit: GST_PLAY_BIN3_UNLOCK (playbin); - - if ((playbin->selected_stream_types & ~playbin->active_stream_types & - (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO)) - == 0) { - no_more_pads (playbin); - } - - return; - - /* ERRORS */ -not_linked: - { - GST_DEBUG_OBJECT (playbin, "pad not linked"); - goto exit; - } } static gint select_stream_cb (GstElement * decodebin, GstStreamCollection * collection, - GstStream * stream, GstPlayBin3 * playbin) + GstStream * stream, GstSourceGroup * group) { GstStreamType stype = gst_stream_get_stream_type (stream); GstElement *combiner = NULL; + GstPlayBin3 *playbin = group->playbin; if (stype & GST_STREAM_TYPE_AUDIO) combiner = playbin->audio_stream_combiner; @@ -2950,190 +2986,129 @@ select_stream_cb (GstElement * decodebin, GstStreamCollection * collection, return -1; } -/* we get called when all pads are available and we must connect the sinks to - * them. - * The main purpose of the code is to see if we have video/audio and subtitles - * and pick the right pipelines to display them. - * - * The combiners installed on the group tell us about the presence of - * audio/video and subtitle streams. This allows us to see if we need - * visualisation, video or/and audio. +/* We get called when the selected stream types change and + * reconfiguration of output (i.e. playsink and potential combiners) + * are required. */ static void -no_more_pads (GstPlayBin3 * playbin) +reconfigure_output (GstPlayBin3 * playbin) { - GstSourceGroup *group; GstPadLinkReturn res; gint i; - gboolean configure; - GST_DEBUG_OBJECT (playbin, "no more pads"); + g_assert (playbin->selected_stream_types != playbin->active_stream_types); - GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown); + GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT, + STREAM_TYPES_ARGS (playbin->selected_stream_types)); + GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT, + STREAM_TYPES_ARGS (playbin->active_stream_types)); GST_PLAY_BIN3_LOCK (playbin); - group = playbin->curr_group; + /* Make sure combiners/playsink are in sync with selected stream types */ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { GstSourceCombine *combine = &playbin->combiner[i]; + gboolean is_selected = + (combine->stream_type & playbin->selected_stream_types) == + combine->stream_type; + gboolean is_active = + (combine->stream_type & playbin->active_stream_types) == + combine->stream_type; - /* check if the specific media type was detected and thus has a combiner - * created for it. If there is the media type, get a sinkpad from the sink - * and link it. We only do this if we have not yet requested the sinkpad - * before. */ - if (combine->srcpad && combine->sinkpad == NULL) { - GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type); + GST_DEBUG_OBJECT (playbin, "Stream type status: '%s' %s %s", + combine->media_type, is_selected ? "selected" : "NOT selected", + is_active ? "active" : "NOT active"); + /* FIXME : Remove asserts below once enough testing has been done */ + + if (is_selected && is_active) { + GST_DEBUG_OBJECT (playbin, "Stream type '%s' already active", + combine->media_type); + } else if (is_active && !is_selected) { + GST_DEBUG_OBJECT (playbin, "Stream type '%s' is no longer requested", + combine->media_type); + + /* Unlink combiner from sink */ + if (combine->srcpad) { + GST_LOG_OBJECT (playbin, "Unlinking from sink"); + if (combine->sinkpad) + gst_pad_unlink (combine->srcpad, combine->sinkpad); + gst_object_unref (combine->srcpad); + combine->srcpad = NULL; + } + + if (combine->sinkpad) { + /* Release playsink sink pad */ + GST_LOG_OBJECT (playbin, "Releasing playsink pad"); + gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); + gst_object_unref (combine->sinkpad); + combine->sinkpad = NULL; + } + + /* Release combiner */ + GST_FIXME_OBJECT (playbin, "Release combiner"); + remove_combiner (playbin, combine); + } else if (!is_active && is_selected) { + GST_DEBUG_OBJECT (playbin, "Stream type '%s' is now requested", + combine->media_type); + + /* If we are shutting down, do *not* add more combiners */ + if (g_atomic_int_get (&playbin->shutdown)) + continue; + + g_assert (combine->sinkpad == NULL); + + /* Request playsink sink pad */ combine->sinkpad = gst_play_sink_request_pad (playbin->playsink, combine->type); gst_object_ref (combine->sinkpad); - } else if (combine->srcpad && combine->sinkpad) { - GST_DEBUG_OBJECT (playbin, "re-using sink pad %d", combine->type); - } else if (combine->sinkpad && combine->srcpad == NULL) { - GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type); - gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); - gst_object_unref (combine->sinkpad); - combine->sinkpad = NULL; - } - if (combine->sinkpad && combine->srcpad && - !gst_pad_is_linked (combine->srcpad)) { - res = gst_pad_link (combine->srcpad, combine->sinkpad); - GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d", - combine->media_type, res); - if (res != GST_PAD_LINK_OK) { - GST_ELEMENT_ERROR (playbin, CORE, PAD, - ("Internal playbin error."), - ("Failed to link combiner to sink. Error %d", res)); + /* Create combiner if needed and link it */ + create_combiner (playbin, combine); + if (combine->combiner) { + res = gst_pad_link (combine->srcpad, combine->sinkpad); + GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d", + combine->media_type, res); + if (res != GST_PAD_LINK_OK) { + GST_ELEMENT_ERROR (playbin, CORE, PAD, + ("Internal playbin error."), + ("Failed to link combiner to sink. Error %d", res)); + } + } } } + + playbin->active_stream_types = playbin->selected_stream_types; + GST_PLAY_BIN3_UNLOCK (playbin); - GST_SOURCE_GROUP_LOCK (group); - GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending, - group->pending - 1); + gst_play_sink_reconfigure (playbin->playsink); - if (group->pending > 0) - group->pending--; + do_async_done (playbin); - if (group->pending == 0) { - /* we are the last group to complete, we will configure the output and then - * signal the other waiters. */ - GST_LOG_OBJECT (playbin, "last group complete"); - configure = TRUE; - } else { - GST_LOG_OBJECT (playbin, "have more pending groups"); - configure = FALSE; - } - GST_SOURCE_GROUP_UNLOCK (group); - - if (configure) { - /* if we have custom sinks, configure them now */ - GST_SOURCE_GROUP_LOCK (group); - - if (group->audio_sink) { - GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT, - group->audio_sink); - gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO, - group->audio_sink); - } - - if (group->video_sink) { - GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT, - group->video_sink); - gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO, - group->video_sink); - } - - if (group->text_sink) { - GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT, - group->text_sink); - gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT, - group->text_sink); - } - - GST_SOURCE_GROUP_UNLOCK (group); - - /* signal the other combiners that they can continue now. */ - GST_PLAY_BIN3_LOCK (playbin); - /* unblock all combiners */ - for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { - GstSourceCombine *combine = &playbin->combiner[i]; - - if (combine->srcpad) { - GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT, - combine->srcpad); - if (combine->block_id) { - gst_pad_remove_probe (combine->srcpad, combine->block_id); - combine->block_id = 0; - } - } - } - GST_PLAY_BIN3_UNLOCK (playbin); - gst_play_sink_reconfigure (playbin->playsink); - } - - GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin); - - if (configure) { - do_async_done (playbin); - } + GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT, + STREAM_TYPES_ARGS (playbin->selected_stream_types)); + GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT, + STREAM_TYPES_ARGS (playbin->active_stream_types)); return; - -shutdown: - { - GST_DEBUG ("ignoring, we are shutting down"); - /* Request a flushing pad from playsink that we then link to the combiner. - * Then we unblock the combiners so that they stop with a WRONG_STATE - * instead of a NOT_LINKED error. - */ - GST_PLAY_BIN3_LOCK (playbin); - for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { - GstSourceCombine *combine = &playbin->combiner[i]; - - if (combine->srcpad) { - if (combine->sinkpad == NULL) { - GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad"); - combine->sinkpad = - gst_play_sink_request_pad (playbin->playsink, - GST_PLAY_SINK_TYPE_FLUSHING); - gst_object_ref (combine->sinkpad); - res = gst_pad_link (combine->srcpad, combine->sinkpad); - GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res); - } - GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT, - combine->srcpad); - if (combine->block_id) { - gst_pad_remove_probe (combine->srcpad, combine->block_id); - combine->block_id = 0; - } - } - } - GST_PLAY_BIN3_UNLOCK (playbin); - return; - } } -/* FIXME : DISABLED UNTIL GAPLESS IS IMPLEMENTED */ -#if 0 static void -drained_cb (GstElement * decodebin, GstSourceGroup * group) +about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group) { - GstPlayBin3 *playbin; - - playbin = group->playbin; - + GstPlayBin3 *playbin = group->playbin; GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group); /* after this call, we should have a next group to activate or we EOS */ g_signal_emit (G_OBJECT (playbin), gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); +#if 0 /* now activate the next group. If the app did not set a uri, this will * fail and we can do EOS */ - setup_next_source (playbin, GST_STATE_PAUSED); -} + setup_next_source (playbin); #endif +} #if 0 /* AUTOPLUG DISABLED */ /* Like gst_element_factory_can_sink_any_caps() but doesn't @@ -4248,10 +4223,8 @@ group_set_locked_state_unlocked (GstPlayBin3 * playbin, GstSourceGroup * group, { GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group); - if (group->urisourcebin) - gst_element_set_locked_state (group->urisourcebin, locked); - if (group->suburisourcebin) - gst_element_set_locked_state (group->suburisourcebin, locked); + if (group->uridecodebin) + gst_element_set_locked_state (group->uridecodebin, locked); return TRUE; } @@ -4281,149 +4254,6 @@ make_or_reuse_element (GstPlayBin3 * playbin, const gchar * name, return TRUE; } -static void -urisrc_pad_added (GstElement * urisrc, GstPad * pad, GstSourceGroup * group) -{ - GstPadLinkReturn res; - GstPad *sinkpad = NULL; - GstPlayBin3 *playbin; - - GST_SOURCE_GROUP_LOCK (group); - playbin = group->playbin; - if (urisrc == group->urisourcebin) { - /* Primary stream, link to the main pad of decodebin3 */ - sinkpad = gst_element_get_static_pad (playbin->decodebin, "sink"); - if (gst_pad_is_linked (sinkpad)) { - gst_object_unref (GST_OBJECT (sinkpad)); - sinkpad = NULL; - } - } - GST_SOURCE_GROUP_UNLOCK (group); - - if (sinkpad == NULL) { - /* Auxiliary stream, request a new pad from decodebin */ - if ((sinkpad = gst_element_get_request_pad (playbin->decodebin, "sink_%u"))) { - g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad); - } - } - if (sinkpad) { - GST_DEBUG_OBJECT (playbin, "New pad %" GST_PTR_FORMAT - " from urisourcebin %" GST_PTR_FORMAT " linking to %" - GST_PTR_FORMAT, pad, urisrc, sinkpad); - - res = gst_pad_link (pad, sinkpad); - gst_object_unref (sinkpad); - - if (GST_PAD_LINK_FAILED (res)) - goto link_failed; - } - return; - -link_failed: - { - GST_ERROR_OBJECT (playbin, - "failed to link pad %s:%s to decodebin, reason %s (%d)", - GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res); - return; - } -} - -static void -urisrc_pad_removed_cb (GstElement * urisrc, GstPad * pad, - GstSourceGroup * group) -{ -} - -/* must be called with PLAY_BIN_LOCK */ -static GstStateChangeReturn -activate_decodebin (GstPlayBin3 * playbin, GstState target) -{ - GstStateChangeReturn state_ret; - GstElement *decodebin = NULL; - - if (playbin->decodebin_active) - return GST_STATE_CHANGE_SUCCESS; - - GST_LOG_OBJECT (playbin, "Adding and activating decodebin"); - - if (!make_or_reuse_element (playbin, "decodebin3", &playbin->decodebin)) - goto no_decodebin; - decodebin = playbin->decodebin; - - /* connect pads and other things */ - playbin->db_pad_added_id = g_signal_connect (decodebin, "pad-added", - G_CALLBACK (pad_added_cb), playbin); - playbin->db_pad_removed_id = g_signal_connect (decodebin, "pad-removed", - G_CALLBACK (pad_removed_cb), playbin); - playbin->db_select_stream_id = g_signal_connect (decodebin, "select-stream", - G_CALLBACK (select_stream_cb), playbin); - /* is called when the decodebin is out of data and we can switch to the - * next uri */ -#if 0 - /* FIXME: Re-enable if/when decodebin3 supports 'drained' */ - playbin->db_drained_id = - g_signal_connect (decodebin, "drained", G_CALLBACK (drained_cb), playbin); -#endif - - gst_element_set_locked_state (decodebin, TRUE); - if ((state_ret = - gst_element_set_state (decodebin, - target)) == GST_STATE_CHANGE_FAILURE) - goto decodebin_failure; - gst_element_set_locked_state (decodebin, FALSE); - - playbin->decodebin_active = TRUE; - - return state_ret; - - -no_decodebin: - { - GstMessage *msg; - - msg = - gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), - "decodebin3"); - gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); - - GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN, - (_("Could not create \"decodebin3\" element.")), (NULL)); - - goto error_cleanup; - } -decodebin_failure: - { - GST_DEBUG_OBJECT (playbin, "failed state change of decodebin"); - goto error_cleanup; - } -error_cleanup:{ - if (decodebin) { - REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id); - gst_element_set_state (decodebin, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (playbin), decodebin); - } - return GST_STATE_CHANGE_FAILURE; - } -} - -/* must be called with PLAY_BIN_LOCK */ -static void -deactivate_decodebin (GstPlayBin3 * playbin) -{ - if (playbin->decodebin) { - GST_LOG_OBJECT (playbin, "Deactivating and removing decodebin"); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id); - REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id); - gst_bin_remove (GST_BIN_CAST (playbin), playbin->decodebin); - playbin->decodebin_active = FALSE; - playbin->active_stream_types = 0; - } -} static void source_setup_cb (GstElement * element, GstElement * source, @@ -4435,10 +4265,9 @@ source_setup_cb (GstElement * element, GstElement * source, /* must be called with PLAY_BIN_LOCK */ static GstStateChangeReturn -activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) +activate_group (GstPlayBin3 * playbin, GstSourceGroup * group) { - GstElement *urisrcbin = NULL; - GstElement *suburisrcbin = NULL; + GstElement *uridecodebin = NULL; GstPlayFlags flags; gboolean audio_sink_activated = FALSE; gboolean video_sink_activated = FALSE; @@ -4505,13 +4334,13 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) } - if (!make_or_reuse_element (playbin, "urisourcebin", &group->urisourcebin)) - goto no_urisrcbin; - urisrcbin = group->urisourcebin; + if (!make_or_reuse_element (playbin, "uridecodebin3", &group->uridecodebin)) + goto no_uridecodebin; + uridecodebin = group->uridecodebin; flags = gst_play_sink_get_flags (playbin->playsink); - g_object_set (urisrcbin, + g_object_set (uridecodebin, /* configure connection speed */ "connection-speed", playbin->connection_speed / 1000, /* configure uri */ @@ -4525,105 +4354,30 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) "buffer-size", playbin->buffer_size, "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL); - /* we have 1 pending no-more-pads */ - group->pending = 1; - - group->urisrc_source_setup_id = g_signal_connect (urisrcbin, "source-setup", + group->pad_added_id = g_signal_connect (uridecodebin, "pad-added", + G_CALLBACK (pad_added_cb), group); + group->pad_removed_id = g_signal_connect (uridecodebin, + "pad-removed", G_CALLBACK (pad_removed_cb), group); + group->select_stream_id = g_signal_connect (uridecodebin, "select-stream", + G_CALLBACK (select_stream_cb), group); + group->source_setup_id = g_signal_connect (uridecodebin, "source-setup", G_CALLBACK (source_setup_cb), group); + group->about_to_finish_id = + g_signal_connect (uridecodebin, "about-to-finish", + G_CALLBACK (about_to_finish_cb), group); -#if 0 /* AUTOPLUG DISABLED */ - /* will be called when a new media type is found. We return a list of decoders - * including sinks for decodebin to try */ - group->autoplug_factories_id = - g_signal_connect (urisrcbin, "autoplug-factories", - G_CALLBACK (autoplug_factories_cb), group); - group->autoplug_select_id = - g_signal_connect (urisrcbin, "autoplug-select", - G_CALLBACK (autoplug_select_cb), group); - group->autoplug_continue_id = - g_signal_connect (urisrcbin, "autoplug-continue", - G_CALLBACK (autoplug_continue_cb), group); - group->autoplug_query_id = - g_signal_connect (urisrcbin, "autoplug-query", - G_CALLBACK (autoplug_query_cb), group); -#endif - - group->urisrc_pad_added_id = g_signal_connect (urisrcbin, "pad-added", - G_CALLBACK (urisrc_pad_added), group); - group->urisrc_pad_removed_id = g_signal_connect (urisrcbin, - "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group); - - if (group->suburi) { - /* subtitles */ - if (!make_or_reuse_element (playbin, "urisourcebin", - &group->suburisourcebin)) - goto no_urisrcbin; - suburisrcbin = group->suburisourcebin; - - g_object_set (suburisrcbin, - /* configure connection speed */ - "connection-speed", playbin->connection_speed, - /* configure uri */ - "uri", group->suburi, NULL); - - /* connect pads and other things */ - group->sub_pad_added_id = g_signal_connect (suburisrcbin, "pad-added", - G_CALLBACK (urisrc_pad_added), group); - group->sub_pad_removed_id = g_signal_connect (suburisrcbin, - "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group); - -#if 0 - group->sub_autoplug_continue_id = - g_signal_connect (suburisrcbin, "autoplug-continue", - G_CALLBACK (autoplug_continue_cb), group); - - group->sub_autoplug_query_id = - g_signal_connect (suburisrcbin, "autoplug-query", - G_CALLBACK (autoplug_query_cb), group); -#endif - - /* we have 2 pending no-more-pads */ - group->pending = 2; - group->sub_pending = TRUE; - } else { - group->sub_pending = FALSE; - } + if (group->suburi) + g_object_set (group->uridecodebin, "suburi", group->suburi, NULL); /* release the group lock before setting the state of the source bins, they * might fire signals in this thread that we need to handle with the * group_lock taken. */ GST_SOURCE_GROUP_UNLOCK (group); - if (suburisrcbin) { - if (gst_element_set_state (suburisrcbin, - target) == GST_STATE_CHANGE_FAILURE) { - GST_DEBUG_OBJECT (playbin, - "failed state change of subtitle urisourcebin"); - GST_SOURCE_GROUP_LOCK (group); - - REMOVE_SIGNAL (suburisrcbin, group->sub_pad_added_id); - REMOVE_SIGNAL (suburisrcbin, group->sub_pad_removed_id); -#if 0 - REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_continue_id); - REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_query_id); -#endif - /* Might already be removed because of an error message */ - if (GST_OBJECT_PARENT (suburisrcbin) == GST_OBJECT_CAST (playbin)) - gst_bin_remove (GST_BIN_CAST (playbin), suburisrcbin); - if (group->sub_pending) { - group->pending--; - group->sub_pending = FALSE; - } - gst_element_set_state (suburisrcbin, GST_STATE_READY); - g_free (group->suburi); - group->suburi = NULL; - GST_SOURCE_GROUP_UNLOCK (group); - } - } if ((state_ret = - gst_element_set_state (urisrcbin, - target)) == GST_STATE_CHANGE_FAILURE) - goto urisrcbin_failure; + gst_element_set_state (uridecodebin, + GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + goto uridecodebin_failure; GST_SOURCE_GROUP_LOCK (group); /* allow state changes of the playbin affect the group elements now */ @@ -4634,26 +4388,26 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) return state_ret; /* ERRORS */ -no_urisrcbin: +no_uridecodebin: { GstMessage *msg; GST_SOURCE_GROUP_UNLOCK (group); msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), - "urisourcebin"); + "uridecodebin3"); gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN, - (_("Could not create \"urisourcebin\" element.")), (NULL)); + (_("Could not create \"uridecodebin3\" element.")), (NULL)); GST_SOURCE_GROUP_LOCK (group); goto error_cleanup; } -urisrcbin_failure: +uridecodebin_failure: { - GST_DEBUG_OBJECT (playbin, "failed state change of urisrcbin"); + GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin"); GST_SOURCE_GROUP_LOCK (group); goto error_cleanup; } @@ -4665,6 +4419,8 @@ sink_failure: error_cleanup: { + group->selected_stream_types = 0; + /* delete any custom sinks we might have */ if (group->audio_sink) { /* If this is a automatically created sink set it to NULL */ @@ -4690,10 +4446,12 @@ error_cleanup: } group->text_sink = NULL; - if (urisrcbin) { - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id); - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id); - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_source_setup_id); + if (uridecodebin) { + REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id); + REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id); + REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id); + REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id); + REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id); #if 0 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id); REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id); @@ -4701,8 +4459,8 @@ error_cleanup: REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id); #endif - gst_element_set_state (urisrcbin, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (playbin), urisrcbin); + gst_element_set_state (uridecodebin, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin); } GST_SOURCE_GROUP_UNLOCK (group); @@ -4711,13 +4469,11 @@ error_cleanup: } } -/* unlink a group of urisrcbin from the decodebin. +/* unlink a group of uridecodebin from the decodebin. * must be called with PLAY_BIN_LOCK */ static gboolean deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) { - gint i; - g_return_val_if_fail (group->active, FALSE); g_return_val_if_fail (group->valid, FALSE); @@ -4725,32 +4481,15 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) GST_SOURCE_GROUP_LOCK (group); group->active = FALSE; - for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { - GstSourceCombine *combine = &playbin->combiner[i]; - GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_type); + group->selected_stream_types = 0; + /* Update global selected_stream_types */ + playbin->selected_stream_types = + playbin->groups[0].selected_stream_types | playbin-> + groups[1].selected_stream_types; + if (playbin->active_stream_types != playbin->selected_stream_types) + reconfigure_output (playbin); - if (combine->srcpad) { - source_combine_remove_pads (playbin, combine); - } - - if (combine->combiner) { - gint n; - - /* release and unref requests pad from the combiner */ - for (n = 0; n < combine->channels->len; n++) { - GstPad *sinkpad = g_ptr_array_index (combine->channels, n); - - gst_element_release_request_pad (combine->combiner, sinkpad); - gst_object_unref (sinkpad); - } - g_ptr_array_set_size (combine->channels, 0); - - gst_element_set_state (combine->combiner, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); - combine->combiner = NULL; - } - } #if 0 /* delete any custom sinks we might have. * conditionally set them to null if they aren't inside playsink yet */ @@ -4780,30 +4519,20 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) group->text_sink = NULL; #endif - if (group->urisourcebin) { - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id); - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id); - REMOVE_SIGNAL (group->urisourcebin, group->urisrc_source_setup_id); + if (group->uridecodebin) { + REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id); + REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id); + REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id); + REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id); + REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id); #if 0 REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id); REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id); REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id); REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id); #endif - gst_bin_remove (GST_BIN_CAST (playbin), group->urisourcebin); - } - - if (group->suburisourcebin) { - REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_added_id); - REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_removed_id); -#if 0 - REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_continue_id); - REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_query_id); -#endif - - /* Might already be removed because of errors */ - if (GST_OBJECT_PARENT (group->suburisourcebin) == GST_OBJECT_CAST (playbin)) - gst_bin_remove (GST_BIN_CAST (playbin), group->suburisourcebin); + gst_element_set_state (group->uridecodebin, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin); } GST_SOURCE_GROUP_UNLOCK (group); @@ -4815,7 +4544,7 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) * configured. It swaps out the current_group and activates the valid * next_group. */ static GstStateChangeReturn -setup_next_source (GstPlayBin3 * playbin, GstState target) +setup_next_source (GstPlayBin3 * playbin) { GstSourceGroup *new_group, *old_group; GstStateChangeReturn state_ret; @@ -4842,15 +4571,9 @@ setup_next_source (GstPlayBin3 * playbin, GstState target) playbin->curr_group = new_group; playbin->next_group = old_group; - /* Get decodebin ready now */ - if ((state_ret = - activate_decodebin (playbin, target)) == GST_STATE_CHANGE_FAILURE) - goto activate_failed; - /* activate the new group */ - if ((state_ret = - activate_group (playbin, new_group, - target)) == GST_STATE_CHANGE_FAILURE) + state_ret = activate_group (playbin, new_group); + if (state_ret == GST_STATE_CHANGE_FAILURE) goto activate_failed; GST_PLAY_BIN3_UNLOCK (playbin); @@ -4861,8 +4584,6 @@ setup_next_source (GstPlayBin3 * playbin, GstState target) no_next_group: { GST_DEBUG_OBJECT (playbin, "no next group"); - if (target == GST_STATE_READY && new_group && new_group->uri == NULL) - GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL)); GST_PLAY_BIN3_UNLOCK (playbin); return GST_STATE_CHANGE_FAILURE; } @@ -4952,8 +4673,9 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) break; case GST_STATE_CHANGE_READY_TO_NULL: /* we go async to PAUSED, so if that fails, we never make it to PAUSED - * and no state change PAUSED to READY passes here, - * though it is a nice-to-have ... */ + * and we will never be called with the GST_STATE_CHANGE_PAUSED_TO_READY. + * Make sure we do go through the same steps (see above) for + * proper cleanup */ if (!g_atomic_int_get (&playbin->shutdown)) { do_save = TRUE; goto async_down; @@ -4972,9 +4694,7 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - if ((ret = - setup_next_source (playbin, - GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + if ((ret = setup_next_source (playbin)) == GST_STATE_CHANGE_FAILURE) goto failure; if (ret == GST_STATE_CHANGE_SUCCESS) ret = GST_STATE_CHANGE_ASYNC; @@ -4994,35 +4714,20 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) /* also do missed state change down to READY */ if (do_save) save_current_group (playbin); - /* Deactive the groups, set the urisrcbins to NULL - * and unref them. - */ + /* Deactive the groups, set uridecodebin to NULL and unref it */ for (i = 0; i < 2; i++) { if (playbin->groups[i].active && playbin->groups[i].valid) { deactivate_group (playbin, &playbin->groups[i]); playbin->groups[i].valid = FALSE; } - if (playbin->groups[i].urisourcebin) { - gst_element_set_state (playbin->groups[i].urisourcebin, + if (playbin->groups[i].uridecodebin) { + gst_element_set_state (playbin->groups[i].uridecodebin, GST_STATE_NULL); - gst_object_unref (playbin->groups[i].urisourcebin); - playbin->groups[i].urisourcebin = NULL; + gst_object_unref (playbin->groups[i].uridecodebin); + playbin->groups[i].uridecodebin = NULL; } - if (playbin->groups[i].suburisourcebin) { - gst_element_set_state (playbin->groups[i].suburisourcebin, - GST_STATE_NULL); - gst_object_unref (playbin->groups[i].suburisourcebin); - playbin->groups[i].suburisourcebin = NULL; - } - } - - deactivate_decodebin (playbin); - if (playbin->decodebin) { - gst_object_unref (playbin->decodebin); - playbin->decodebin = NULL; - playbin->decodebin_active = FALSE; } /* Set our sinks back to NULL, they might not be child of playbin */