playback: apply the upstream 1.14.4
authorEunhae Choi <eunhae1.choi@samsung.com>
Mon, 25 Feb 2019 05:57:13 +0000 (14:57 +0900)
committerEunhae Choi <eunhae1.choi@samsung.com>
Mon, 25 Feb 2019 05:57:28 +0000 (14:57 +0900)
- apply the latest stable playback code to use
  playbin3/decodebin3.

Change-Id: Ifb3e6600d1b3dec971dbb9c26ef8c3a94de82050

13 files changed:
gst/playback/Makefile.am
gst/playback/gstdecodebin3-parse.c
gst/playback/gstdecodebin3.c
gst/playback/gstparsebin.c
gst/playback/gstplayback.c
gst/playback/gstplayback.h
gst/playback/gstplaybackutils.c
gst/playback/gstplaybin2.c
gst/playback/gstplaybin3.c
gst/playback/gstplaysink.c
gst/playback/gsturidecodebin.c
gst/playback/gsturidecodebin3.c [new file with mode: 0644]
gst/playback/gsturisourcebin.c

index 9603dab..180bf5f 100644 (file)
@@ -6,6 +6,7 @@ libgstplayback_la_SOURCES = \
        gstdecodebin2.c \
        gstdecodebin3.c \
        gsturidecodebin.c \
+       gsturidecodebin3.c \
        gsturisourcebin.c \
        gstparsebin.c \
        gstplayback.c \
index 8c51d91..aea0f66 100644 (file)
 #include "gstplayback.h"
 #endif
 
+#define CUSTOM_EOS_QUARK _custom_eos_quark_get ()
+#define CUSTOM_EOS_QUARK_DATA "custom-eos"
+static GQuark
+_custom_eos_quark_get (void)
+{
+  static gsize g_quark;
+
+  if (g_once_init_enter (&g_quark)) {
+    gsize quark = (gsize) g_quark_from_static_string ("decodebin3-custom-eos");
+    g_once_init_leave (&g_quark, quark);
+  }
+  return g_quark;
+}
+
 /* Streams that come from demuxers (input/upstream) */
 /* FIXME : All this is hardcoded. Switch to tree of chains */
 struct _DecodebinInputStream
@@ -63,6 +77,7 @@ static void parsebin_pad_added_cb (GstElement * demux, GstPad * pad,
 static void parsebin_pad_removed_cb (GstElement * demux, GstPad * pad,
     DecodebinInput * input);
 
+/* WITH SELECTION_LOCK TAKEN! */
 static gboolean
 pending_pads_are_eos (DecodebinInput * input)
 {
@@ -77,6 +92,7 @@ pending_pads_are_eos (DecodebinInput * input)
   return TRUE;
 }
 
+/* WITH SELECTION_LOCK TAKEN! */
 static gboolean
 all_inputs_are_eos (GstDecodebin3 * dbin)
 {
@@ -99,6 +115,7 @@ all_inputs_are_eos (GstDecodebin3 * dbin)
   return TRUE;
 }
 
+/* WITH SELECTION_LOCK TAKEN! */
 static void
 check_all_streams_for_eos (GstDecodebin3 * dbin)
 {
@@ -205,7 +222,7 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info,
       case GST_EVENT_STREAM_START:
       {
         GstStream *stream = NULL;
-        guint group_id = G_MAXUINT32;
+        guint group_id = GST_GROUP_ID_INVALID;
 
         if (!gst_event_parse_group_id (ev, &group_id)) {
           GST_FIXME_OBJECT (pad,
@@ -231,10 +248,10 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info,
               gst_object_unref (input->active_stream);
             input->active_stream = stream;
             /* We have the beginning of a stream, get a multiqueue slot and link to it */
-            g_mutex_lock (&input->dbin->selection_lock);
+            SELECTION_LOCK (input->dbin);
             slot = get_slot_for_input (input->dbin, input);
             link_input_to_slot (input, slot);
-            g_mutex_unlock (&input->dbin->selection_lock);
+            SELECTION_UNLOCK (input->dbin);
           } else
             gst_object_unref (stream);
         }
@@ -253,18 +270,21 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info,
         input->saw_eos = TRUE;
         if (all_inputs_are_eos (input->dbin)) {
           GST_DEBUG_OBJECT (pad, "real input pad, marking as EOS");
+          SELECTION_LOCK (input->dbin);
           check_all_streams_for_eos (input->dbin);
+          SELECTION_UNLOCK (input->dbin);
         } else {
           GstPad *peer = gst_pad_get_peer (input->srcpad);
           if (peer) {
             /* Send custom-eos event to multiqueue slot */
-            GstStructure *s;
             GstEvent *event;
 
             GST_DEBUG_OBJECT (pad,
                 "Got EOS end of input stream, post custom-eos");
-            s = gst_structure_new_empty ("decodebin3-custom-eos");
-            event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+            event = gst_event_new_eos ();
+            gst_event_set_seqnum (event, gst_event_get_seqnum (ev));
+            gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event),
+                CUSTOM_EOS_QUARK, (gchar *) CUSTOM_EOS_QUARK_DATA, NULL);
             gst_pad_send_event (peer, event);
             gst_object_unref (peer);
           } else {
@@ -337,12 +357,15 @@ create_input_stream (GstDecodebin3 * dbin, GstStream * stream, GstPad * pad,
       (GstPadProbeCallback) parse_chain_output_probe, res, NULL);
 
   /* Add to list of current input streams */
+  SELECTION_LOCK (dbin);
   dbin->input_streams = g_list_append (dbin->input_streams, res);
+  SELECTION_UNLOCK (dbin);
   GST_DEBUG_OBJECT (pad, "Done creating input stream");
 
   return res;
 }
 
+/* WITH SELECTION_LOCK TAKEN! */
 static void
 remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream)
 {
@@ -362,9 +385,7 @@ remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream)
     }
   }
 
-  g_mutex_lock (&dbin->selection_lock);
   slot = get_slot_for_input (dbin, stream);
-  g_mutex_unlock (&dbin->selection_lock);
   if (slot) {
     slot->pending_stream = NULL;
     slot->input = NULL;
@@ -390,8 +411,6 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
   GstDecodebin3 *dbin = input->dbin;
   GList *tmp, *unused_slot = NULL;
 
-  GST_FIXME_OBJECT (dbin, "Need a lock !");
-
   GST_DEBUG_OBJECT (pad, "Got a buffer ! UNBLOCK !");
 
   /* Any data out the demuxer means it's not creating pads
@@ -402,11 +421,17 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
 
   /* 2. Remove unused streams (push EOS) */
   GST_DEBUG_OBJECT (dbin, "Removing unused streams");
+  SELECTION_LOCK (dbin);
   tmp = dbin->input_streams;
   while (tmp != NULL) {
     DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
     GList *next = tmp->next;
 
+    if (input_stream->input != input) {
+      tmp = next;
+      continue;
+    }
+
     GST_DEBUG_OBJECT (dbin, "Checking input stream %p", input_stream);
     if (input_stream->input_buffer_probe_id) {
       GST_DEBUG_OBJECT (dbin,
@@ -423,6 +448,7 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
     } else
       tmp = next;
   }
+  SELECTION_UNLOCK (dbin);
 
   GST_DEBUG_OBJECT (dbin, "Creating new streams (if needed)");
   /* 3. Create new streams */
@@ -442,10 +468,10 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
       /* See if we can link it straight away */
       input_stream->active_stream = stream;
 
-      g_mutex_lock (&dbin->selection_lock);
+      SELECTION_LOCK (dbin);
       slot = get_slot_for_input (dbin, input_stream);
       link_input_to_slot (input_stream, slot);
-      g_mutex_unlock (&dbin->selection_lock);
+      SELECTION_UNLOCK (dbin);
 
       /* Remove the buffer and event probe */
       gst_pad_remove_probe (ppad->pad, ppad->buffer_probe);
@@ -458,7 +484,7 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
   input->pending_pads = NULL;
 
   /* Weed out unused multiqueue slots */
-  g_mutex_lock (&dbin->selection_lock);
+  SELECTION_LOCK (dbin);
   for (tmp = dbin->slots; tmp; tmp = tmp->next) {
     MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
     GST_LOG_OBJECT (dbin, "Slot %d input:%p", slot->id, slot->input);
@@ -467,7 +493,7 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
           g_list_append (unused_slot, gst_object_ref (slot->sink_pad));
     }
   }
-  g_mutex_unlock (&dbin->selection_lock);
+  SELECTION_UNLOCK (dbin);
 
   for (tmp = unused_slot; tmp; tmp = tmp->next) {
     GstPad *sink_pad = (GstPad *) tmp->data;
@@ -536,11 +562,11 @@ parsebin_pad_added_cb (GstElement * demux, GstPad * pad, DecodebinInput * input)
 
   input->pending_pads = g_list_append (input->pending_pads, ppad);
 
-  /* FIXME : ONLY DO FOR THIS PARSEBIN/INPUT ! */
   /* Check if all existing input streams have a buffer probe set */
   for (tmp = dbin->input_streams; tmp; tmp = tmp->next) {
     DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data;
-    if (input_stream->input_buffer_probe_id == 0) {
+    if (input_stream->input == input &&
+        input_stream->input_buffer_probe_id == 0) {
       GST_DEBUG_OBJECT (input_stream->srcpad, "Adding blocking buffer probe");
       input_stream->input_buffer_probe_id =
           gst_pad_add_probe (input_stream->srcpad,
@@ -575,11 +601,8 @@ parsebin_pad_removed_cb (GstElement * demux, GstPad * pad, DecodebinInput * inp)
 
       SELECTION_LOCK (dbin);
       slot = get_slot_for_input (dbin, input);
-      SELECTION_UNLOCK (dbin);
 
       remove_input_stream (dbin, input);
-
-      SELECTION_LOCK (dbin);
       if (slot && g_list_find (dbin->slots, slot) && slot->is_drained) {
         /* if slot is still there and already drained, remove it in here */
         if (slot->output) {
index 773f624..68b0c76 100644 (file)
@@ -181,6 +181,21 @@ GST_DEBUG_CATEGORY_STATIC (decodebin3_debug);
 
 #define EXTRA_DEBUG 1
 
+#define CUSTOM_FINAL_EOS_QUARK _custom_final_eos_quark_get ()
+#define CUSTOM_FINAL_EOS_QUARK_DATA "custom-final-eos"
+static GQuark
+_custom_final_eos_quark_get (void)
+{
+  static gsize g_quark;
+
+  if (g_once_init_enter (&g_quark)) {
+    gsize quark =
+        (gsize) g_quark_from_static_string ("decodebin3-custom-final-eos");
+    g_once_init_leave (&g_quark, quark);
+  }
+  return g_quark;
+}
+
 typedef struct _GstDecodebin3 GstDecodebin3;
 typedef struct _GstDecodebin3Class GstDecodebin3Class;
 
@@ -200,23 +215,22 @@ struct _GstDecodebin3
   GList *other_inputs;
   /* counter for input */
   guint32 input_counter;
-  /* Current stream group_id (default : G_MAXUINT32) */
+  /* Current stream group_id (default : GST_GROUP_ID_INVALID) */
   /* FIXME : Needs to be resetted appropriately (when upstream changes ?) */
   guint32 current_group_id;
   /* End of variables protected by input_lock */
 
   GstElement *multiqueue;
 
-  /* FIXME : Mutex for protecting values below */
-  GstStreamCollection *collection;      /* Active collection */
-
+  /* selection_lock protects access to following variables */
+  GMutex selection_lock;
   GList *input_streams;         /* List of DecodebinInputStream for active collection */
   GList *output_streams;        /* List of DecodebinOutputStream used for output */
   GList *slots;                 /* List of MultiQueueSlot */
   guint slot_id;
 
-  /* selection_lock protects access to following variables */
-  GMutex selection_lock;
+  /* Active collection */
+  GstStreamCollection *collection;
   /* requested selection of stream-id to activate post-multiqueue */
   GList *requested_selection;
   /* list of stream-id currently activated in output */
@@ -236,7 +250,6 @@ struct _GstDecodebin3
    * FIXME : Is this really needed ? */
   GList *pending_collection;
 
-
   /* Factories */
   GMutex factories_lock;
   guint32 factories_cookie;
@@ -280,6 +293,12 @@ struct _DecodebinInput
 
   gulong pad_added_sigid;
   gulong pad_removed_sigid;
+  gulong drained_sigid;
+
+  /* TRUE if the input got drained
+   * FIXME : When do we reset it if re-used ?
+   */
+  gboolean drained;
 
   /* HACK : Remove these fields */
   /* List of PendingPad structures */
@@ -349,8 +368,6 @@ typedef struct _PendingPad
 } PendingPad;
 
 /* properties */
-#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
-
 enum
 {
   PROP_0,
@@ -361,6 +378,7 @@ enum
 enum
 {
   SIGNAL_SELECT_STREAM,
+  SIGNAL_ABOUT_TO_FINISH,
   LAST_SIGNAL
 };
 static guint gst_decodebin3_signals[LAST_SIGNAL] = { 0 };
@@ -496,8 +514,7 @@ static void free_multiqueue_slot_async (GstDecodebin3 * dbin,
     MultiQueueSlot * slot);
 
 static GstStreamCollection *get_merged_collection (GstDecodebin3 * dbin);
-static void update_requested_selection (GstDecodebin3 * dbin,
-    GstStreamCollection * collection);
+static void update_requested_selection (GstDecodebin3 * dbin);
 
 /* FIXME: Really make all the parser stuff a self-contained helper object */
 #include "gstdecodebin3-parse.c"
@@ -553,6 +570,17 @@ gst_decodebin3_class_init (GstDecodebin3Class * klass)
       _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
       G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
 
+  /**
+   * GstDecodebin3::about-to-finish:
+   *
+   * This signal is emitted when the data for the selected URI is
+   * entirely buffered and it is safe to specify anothe URI.
+   */
+  gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH] =
+      g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+      0, G_TYPE_NONE);
+
 
   element_class->request_new_pad =
       GST_DEBUG_FUNCPTR (gst_decodebin3_request_new_pad);
@@ -593,7 +621,7 @@ gst_decodebin3_init (GstDecodebin3 * dbin)
       "max-size-buffers", 0, "use-interleave", TRUE, NULL);
   gst_bin_add ((GstBin *) dbin, dbin->multiqueue);
 
-  dbin->current_group_id = G_MAXUINT32;
+  dbin->current_group_id = GST_GROUP_ID_INVALID;
 
   g_mutex_init (&dbin->factories_lock);
   g_mutex_init (&dbin->selection_lock);
@@ -698,7 +726,7 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id)
   GstDecodebin3 *dbin = input->dbin;
 
   if (input->group_id != *group_id) {
-    if (input->group_id != G_MAXUINT32)
+    if (input->group_id != GST_GROUP_ID_INVALID)
       GST_WARNING_OBJECT (dbin,
           "Group id changed (%" G_GUINT32_FORMAT " -> %" G_GUINT32_FORMAT
           ") on input %p ", input->group_id, *group_id, input);
@@ -706,7 +734,7 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id)
   }
 
   if (*group_id != dbin->current_group_id) {
-    if (dbin->current_group_id == G_MAXUINT32) {
+    if (dbin->current_group_id == GST_GROUP_ID_INVALID) {
       GST_DEBUG_OBJECT (dbin, "Setting current group id to %" G_GUINT32_FORMAT,
           *group_id);
       dbin->current_group_id = *group_id;
@@ -718,6 +746,30 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id)
   return FALSE;
 }
 
+static void
+parsebin_drained_cb (GstElement * parsebin, DecodebinInput * input)
+{
+  GstDecodebin3 *dbin = input->dbin;
+  gboolean all_drained;
+  GList *tmp;
+
+  GST_WARNING_OBJECT (dbin, "input %p drained", input);
+  input->drained = TRUE;
+
+  all_drained = dbin->main_input->drained;
+  for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) {
+    DecodebinInput *data = (DecodebinInput *) tmp->data;
+
+    all_drained &= data->drained;
+  }
+
+  if (all_drained) {
+    GST_WARNING_OBJECT (dbin, "All inputs drained. Posting about-to-finish");
+    g_signal_emit (dbin, gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH], 0,
+        NULL);
+  }
+}
+
 /* Call with INPUT_LOCK taken */
 static gboolean
 ensure_input_parsebin (GstDecodebin3 * dbin, DecodebinInput * input)
@@ -736,6 +788,9 @@ ensure_input_parsebin (GstDecodebin3 * dbin, DecodebinInput * input)
     input->pad_removed_sigid =
         g_signal_connect (input->parsebin, "pad-removed",
         (GCallback) parsebin_pad_removed_cb, input);
+    input->drained_sigid =
+        g_signal_connect (input->parsebin, "drained",
+        (GCallback) parsebin_drained_cb, input);
     g_signal_connect (input->parsebin, "autoplug-continue",
         (GCallback) parsebin_autoplug_continue_cb, dbin);
   }
@@ -834,6 +889,7 @@ gst_decodebin3_input_pad_unlink (GstPad * pad, GstObject * parent)
       input->collection = NULL;
     }
 
+    SELECTION_LOCK (dbin);
     collection = get_merged_collection (dbin);
     if (collection && collection != dbin->collection) {
       GstMessage *msg;
@@ -847,14 +903,17 @@ gst_decodebin3_input_pad_unlink (GstPad * pad, GstObject * parent)
           gst_message_new_stream_collection ((GstObject *) dbin,
           dbin->collection);
 
+      SELECTION_UNLOCK (dbin);
       gst_element_post_message (GST_ELEMENT_CAST (dbin), msg);
-      update_requested_selection (dbin, dbin->collection);
-    }
+      update_requested_selection (dbin);
+    } else
+      SELECTION_UNLOCK (dbin);
 
     gst_bin_remove (GST_BIN (dbin), input->parsebin);
     gst_element_set_state (input->parsebin, GST_STATE_NULL);
     g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid);
     g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid);
+    g_signal_handler_disconnect (input->parsebin, input->drained_sigid);
     gst_pad_remove_probe (input->parsebin_sink, probe_id);
     gst_object_unref (input->parsebin);
     gst_object_unref (input->parsebin_sink);
@@ -884,6 +943,7 @@ free_input (GstDecodebin3 * dbin, DecodebinInput * input)
   if (input->parsebin) {
     g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid);
     g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid);
+    g_signal_handler_disconnect (input->parsebin, input->drained_sigid);
     gst_element_set_state (input->parsebin, GST_STATE_NULL);
     gst_object_unref (input->parsebin);
     gst_object_unref (input->parsebin_sink);
@@ -910,7 +970,7 @@ create_new_input (GstDecodebin3 * dbin, gboolean main)
   input = g_new0 (DecodebinInput, 1);
   input->dbin = dbin;
   input->is_main = main;
-  input->group_id = G_MAXUINT32;
+  input->group_id = GST_GROUP_ID_INVALID;
   if (main)
     input->ghost_sink = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
   else {
@@ -940,13 +1000,13 @@ gst_decodebin3_request_new_pad (GstElement * element, GstPadTemplate * temp,
 
   /* We are ignoring names for the time being, not sure it makes any sense
    * within the context of decodebin3 ... */
-  INPUT_LOCK (dbin);
   input = create_new_input (dbin, FALSE);
   if (input) {
+    INPUT_LOCK (dbin);
     dbin->other_inputs = g_list_append (dbin->other_inputs, input);
     res = input->ghost_sink;
+    INPUT_UNLOCK (dbin);
   }
-  INPUT_UNLOCK (dbin);
 
   return res;
 }
@@ -1011,14 +1071,12 @@ stream_in_list (GList * list, const gchar * sid)
 }
 
 static void
-update_requested_selection (GstDecodebin3 * dbin,
-    GstStreamCollection * collection)
+update_requested_selection (GstDecodebin3 * dbin)
 {
   guint i, nb;
   GList *tmp = NULL;
   GstStreamType used_types = 0;
-
-  nb = gst_stream_collection_get_size (collection);
+  GstStreamCollection *collection;
 
   /* 1. Is there a pending SELECT_STREAMS we can return straight away since
    *  the switch handler will take care of the pending selection */
@@ -1029,6 +1087,13 @@ update_requested_selection (GstDecodebin3 * dbin,
     goto beach;
   }
 
+  collection = dbin->collection;
+  if (G_UNLIKELY (collection == NULL)) {
+    GST_DEBUG_OBJECT (dbin, "No current GstStreamCollection");
+    goto beach;
+  }
+  nb = gst_stream_collection_get_size (collection);
+
   /* 2. If not, are we in EXPOSE_ALL_MODE ? If so, match everything */
   GST_FIXME_OBJECT (dbin, "Implement EXPOSE_ALL_MODE");
 
@@ -1257,6 +1322,7 @@ handle_stream_collection (GstDecodebin3 * dbin,
 #endif
 
   /* Store collection for later usage */
+  SELECTION_LOCK (dbin);
   if (dbin->collection == NULL) {
     dbin->collection = collection;
   } else {
@@ -1272,6 +1338,7 @@ handle_stream_collection (GstDecodebin3 * dbin,
     /* dbin->pending_collection = */
     /*     g_list_append (dbin->pending_collection, collection); */
   }
+  SELECTION_UNLOCK (dbin);
 }
 
 static void
@@ -1294,6 +1361,8 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message)
         posting_collection = TRUE;
         INPUT_UNLOCK (dbin);
       }
+
+      SELECTION_LOCK (dbin);
       if (dbin->collection && collection != dbin->collection) {
         /* Replace collection message, we most likely aggregated it */
         GstMessage *new_msg;
@@ -1303,6 +1372,8 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message)
         gst_message_unref (message);
         message = new_msg;
       }
+      SELECTION_UNLOCK (dbin);
+
       if (collection)
         gst_object_unref (collection);
       break;
@@ -1315,7 +1386,7 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message)
 
   if (posting_collection) {
     /* Figure out a selection for that collection */
-    update_requested_selection (dbin, dbin->collection);
+    update_requested_selection (dbin);
   }
 }
 
@@ -1479,6 +1550,84 @@ is_selection_done (GstDecodebin3 * dbin)
   return msg;
 }
 
+/* Must be called with SELECTION_LOCK taken */
+static void
+check_all_slot_for_eos (GstDecodebin3 * dbin)
+{
+  gboolean all_drained = TRUE;
+  GList *iter;
+
+  GST_DEBUG_OBJECT (dbin, "check slot for eos");
+
+  for (iter = dbin->slots; iter; iter = iter->next) {
+    MultiQueueSlot *slot = iter->data;
+
+    if (!slot->output)
+      continue;
+
+    if (slot->is_drained) {
+      GST_LOG_OBJECT (slot->sink_pad, "slot %p is drained", slot);
+      continue;
+    }
+
+    all_drained = FALSE;
+    break;
+  }
+
+  if (all_drained) {
+    INPUT_LOCK (dbin);
+    if (!pending_pads_are_eos (dbin->main_input))
+      all_drained = FALSE;
+
+    if (all_drained) {
+      for (iter = dbin->other_inputs; iter; iter = iter->next) {
+        if (!pending_pads_are_eos ((DecodebinInput *) iter->data)) {
+          all_drained = FALSE;
+          break;
+        }
+      }
+    }
+    INPUT_UNLOCK (dbin);
+  }
+
+  if (all_drained) {
+    GST_DEBUG_OBJECT (dbin,
+        "All active slots are drained, and no pending input, push EOS");
+
+    for (iter = dbin->input_streams; iter; iter = iter->next) {
+      DecodebinInputStream *input = (DecodebinInputStream *) iter->data;
+      GstPad *peer = gst_pad_get_peer (input->srcpad);
+
+      /* Send EOS to all slots */
+      if (peer) {
+        GstEvent *stream_start, *eos;
+
+        stream_start =
+            gst_pad_get_sticky_event (input->srcpad, GST_EVENT_STREAM_START, 0);
+
+        /* First forward a custom STREAM_START event to reset the EOS status (if any) */
+        if (stream_start) {
+          GstStructure *s;
+          GstEvent *custom_stream_start = gst_event_copy (stream_start);
+          gst_event_unref (stream_start);
+          s = (GstStructure *) gst_event_get_structure (custom_stream_start);
+          gst_structure_set (s, "decodebin3-flushing-stream-start",
+              G_TYPE_BOOLEAN, TRUE, NULL);
+          gst_pad_send_event (peer, custom_stream_start);
+        }
+
+        eos = gst_event_new_eos ();
+        gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (eos),
+            CUSTOM_FINAL_EOS_QUARK, (gchar *) CUSTOM_FINAL_EOS_QUARK_DATA,
+            NULL);
+        gst_pad_send_event (peer, eos);
+        gst_object_unref (peer);
+      } else
+        GST_DEBUG_OBJECT (dbin, "no output");
+    }
+  }
+}
+
 static GstPadProbeReturn
 multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
     MultiQueueSlot * slot)
@@ -1494,7 +1643,16 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
       case GST_EVENT_STREAM_START:
       {
         GstStream *stream = NULL;
-        const gchar *stream_id;
+        const GstStructure *s = gst_event_get_structure (ev);
+
+        /* Drop STREAM_START events used to cleanup multiqueue */
+        if (s
+            && gst_structure_has_field (s,
+                "decodebin3-flushing-stream-start")) {
+          ret = GST_PAD_PROBE_HANDLED;
+          gst_event_unref (ev);
+          break;
+        }
 
         gst_event_parse_stream (ev, &stream);
         if (stream == NULL) {
@@ -1503,8 +1661,8 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
           break;
         }
         slot->is_drained = FALSE;
-        stream_id = gst_stream_get_stream_id (stream);
-        GST_DEBUG_OBJECT (pad, "Stream Start '%s'", stream_id);
+        GST_DEBUG_OBJECT (pad, "Stream Start '%s'",
+            gst_stream_get_stream_id (stream));
         if (slot->active_stream == NULL) {
           slot->active_stream = stream;
         } else if (slot->active_stream != stream) {
@@ -1559,10 +1717,44 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
       }
         break;
       case GST_EVENT_EOS:
-        /* FIXME : Figure out */
+      {
+        gboolean was_drained = slot->is_drained;
+        slot->is_drained = TRUE;
+
+        /* Custom EOS handling first */
+        if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev),
+                CUSTOM_EOS_QUARK)) {
+          /* remove custom-eos */
+          gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev),
+              CUSTOM_EOS_QUARK, NULL, NULL);
+          GST_LOG_OBJECT (pad, "Received custom EOS");
+          ret = GST_PAD_PROBE_HANDLED;
+          SELECTION_LOCK (dbin);
+          if (slot->input == NULL) {
+            GST_DEBUG_OBJECT (pad,
+                "Got custom-eos from null input stream, remove output stream");
+            /* Remove the output */
+            if (slot->output) {
+              DecodebinOutputStream *output = slot->output;
+              dbin->output_streams =
+                  g_list_remove (dbin->output_streams, output);
+              free_output_stream (dbin, output);
+            }
+            slot->probe_id = 0;
+            dbin->slots = g_list_remove (dbin->slots, slot);
+            free_multiqueue_slot_async (dbin, slot);
+            ret = GST_PAD_PROBE_REMOVE;
+          } else if (!was_drained) {
+            check_all_slot_for_eos (dbin);
+          }
+          if (ret == GST_PAD_PROBE_HANDLED)
+            gst_event_unref (ev);
+          SELECTION_UNLOCK (dbin);
+          break;
+        }
+
         GST_FIXME_OBJECT (pad, "EOS on multiqueue source pad. input:%p",
             slot->input);
-        slot->is_drained = TRUE;
         if (slot->input == NULL) {
           GstPad *peer;
           GST_DEBUG_OBJECT (pad,
@@ -1585,33 +1777,23 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info,
           }
           slot->probe_id = 0;
           dbin->slots = g_list_remove (dbin->slots, slot);
-          free_multiqueue_slot_async (dbin, slot);
           SELECTION_UNLOCK (dbin);
+
+          free_multiqueue_slot_async (dbin, slot);
           ret = GST_PAD_PROBE_REMOVE;
-        }
-        break;
-      case GST_EVENT_CUSTOM_DOWNSTREAM:
-        if (gst_event_has_name (ev, "decodebin3-custom-eos")) {
-          slot->is_drained = TRUE;
+        } else if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev),
+                CUSTOM_FINAL_EOS_QUARK)) {
+          GST_DEBUG_OBJECT (pad, "Got final eos, propagating downstream");
+        } else {
+          GST_DEBUG_OBJECT (pad, "Got regular eos (all_inputs_are_eos)");
+          /* drop current event as eos will be sent in check_all_slot_for_eos
+           * when all output streams are also eos */
           ret = GST_PAD_PROBE_DROP;
           SELECTION_LOCK (dbin);
-          if (slot->input == NULL) {
-            GST_DEBUG_OBJECT (pad,
-                "Got custom-eos from null input stream, remove output stream");
-            /* Remove the output */
-            if (slot->output) {
-              DecodebinOutputStream *output = slot->output;
-              dbin->output_streams =
-                  g_list_remove (dbin->output_streams, output);
-              free_output_stream (dbin, output);
-            }
-            slot->probe_id = 0;
-            dbin->slots = g_list_remove (dbin->slots, slot);
-            free_multiqueue_slot_async (dbin, slot);
-            ret = GST_PAD_PROBE_REMOVE;
-          }
+          check_all_slot_for_eos (dbin);
           SELECTION_UNLOCK (dbin);
         }
+      }
         break;
       default:
         break;
@@ -1658,11 +1840,14 @@ create_new_slot (GstDecodebin3 * dbin, GstStreamType type)
       gst_stream_type_get_name (type));
   slot = g_new0 (MultiQueueSlot, 1);
   slot->dbin = dbin;
+
   slot->id = dbin->slot_id++;
+
   slot->type = type;
   slot->sink_pad = gst_element_get_request_pad (dbin->multiqueue, "sink_%u");
   if (slot->sink_pad == NULL)
     goto fail;
+
   it = gst_pad_iterate_internal_links (slot->sink_pad);
   if (!it || (gst_iterator_next (it, &item)) != GST_ITERATOR_OK
       || ((slot->src_pad = g_value_dup_object (&item)) == NULL)) {
@@ -1683,7 +1868,9 @@ create_new_slot (GstDecodebin3 * dbin, GstStreamType type)
 
   GST_DEBUG ("Created new slot %u (%p) (%s:%s)", slot->id, slot,
       GST_DEBUG_PAD_NAME (slot->src_pad));
+
   dbin->slots = g_list_append (dbin->slots, slot);
+
   return slot;
 
   /* ERRORS */
@@ -1760,7 +1947,6 @@ get_slot_for_input (GstDecodebin3 * dbin, DecodebinInputStream * input)
 static void
 link_input_to_slot (DecodebinInputStream * input, MultiQueueSlot * slot)
 {
-  GstEvent *event;
   if (slot->input != NULL && slot->input != input) {
     GST_ERROR_OBJECT (slot->dbin,
         "Trying to link input to an already used slot");
@@ -1769,9 +1955,6 @@ link_input_to_slot (DecodebinInputStream * input, MultiQueueSlot * slot)
   gst_pad_link_full (input->srcpad, slot->sink_pad, GST_PAD_LINK_CHECK_NOTHING);
   slot->pending_stream = input->active_stream;
   slot->input = input;
-  event = gst_pad_get_sticky_event (input->srcpad, GST_EVENT_STREAM_START, 0);
-  if (event)
-    gst_pad_send_event (slot->sink_pad, event);
 }
 
 #if 0
@@ -2357,6 +2540,14 @@ handle_stream_switch (GstDecodebin3 * dbin, GList * select_streams,
     g_list_free (unknown);
   }
 
+  if (to_activate && !slots_to_reassign) {
+    for (tmp = to_activate; tmp; tmp = tmp->next) {
+      MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data;
+      gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_IDLE,
+          (GstPadProbeCallback) idle_reconfigure, slot, NULL);
+    }
+  }
+
   /* For all streams to deactivate, add an idle probe where we will do
    * the unassignment and switch over */
   for (tmp = slots_to_reassign; tmp; tmp = tmp->next) {
@@ -2403,6 +2594,8 @@ ghost_pad_event_probe (GstPad * pad, GstPadProbeInfo * info,
         SELECTION_UNLOCK (dbin);
         GST_DEBUG_OBJECT (pad,
             "Already handled/handling that SELECT_STREAMS event");
+        gst_event_unref (event);
+        ret = GST_PAD_PROBE_HANDLED;
         break;
       }
       dbin->select_streams_seqnum = seqnum;
@@ -2625,6 +2818,7 @@ gst_decodebin3_change_state (GstElement * element, GstStateChange transition)
       }
       g_list_free (dbin->slots);
       dbin->slots = NULL;
+      dbin->current_group_id = GST_GROUP_ID_INVALID;
       /* Free inputs */
     }
       break;
index ce3c317..87ee11f 100644 (file)
 #include "gstplay-enum.h"
 #include "gstplayback.h"
 #include "gstplaybackutils.h"
+#include "gstrawcaps.h"
 
 /* generic templates */
 static GstStaticPadTemplate parse_bin_sink_template =
@@ -169,8 +170,6 @@ struct _GstParseBin
   gboolean have_type;           /* if we received the have_type signal */
   guint have_type_id;           /* signal id for have-type from typefind */
 
-  gboolean async_pending;       /* async-start has been emitted */
-
   GMutex dyn_lock;              /* lock protecting pad blocking */
   gboolean shutdown;            /* if we are shutting down */
   GList *blocked_pads;          /* pads that have set to block */
@@ -179,6 +178,13 @@ struct _GstParseBin
 
   GList *filtered;              /* elements for which error messages are filtered */
   GList *filtered_errors;       /* filtered error messages */
+
+  GMutex cleanup_lock;          /* Mutex used to protect the cleanup thread */
+  GThread *cleanup_thread;      /* thread used to free chains asynchronously.
+                                 * We store it to make sure we end up joining it
+                                 * before stopping the element.
+                                 * Protected by the object lock */
+
 };
 
 struct _GstParseBinClass
@@ -243,9 +249,6 @@ enum
 static GstBinClass *parent_class;
 static guint gst_parse_bin_signals[LAST_SIGNAL] = { 0 };
 
-static void do_async_start (GstParseBin * parsebin);
-static void do_async_done (GstParseBin * parsebin);
-
 static void type_found (GstElement * typefind, guint probability,
     GstCaps * caps, GstParseBin * parse_bin);
 
@@ -279,6 +282,7 @@ static void gst_parse_pad_update_stream_collection (GstParsePad * parsepad,
     GstStreamCollection * collection);
 
 static GstCaps *get_pad_caps (GstPad * pad);
+static GstStreamType guess_stream_type_from_caps (GstCaps * caps);
 
 #define EXPOSE_LOCK(parsebin) G_STMT_START {                           \
     GST_LOG_OBJECT (parsebin,                                          \
@@ -650,11 +654,11 @@ gst_parse_bin_class_init (GstParseBinClass * klass)
    * emitted before looking for any elements that can handle that stream.
    *
    * >   Invocation of signal handlers stops after the first signal handler
-   * >   returns #FALSE. Signal handlers are invoked in the order they were
+   * >   returns %FALSE. Signal handlers are invoked in the order they were
    * >   connected in.
    *
-   * Returns: #TRUE if you wish ParseBin to look for elements that can
-   * handle the given @caps. If #FALSE, those caps will be considered as
+   * Returns: %TRUE if you wish ParseBin to look for elements that can
+   * handle the given @caps. If %FALSE, those caps will be considered as
    * final and the pad will be exposed as such (see 'pad-added' signal of
    * #GstElement).
    */
@@ -706,11 +710,11 @@ gst_parse_bin_class_init (GstParseBinClass * klass)
    * the application to perform additional sorting or filtering on the element
    * factory array.
    *
-   * The callee should copy and modify @factories or return #NULL if the
+   * The callee should copy and modify @factories or return %NULL if the
    * order should not change.
    *
    * >   Invocation of signal handlers stops after one signal handler has
-   * >   returned something else than #NULL. Signal handlers are invoked in
+   * >   returned something else than %NULL. Signal handlers are invoked in
    * >   the order they were connected in.
    * >   Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
    * >   signal, they will never be invoked!
@@ -778,7 +782,7 @@ gst_parse_bin_class_init (GstParseBinClass * klass)
    * be used to tell the element about the downstream supported caps
    * for example.
    *
-   * Returns: #TRUE if the query was handled, #FALSE otherwise.
+   * Returns: %TRUE if the query was handled, %FALSE otherwise.
    */
   gst_parse_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
       g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
@@ -938,6 +942,9 @@ gst_parse_bin_init (GstParseBin * parse_bin)
   parse_bin->expose_allstreams = DEFAULT_EXPOSE_ALL_STREAMS;
   parse_bin->connection_speed = DEFAULT_CONNECTION_SPEED;
 
+  g_mutex_init (&parse_bin->cleanup_lock);
+  parse_bin->cleanup_thread = NULL;
+
   GST_OBJECT_FLAG_SET (parse_bin, GST_BIN_FLAG_STREAMS_AWARE);
 }
 
@@ -976,6 +983,7 @@ gst_parse_bin_finalize (GObject * object)
   g_mutex_clear (&parse_bin->dyn_lock);
   g_mutex_clear (&parse_bin->subtitle_lock);
   g_mutex_clear (&parse_bin->factories_lock);
+  g_mutex_clear (&parse_bin->cleanup_lock);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -1099,9 +1107,18 @@ static gboolean
 gst_parse_bin_autoplug_continue (GstElement * element, GstPad * pad,
     GstCaps * caps)
 {
+  static GstStaticCaps raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
+  GST_DEBUG_OBJECT (element, "caps %" GST_PTR_FORMAT, caps);
+
+  /* If it matches our target caps, expose it */
+  if (gst_caps_can_intersect (caps, gst_static_caps_get (&raw_caps))) {
+    GST_DEBUG_OBJECT (element, "autoplug-continue returns FALSE");
+    return FALSE;
+  }
+
   GST_DEBUG_OBJECT (element, "autoplug-continue returns TRUE");
 
-  /* by default we always continue */
   return TRUE;
 }
 
@@ -1514,6 +1531,7 @@ unknown_type:
 
     chain->deadend_details = deadend_details;
     chain->deadend = TRUE;
+    chain->drained = TRUE;
     chain->endcaps = caps;
     gst_object_replace ((GstObject **) & chain->current_pad, NULL);
 
@@ -1537,7 +1555,6 @@ unknown_type:
         GST_ELEMENT_ERROR (parsebin, STREAM, TYPE_NOT_FOUND,
             (_("Could not determine type of stream")), (NULL));
       }
-      do_async_done (parsebin);
     }
     return;
   }
@@ -2944,11 +2961,19 @@ gst_parse_chain_start_free_hidden_groups_thread (GstParseChain * chain)
   GThread *thread;
   GError *error = NULL;
   GList *old_groups;
+  GstParseBin *parsebin = chain->parsebin;
 
   old_groups = chain->old_groups;
   if (!old_groups)
     return;
 
+  /* If we already have a thread running, wait for it to finish */
+  g_mutex_lock (&parsebin->cleanup_lock);
+  if (parsebin->cleanup_thread) {
+    g_thread_join (parsebin->cleanup_thread);
+    parsebin->cleanup_thread = NULL;
+  }
+
   chain->old_groups = NULL;
   thread = g_thread_try_new ("free-hidden-groups",
       (GThreadFunc) gst_parse_chain_free_hidden_groups, old_groups, &error);
@@ -2957,11 +2982,14 @@ gst_parse_chain_start_free_hidden_groups_thread (GstParseChain * chain)
         error ? error->message : "unknown reason");
     g_clear_error (&error);
     chain->old_groups = old_groups;
+    g_mutex_unlock (&parsebin->cleanup_lock);
     return;
   }
+
+  parsebin->cleanup_thread = thread;
+  g_mutex_unlock (&parsebin->cleanup_lock);
+
   GST_DEBUG_OBJECT (chain->parsebin, "Started free-hidden-groups thread");
-  /* We do not need to wait for it or get any results from it */
-  g_thread_unref (thread);
 }
 
 /* gst_parse_group_new:
@@ -3216,14 +3244,13 @@ drain_and_switch_chains (GstParseChain * chain, GstParsePad * drainpad,
 beach:
   CHAIN_MUTEX_UNLOCK (chain);
 
-  GST_DEBUG ("Chain %p (handled:%d, last_group:%d, drained:%d, switched:%d)",
-      chain, handled, *last_group, chain->drained, *switched);
+  GST_DEBUG
+      ("Chain %p (%s:%s handled:%d, last_group:%d, drained:%d, switched:%d, deadend:%d)",
+      chain, GST_DEBUG_PAD_NAME (chain->pad), handled, *last_group,
+      chain->drained, *switched, chain->deadend);
 
   *drained = chain->drained;
 
-  if (*drained)
-    g_signal_emit (parsebin, gst_parse_bin_signals[SIGNAL_DRAINED], 0, NULL);
-
   return handled;
 }
 
@@ -3244,11 +3271,18 @@ gst_parse_pad_handle_eos (GstParsePad * pad)
     drain_and_switch_chains (parsebin->parse_chain, pad, &last_group, &drained,
         &switched);
 
+    GST_LOG_OBJECT (parsebin, "drained:%d switched:%d", drained, switched);
     if (switched) {
       /* If we resulted in a group switch, expose what's needed */
       if (gst_parse_chain_is_complete (parsebin->parse_chain))
         gst_parse_bin_expose (parsebin);
     }
+
+    if (drained) {
+      GST_DEBUG_OBJECT (parsebin, "We are fully drained, emitting signal");
+      g_signal_emit (parsebin, gst_parse_bin_signals[SIGNAL_DRAINED], 0, NULL);
+    }
+
   }
   EXPOSE_UNLOCK (parsebin);
 
@@ -3485,7 +3519,6 @@ retry:
       }
     }
 
-    do_async_done (parsebin);
     return FALSE;
   }
 
@@ -3531,6 +3564,15 @@ retry:
   /* re-order pads : video, then audio, then others */
   endpads = g_list_sort (endpads, (GCompareFunc) sort_end_pads);
 
+  /* Don't expose if we're currently shutting down */
+  DYN_LOCK (parsebin);
+  if (G_UNLIKELY (parsebin->shutdown)) {
+    GST_WARNING_OBJECT (parsebin,
+        "Currently, shutting down, aborting exposing");
+    DYN_UNLOCK (parsebin);
+    return FALSE;
+  }
+
   /* Expose pads */
   for (tmp = endpads; tmp; tmp = tmp->next) {
     GstParsePad *parsepad = (GstParsePad *) tmp->data;
@@ -3570,6 +3612,8 @@ retry:
     GST_INFO_OBJECT (parsepad, "added new parsed pad");
   }
 
+  DYN_UNLOCK (parsebin);
+
   /* Unblock internal pads. The application should have connected stuff now
    * so that streaming can continue. */
   for (tmp = endpads; tmp; tmp = tmp->next) {
@@ -3597,7 +3641,6 @@ retry:
   /* Remove old groups */
   chain_remove_old_groups (parsebin->parse_chain);
 
-  do_async_done (parsebin);
   GST_DEBUG_OBJECT (parsebin, "Exposed everything");
   return TRUE;
 }
@@ -3707,6 +3750,20 @@ build_fallback_collection (GstParseChain * chain,
 
     if (p->active_stream != NULL && p->active_collection == NULL) {
       GST_DEBUG_OBJECT (p, "Adding stream to fallback collection");
+      if (G_UNLIKELY (gst_stream_get_stream_type (p->active_stream) ==
+              GST_STREAM_TYPE_UNKNOWN)) {
+        GstCaps *caps;
+        caps = get_pad_caps (GST_PAD_CAST (p));
+
+        if (caps) {
+          GstStreamType type = guess_stream_type_from_caps (caps);
+          if (type != GST_STREAM_TYPE_UNKNOWN) {
+            gst_stream_set_stream_type (p->active_stream, type);
+            gst_stream_set_caps (p->active_stream, caps);
+          }
+          gst_caps_unref (caps);
+        }
+      }
       gst_stream_collection_add_stream (collection,
           gst_object_ref (p->active_stream));
       p->in_a_fallback_collection = TRUE;
@@ -4182,32 +4239,6 @@ gst_pending_pad_free (GstPendingPad * ppad)
  * Element add/remove
  *****/
 
-static void
-do_async_start (GstParseBin * parsebin)
-{
-  GstMessage *message;
-
-  parsebin->async_pending = TRUE;
-
-  message = gst_message_new_async_start (GST_OBJECT_CAST (parsebin));
-  parent_class->handle_message (GST_BIN_CAST (parsebin), message);
-}
-
-static void
-do_async_done (GstParseBin * parsebin)
-{
-  GstMessage *message;
-
-  if (parsebin->async_pending) {
-    message =
-        gst_message_new_async_done (GST_OBJECT_CAST (parsebin),
-        GST_CLOCK_TIME_NONE);
-    parent_class->handle_message (GST_BIN_CAST (parsebin), message);
-
-    parsebin->async_pending = FALSE;
-  }
-}
-
 /* call with dyn_lock held */
 static void
 unblock_pads (GstParseBin * parsebin)
@@ -4267,8 +4298,6 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition)
       parsebin->shutdown = FALSE;
       DYN_UNLOCK (parsebin);
       parsebin->have_type = FALSE;
-      ret = GST_STATE_CHANGE_ASYNC;
-      do_async_start (parsebin);
 
 
       /* connect a signal to find out when the typefind element found
@@ -4291,20 +4320,12 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition)
       break;
   }
 
-  {
-    GstStateChangeReturn bret;
-
-    bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-    if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE))
-      goto activate_failed;
-    else if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
-      do_async_done (parsebin);
-      ret = bret;
-    }
-  }
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE))
+    goto activate_failed;
+
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-      do_async_done (parsebin);
       EXPOSE_LOCK (parsebin);
       if (parsebin->parse_chain) {
         chain_to_free = parsebin->parse_chain;
@@ -4316,6 +4337,12 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition)
         gst_parse_chain_free (chain_to_free);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
+      g_mutex_lock (&parsebin->cleanup_lock);
+      if (parsebin->cleanup_thread) {
+        g_thread_join (parsebin->cleanup_thread);
+        parsebin->cleanup_thread = NULL;
+      }
+      g_mutex_unlock (&parsebin->cleanup_lock);
     default:
       break;
   }
@@ -4335,7 +4362,6 @@ activate_failed:
   {
     GST_DEBUG_OBJECT (element,
         "element failed to change states -- activation problem?");
-    do_async_done (parsebin);
     return GST_STATE_CHANGE_FAILURE;
   }
 }
index 4789db6..3c3efa5 100644 (file)
@@ -63,6 +63,7 @@ plugin_init (GstPlugin * plugin)
   res &= gst_decode_bin_plugin_init (plugin);
   res &= gst_decodebin3_plugin_init (plugin);
   res &= gst_uri_decode_bin_plugin_init (plugin);
+  res &= gst_uri_decode_bin3_plugin_init (plugin);
   res &= gst_uri_source_bin_plugin_init (plugin);
   res &= gst_parse_bin_plugin_init (plugin);
 
index eaa0564..26e3683 100644 (file)
@@ -26,6 +26,7 @@
 gboolean gst_decode_bin_plugin_init (GstPlugin * plugin);
 gboolean gst_decodebin3_plugin_init (GstPlugin * plugin);
 gboolean gst_uri_decode_bin_plugin_init (GstPlugin * plugin);
+gboolean gst_uri_decode_bin3_plugin_init (GstPlugin * plugin);
 gboolean gst_uri_source_bin_plugin_init (GstPlugin * plugin);
 gboolean gst_parse_bin_plugin_init (GstPlugin * plugin);
 
index 8bf9b08..00becf0 100644 (file)
@@ -81,10 +81,11 @@ gst_playback_utils_get_n_common_capsfeatures (GstElementFactory * fact1,
   fact1_tmpl_caps = get_template_caps (fact1, GST_PAD_SRC);
   fact2_tmpl_caps = get_template_caps (fact2, GST_PAD_SINK);
   if (!fact1_tmpl_caps || !fact2_tmpl_caps) {
-    if (fact1_tmpl_caps) gst_caps_unref (fact1_tmpl_caps);
-    if (fact2_tmpl_caps) gst_caps_unref (fact2_tmpl_caps);
-
     GST_ERROR ("Failed to get template caps from decoder or sink");
+    if (fact1_tmpl_caps)
+      gst_caps_unref (fact1_tmpl_caps);
+    else if (fact2_tmpl_caps)
+      gst_caps_unref (fact2_tmpl_caps);
     return 0;
   }
 
@@ -131,7 +132,6 @@ gst_playback_utils_get_n_common_capsfeatures (GstElementFactory * fact1,
 
   gst_caps_unref (fact1_tmpl_caps);
   gst_caps_unref (fact2_tmpl_caps);
-  gst_caps_unref (raw_caps);
 
   return n_common_cf;
 }
index 04eeb2b..a2e9ed4 100644 (file)
@@ -4570,6 +4570,7 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
   GstAVElement *ave = NULL;
   GSequence *ave_seq = NULL;
   GSequenceIter *seq_iter;
+  gboolean created_sink = FALSE;
 
   playbin = group->playbin;
 
@@ -4635,8 +4636,6 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
     /* if it is a decoder and we don't have a fixed sink, then find out
      * the matching audio/video sink from GstAVElements list */
     for (l = ave_list; l; l = l->next) {
-      gboolean created_sink = FALSE;
-
       ave = (GstAVElement *) l->data;
 
       if (((isaudiodec && !group->audio_sink) ||
@@ -4652,6 +4651,10 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
                 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
             continue;
           } else {
+            /* The sink is ours now, don't leak the floating reference in the
+             * state-changed messages */
+            gst_object_ref_sink (*sinkp);
+
             if (!activate_sink (playbin, *sinkp, NULL)) {
               gst_object_unref (*sinkp);
               *sinkp = NULL;
@@ -4660,7 +4663,6 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
                   gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
               continue;
             }
-            gst_object_ref_sink (*sinkp);
             created_sink = TRUE;
           }
         }
@@ -4725,6 +4727,7 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
           gst_element_set_state (*sinkp, GST_STATE_NULL);
           gst_object_unref (*sinkp);
           *sinkp = NULL;
+          created_sink = FALSE;
         } else {
           g_mutex_unlock (&playbin->elements_lock);
           GST_SOURCE_GROUP_UNLOCK (group);
@@ -4799,6 +4802,12 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
     return GST_AUTOPLUG_SELECT_SKIP;
   }
 
+  /* The sink is ours now, don't leak floating references in the state-changed
+   * messages, but only do that if we didn't just create the sink above and
+   * already ref_sink'd it there */
+  if (!created_sink)
+    gst_object_ref_sink (*sinkp);
+
   element = *sinkp;
 
   if (!activate_sink (playbin, element, NULL)) {
@@ -4820,13 +4829,11 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
     return GST_AUTOPLUG_SELECT_SKIP;
   }
 
-  /* remember the sink in the group now, the element is floating, we take
-   * ownership now
+  /* remember the sink in the group now
    *
    * store the sink in the group, we will configure it later when we
    * reconfigure the sink */
   GST_DEBUG_OBJECT (playbin, "remember sink");
-  gst_object_ref_sink (element);
   GST_SOURCE_GROUP_UNLOCK (group);
 
   /* tell decodebin to expose the pad because we are going to use this
index f3f5efe..027766b 100644 (file)
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
-
 /**
  * SECTION:element-playbin3
  * @title: playbin3
  * video sinks implement. See the documentation there for more details.
  *
  * ## Specifying which CD/DVD device to use
- * The device to use for CDs/DVDs needs to be set on the source element
- * playbin3 creates before it is opened. The most generic way of doing this
- * is to connect to playbin3's "source-setup" (or "notify::source") signal,
- * which will be emitted by playbin3 when it has created the source element
- * for a particular URI. In the signal callback you can check if the source
- * element has a "device" property and set it appropriately. In some cases
- * the device can also be set as part of the URI, but it depends on the
- * elements involved if this will work or not. For example, for DVD menu
- * playback, the following syntax might work (if the resindvd plugin is used):
- * dvd://[/path/to/device]
+ *
+ * The device to use for CDs/DVDs needs to be set on the source element playbin3
+ * creates before it is opened. The most generic way of doing this is to connect
+ * to playbin3's "source-setup" signal, which will be emitted by playbin3 when
+ * it has created the source element for a particular URI. In the signal
+ * callback you can check if the source element has a "device" property and set
+ * it appropriately. In some cases the device can also be set as part of the
+ * URI, but it depends on the elements involved if this will work or not. For
+ * example, for DVD menu playback, the following syntax might work (if the
+ * resindvd plugin is used): dvd://[/path/to/device]
  *
  * ## Handling redirects
  *
  *
  */
 
-/* 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,30 +238,34 @@ 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 */
 
-  gboolean has_always_ok;       /* stream combiner's sink pads have the "always-ok" property */
+  gboolean is_concat;           /* The stream combiner is the 'concat' element */
 };
 
 #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
@@ -286,9 +285,19 @@ 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,
     gboolean isaudioelement);
+#endif
 
 /* The GstAudioVideoElement structure holding the audio/video decoder
  * and the audio/video sink factories together with field indicating
@@ -300,6 +309,14 @@ 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 */
+  GstStreamType stream_type;    /* stream type of the controlled pad */
+  gulong event_probe_id;
+};
+
 /* a structure to hold the objects for decoding a uri and the subtitle uri
  */
 struct _GstSourceGroup
@@ -311,17 +328,30 @@ struct _GstSourceGroup
   gboolean valid;               /* the group has valid info to start playback */
   gboolean active;              /* the group is active */
 
+  gboolean playing;             /* the group is currently playing
+                                 * (outputted on the sinks) */
+
   /* properties */
   gchar *uri;
   gchar *suburi;
-  GValueArray *streaminfo;
-  GstElement *source;
 
+  /* The currently outputted group_id */
+  guint group_id;
 
-  /* 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;
+
+  /* Bit-wise set of stream types for which pads are present */
+  GstStreamType present_stream_types;
+
+  /* TRUE if a 'about-to-finish' needs to be posted once we have
+   * got source pads for all requested stream types
+   *
+   * FIXME : Move this logic to uridecodebin3 later */
+  gboolean pending_about_to_finish;
+
+  /* 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,28 +361,28 @@ 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 notify_source_id;
+#if 0                           /* AUTOPLUG DISABLED */
   gulong autoplug_factories_id;
   gulong autoplug_select_id;
   gulong autoplug_continue_id;
   gulong autoplug_query_id;
+#endif
 
-  /* subtitle uri signals */
-  gulong sub_pad_added_id;
-  gulong sub_pad_removed_id;
-  gulong sub_autoplug_continue_id;
-  gulong sub_autoplug_query_id;
+  gboolean stream_changed_pending;
 
-  gulong block_id;
+  /* Active stream collection */
+  GstStreamCollection *collection;
 
-  GMutex stream_changed_pending_lock;
-  gboolean stream_changed_pending;
 
   /* buffering message stored for after switching */
   GstMessage *pending_buffering_msg;
@@ -367,20 +397,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:
@@ -398,25 +428,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_no_more_pads_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) */
@@ -438,8 +463,9 @@ struct _GstPlayBin3
   /* our play sink */
   GstPlaySink *playsink;
 
-  /* the last activated source */
-  GstElement *source;
+  /* Task for (de)activating groups, protected by the activation lock */
+  GstTask *activation_task;
+  GRecMutex activation_lock;
 
   /* lock protecting dynamic adding/removing */
   GMutex dyn_lock;
@@ -476,19 +502,7 @@ struct _GstPlayBin3
   GSequence *aelements;         /* a list of GstAVElements for audio stream */
   GSequence *velements;         /* a list of GstAVElements for video stream */
 
-  struct
-  {
-    gboolean valid;
-    GstFormat format;
-    gint64 duration;
-  } duration[5];                /* cached durations */
-
   guint64 ring_buffer_max_size; /* 0 means disabled */
-
-  GList *contexts;
-
-  /* Active stream collection */
-  GstStreamCollection *collection;
 };
 
 struct _GstPlayBin3Class
@@ -506,14 +520,12 @@ struct _GstPlayBin3Class
 /* props */
 #define DEFAULT_URI               NULL
 #define DEFAULT_SUBURI            NULL
-#define DEFAULT_SOURCE            NULL
 #define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
                                   GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
                                   GST_PLAY_FLAG_SOFT_COLORBALANCE | GST_PLAY_FLAG_BUFFERING
 #define DEFAULT_CURRENT_VIDEO     -1
 #define DEFAULT_CURRENT_AUDIO     -1
 #define DEFAULT_CURRENT_TEXT      -1
-#define DEFAULT_AUTO_SELECT_STREAMS TRUE
 #define DEFAULT_SUBTITLE_ENCODING NULL
 #define DEFAULT_AUDIO_SINK        NULL
 #define DEFAULT_VIDEO_SINK        NULL
@@ -535,7 +547,6 @@ enum
   PROP_CURRENT_URI,
   PROP_SUBURI,
   PROP_CURRENT_SUBURI,
-  PROP_SOURCE,
   PROP_FLAGS,
   PROP_SUBTITLE_ENCODING,
   PROP_AUDIO_SINK,
@@ -571,8 +582,10 @@ enum
   LAST_SIGNAL
 };
 
+#if 0                           /* AUTOPLUG DISABLED */
 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
+#endif
 
 static void gst_play_bin3_class_init (GstPlayBin3Class * klass);
 static void gst_play_bin3_init (GstPlayBin3 * playbin);
@@ -589,37 +602,35 @@ static GstStateChangeReturn gst_play_bin3_change_state (GstElement * element,
 static void gst_play_bin3_handle_message (GstBin * bin, GstMessage * message);
 static void gst_play_bin3_deep_element_added (GstBin * playbin,
     GstBin * sub_bin, GstElement * child);
-static gboolean gst_play_bin3_query (GstElement * element, GstQuery * query);
-static void gst_play_bin3_set_context (GstElement * element,
-    GstContext * context);
 static gboolean gst_play_bin3_send_event (GstElement * element,
     GstEvent * event);
 
 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_cb (GstElement * decodebin, GstPlayBin3 * playbin);
+static void gst_play_bin3_check_group_status (GstPlayBin3 * playbin);
+static void emit_about_to_finish (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);
@@ -706,7 +717,7 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass)
       g_param_spec_string ("uri", "URI", "URI of the media to play",
           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
-   /**
+  /**
    * GstPlayBin3:current-uri
    *
    * The currently playing uri.
@@ -736,10 +747,6 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass)
           "The currently playing URI of a subtitle",
           NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
-  g_object_class_install_property (gobject_klass, PROP_SOURCE,
-      g_param_spec_object ("source", "Source", "Source element",
-          GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
   /**
    * GstPlayBin3:flags
    *
@@ -801,32 +808,34 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass)
   /**
    * GstPlayBin3:video-stream-combiner
    *
-   * Get or set the current video stream combiner. By default, an input-selector
-   * is created and deleted as-needed.
+   * Get or set the current video stream combiner. By default, no
+   * element is used and the selected stream is used directly.
    */
   g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
       g_param_spec_object ("video-stream-combiner", "Video stream combiner",
-          "Current video stream combiner (NULL = input-selector)",
+          "Current video stream combiner (default: none)",
           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   /**
    * GstPlayBin3:audio-stream-combiner
    *
-   * Get or set the current audio stream combiner. By default, an input-selector
-   * is created and deleted as-needed.
+   * Get or set the current audio stream combiner. By default, no
+   * element is used and the selected stream is used directly.
    */
   g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
       g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
-          "Current audio stream combiner (NULL = input-selector)",
+          "Current audio stream combiner (default: none))",
           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   /**
    * GstPlayBin3:text-stream-combiner
    *
-   * Get or set the current text stream combiner. By default, an input-selector
-   * is created and deleted as-needed.
+   * Get or set the current text stream combiner. By default, no
+   * element is used and the selected stream is used directly.
    */
   g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
       g_param_spec_object ("text-stream-combiner", "Text stream combiner",
-          "Current text stream combiner (NULL = input-selector)",
+          "Current text stream combiner (default: none)",
           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
@@ -1037,8 +1046,6 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass)
 
   gstelement_klass->change_state =
       GST_DEBUG_FUNCPTR (gst_play_bin3_change_state);
-  gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin3_query);
-  gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin3_set_context);
   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin3_send_event);
 
   gstbin_klass->handle_message =
@@ -1089,35 +1096,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");
@@ -1128,18 +1138,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) {
@@ -1162,40 +1168,24 @@ 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;
-        }
-      }
-    }
-  }
-}
+#ifndef GST_DISABLE_GST_DEBUG
+#define debug_groups(playbin) G_STMT_START {   \
+    guint i;                                   \
+                                               \
+    for (i = 0; i < 2; i++) {                          \
+      GstSourceGroup *group = &playbin->groups[i];     \
+                                                       \
+      GST_DEBUG ("GstSourceGroup #%d (%s)", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused"); \
+      GST_DEBUG ("  valid:%d , active:%d , playing:%d", group->valid, group->active, group->playing); \
+      GST_DEBUG ("  uri:%s", group->uri);                              \
+      GST_DEBUG ("  suburi:%s", group->suburi);                                \
+      GST_DEBUG ("  group_id:%d", group->group_id);                    \
+      GST_DEBUG ("  pending_about_to_finish:%d", group->pending_about_to_finish); \
+    }                                                                  \
+  } G_STMT_END
+#else
+#define debug_groups(p) {}
+#endif
 
 static void
 init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
@@ -1203,7 +1193,7 @@ init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
   g_mutex_init (&group->lock);
 
   group->stream_changed_pending = FALSE;
-  g_mutex_init (&group->stream_changed_pending_lock);
+  group->group_id = GST_GROUP_ID_INVALID;
 
   group->playbin = playbin;
 }
@@ -1216,12 +1206,13 @@ free_group (GstPlayBin3 * playbin, GstSourceGroup * group)
 
   g_mutex_clear (&group->lock);
   group->stream_changed_pending = FALSE;
-  g_mutex_clear (&group->stream_changed_pending_lock);
 
   if (group->pending_buffering_msg)
     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);
@@ -1246,6 +1237,7 @@ colorbalance_value_changed_cb (GstColorBalance * balance,
   gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
 }
 
+#if 0                           /* AUTOPLUG DISABLED */
 static gint
 compare_factories_func (gconstpointer p1, gconstpointer p2)
 {
@@ -1320,6 +1312,7 @@ gst_play_bin3_update_elements_list (GstPlayBin3 * playbin)
 
   playbin->elements_cookie = cookie;
 }
+#endif
 
 static void
 gst_play_bin3_init (GstPlayBin3 * playbin)
@@ -1341,6 +1334,8 @@ gst_play_bin3_init (GstPlayBin3 * playbin)
   /* first filter out the interesting element factories */
   g_mutex_init (&playbin->elements_lock);
 
+  g_rec_mutex_init (&playbin->activation_lock);
+
   /* add sink */
   playbin->playsink =
       g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
@@ -1383,9 +1378,6 @@ gst_play_bin3_finalize (GObject * object)
   for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
     g_ptr_array_free (playbin->channels[i], TRUE);
 
-  if (playbin->source)
-    gst_object_unref (playbin->source);
-
   /* Setting states to NULL is safe here because playsink
    * will already be gone and none of these sinks will be
    * a child of playsink
@@ -1420,9 +1412,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);
 
@@ -1432,11 +1421,7 @@ gst_play_bin3_finalize (GObject * object)
   if (playbin->velements)
     g_sequence_free (playbin->velements);
 
-  if (playbin->collection)
-    gst_object_unref (playbin->collection);
-
-  g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref);
-
+  g_rec_mutex_clear (&playbin->activation_lock);
   g_rec_mutex_clear (&playbin->lock);
   g_mutex_clear (&playbin->dyn_lock);
   g_mutex_clear (&playbin->elements_lock);
@@ -1567,43 +1552,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", &current, 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)
 {
@@ -1647,10 +1595,10 @@ gst_play_bin3_set_current_stream (GstPlayBin3 * playbin,
   GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
       stream_type_names[stream_type], *current_value, stream);
 
-  if (combine->combiner == NULL) {
+  if (combine->combiner == NULL || combine->is_concat) {
     /* 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;
   }
@@ -1740,35 +1688,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,14 +1729,7 @@ gst_play_bin3_set_stream_combiner (GstPlayBin3 * playbin, GstElement ** elem,
 static void
 gst_play_bin3_set_encoding (GstPlayBin3 * playbin, const gchar * encoding)
 {
-  GstElement *elem;
-
   GST_PLAY_BIN3_LOCK (playbin);
-
-  /* set subtitles on decodebin. */
-  if ((elem = playbin->decodebin))
-    g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
-
   gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
   GST_PLAY_BIN3_UNLOCK (playbin);
 }
@@ -1839,8 +1751,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);
@@ -1914,8 +1826,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);
@@ -1968,7 +1880,10 @@ gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin,
   GstElement *combiner;
 
   GST_PLAY_BIN3_LOCK (playbin);
-  if ((combiner = playbin->combiner[stream_type].combiner))
+  /* The special concat element should never be returned */
+  if (playbin->combiner[stream_type].is_concat)
+    combiner = NULL;
+  else if ((combiner = playbin->combiner[stream_type].combiner))
     gst_object_ref (combiner);
   else if ((combiner = *elem))
     gst_object_ref (combiner);
@@ -2024,13 +1939,6 @@ gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value,
       GST_PLAY_BIN3_UNLOCK (playbin);
       break;
     }
-    case PROP_SOURCE:
-    {
-      GST_OBJECT_LOCK (playbin);
-      g_value_set_object (value, playbin->source);
-      GST_OBJECT_UNLOCK (playbin);
-      break;
-    }
     case PROP_FLAGS:
       g_value_set_flags (value, gst_play_bin3_get_flags (playbin));
       break;
@@ -2143,108 +2051,6 @@ gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
-static void
-gst_play_bin3_update_cached_duration_from_query (GstPlayBin3 * playbin,
-    gboolean valid, GstQuery * query)
-{
-  GstFormat fmt;
-  gint64 duration;
-  gint i;
-
-  GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
-  gst_query_parse_duration (query, &fmt, &duration);
-
-  for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
-    if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
-      playbin->duration[i].valid = valid;
-      playbin->duration[i].format = fmt;
-      playbin->duration[i].duration = valid ? duration : -1;
-      break;
-    }
-  }
-}
-
-static void
-gst_play_bin3_update_cached_duration (GstPlayBin3 * playbin)
-{
-  const GstFormat formats[] =
-      { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
-  gboolean ret;
-  GstQuery *query;
-  gint i;
-
-  GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
-  for (i = 0; i < G_N_ELEMENTS (formats); i++) {
-    query = gst_query_new_duration (formats[i]);
-    ret =
-        GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
-        query);
-    gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
-    gst_query_unref (query);
-  }
-}
-
-static gboolean
-gst_play_bin3_query (GstElement * element, GstQuery * query)
-{
-  GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
-  gboolean ret;
-
-  /* During a group switch we shouldn't allow duration queries
-   * because it's not clear if the old or new group's duration
-   * is returned and if the sinks are already playing new data
-   * or old data. See bug #585969
-   *
-   * While we're at it, also don't do any other queries during
-   * a group switch or any other event that causes topology changes
-   * by taking the playbin lock in any case.
-   */
-  GST_PLAY_BIN3_LOCK (playbin);
-
-  if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
-    GstSourceGroup *group = playbin->curr_group;
-    gboolean pending;
-
-    GST_SOURCE_GROUP_LOCK (group);
-
-    pending = group->pending || group->stream_changed_pending;
-
-    if (pending) {
-      GstFormat fmt;
-      gint i;
-
-      ret = FALSE;
-      gst_query_parse_duration (query, &fmt, NULL);
-      for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
-        if (fmt == playbin->duration[i].format) {
-          ret = playbin->duration[i].valid;
-          gst_query_set_duration (query, fmt,
-              (ret ? playbin->duration[i].duration : -1));
-          break;
-        }
-      }
-      /* if nothing cached yet, we might as well request duration,
-       * such as during initial startup */
-      if (ret) {
-        GST_DEBUG_OBJECT (playbin,
-            "Taking cached duration because of pending group switch: %d", ret);
-        GST_SOURCE_GROUP_UNLOCK (group);
-        GST_PLAY_BIN3_UNLOCK (playbin);
-        return ret;
-      }
-    }
-    GST_SOURCE_GROUP_UNLOCK (group);
-  }
-
-  ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
-
-  if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
-    gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
-  GST_PLAY_BIN3_UNLOCK (playbin);
-
-  return ret;
-}
-
 static gint
 get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine,
     GList * full_list)
@@ -2252,7 +2058,7 @@ get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine,
   gint i;
   GList *tmp;
 
-  for (i = 0; combine->streams->len; i++) {
+  for (i = 0; i < combine->streams->len; i++) {
     GstStream *stream = (GstStream *) g_ptr_array_index (combine->streams, i);
     const gchar *sid = gst_stream_get_stream_id (stream);
     for (tmp = full_list; tmp; tmp = tmp->next) {
@@ -2268,17 +2074,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;
@@ -2301,7 +2106,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;
@@ -2315,13 +2121,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);
@@ -2329,7 +2143,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);
@@ -2337,7 +2153,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);
@@ -2356,6 +2174,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)
 {
@@ -2363,6 +2257,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,
@@ -2370,14 +2265,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);
+
+    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 decodebin, so it works even
-     * if decodebin didn't add any pads yet */
-    res = gst_element_send_event (playbin->decodebin, event);
+    /* 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;
@@ -2401,7 +2314,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;
@@ -2409,7 +2322,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;
@@ -2420,6 +2336,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 */
@@ -2430,6 +2349,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 */
@@ -2439,7 +2360,7 @@ do_stream_selection (GstPlayBin3 * playbin)
           playbin->audio_stream_combiner != NULL);
       nb_audio++;
     } else if (stream_type & GST_STREAM_TYPE_VIDEO) {
-      pb_stream_type = PLAYBIN_STREAM_AUDIO;
+      pb_stream_type = PLAYBIN_STREAM_VIDEO;
       select_this =
           (nb_video == playbin->current_video ||
           (playbin->current_video == -1 && nb_video == 0) ||
@@ -2470,70 +2391,106 @@ 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);
 }
 
-/* mime types we are not handling on purpose right now, don't post a
- * missing-plugin message for these */
-static const gchar *blacklisted_mimes[] = {
-  NULL
-};
+/* 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
 gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
 {
   GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
 
-  if (gst_is_missing_plugin_message (msg)) {
-    gchar *detail;
-    guint i;
-
-    detail = gst_missing_plugin_message_get_installer_detail (msg);
-    for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
-      if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
-        GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
-        gst_message_unref (msg);
-        g_free (detail);
-        return;
-      }
-    }
-    g_free (detail);
-  } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
-    GstSourceGroup *new_group = playbin->curr_group;
-    GstMessage *buffering_msg = NULL;
+  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
+    GstSourceGroup *group = NULL, *other_group = NULL;
+    gboolean changed = FALSE;
+    guint group_id;
+    GstMessage *buffering_msg;
 
-    GST_SOURCE_GROUP_LOCK (new_group);
-    new_group->stream_changed_pending = FALSE;
-    if (new_group->pending_buffering_msg) {
-      buffering_msg = new_group->pending_buffering_msg;
-      new_group->pending_buffering_msg = NULL;
+    if (!gst_message_parse_group_id (msg, &group_id)) {
+      GST_ERROR_OBJECT (bin,
+          "Could not get group_id from STREAM_START message !");
+      goto beach;
     }
-    GST_SOURCE_GROUP_UNLOCK (new_group);
-
-    GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group);
+    GST_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id);
 
-    if (buffering_msg) {
-      GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %"
-          GST_PTR_FORMAT, buffering_msg);
-      GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
+    /* Figure out to which group this group_id corresponds */
+    GST_PLAY_BIN3_LOCK (playbin);
+    if (playbin->groups[0].group_id == group_id) {
+      group = &playbin->groups[0];
+      other_group = &playbin->groups[1];
+    } else if (playbin->groups[1].group_id == group_id) {
+      group = &playbin->groups[1];
+      other_group = &playbin->groups[0];
+    }
+    if (group == NULL) {
+      GST_ERROR_OBJECT (bin, "group_id %u is not provided by any group !",
+          group_id);
+      GST_PLAY_BIN3_UNLOCK (playbin);
+      goto beach;
     }
 
-  } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
-    GstSourceGroup *group = playbin->curr_group;
-    gboolean pending;
+    debug_groups (playbin);
 
-    /* drop buffering messages from child queues while we are switching
-     * groups (because the application set a new uri in about-to-finish)
-     * if the playsink queue still has buffers to play */
+    /* Do the switch now ! */
+    playbin->curr_group = group;
+    playbin->next_group = other_group;
 
     GST_SOURCE_GROUP_LOCK (group);
-    pending = group->stream_changed_pending;
+    if (group->playing == FALSE)
+      changed = TRUE;
+    group->playing = TRUE;
+    buffering_msg = group->pending_buffering_msg;
+    group->pending_buffering_msg = NULL;
+    GST_SOURCE_GROUP_UNLOCK (group);
+
+    GST_SOURCE_GROUP_LOCK (other_group);
+    other_group->playing = FALSE;
+    GST_SOURCE_GROUP_UNLOCK (other_group);
+
+    debug_groups (playbin);
+    GST_PLAY_BIN3_UNLOCK (playbin);
+    if (changed)
+      gst_play_bin3_check_group_status (playbin);
+    else
+      GST_DEBUG_OBJECT (bin, "Groups didn't changed");
+    /* If there was a pending buffering message to send, do it now */
+    if (buffering_msg)
+      GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
+  } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
+    GstSourceGroup *group;
 
-    if (pending) {
+    /* Only post buffering messages for group which is currently playing */
+    group = find_source_group_owner (playbin, msg->src);
+    GST_SOURCE_GROUP_LOCK (group);
+    if (!group->playing) {
       GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group "
           "%p %" GST_PTR_FORMAT, group, msg);
       gst_message_replace (&group->pending_buffering_msg, msg);
@@ -2543,60 +2500,36 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
     GST_SOURCE_GROUP_UNLOCK (group);
   } else 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);
-    gboolean pstate = playbin->do_stream_selections;
-
-    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);
-        gst_object_unref (stream);
-      }
-      if (pstate)
-        playbin->do_stream_selections = FALSE;
-      do_stream_selection (playbin);
-      if (pstate)
-        playbin->do_stream_selections = TRUE;
-      GST_PLAY_BIN3_UNLOCK (playbin);
-
-      gst_object_unref (collection);
-    }
-  }
+  }
 
+beach:
   if (msg)
     GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
 }
@@ -2614,11 +2547,47 @@ gst_play_bin3_deep_element_added (GstBin * playbin, GstBin * sub_bin,
   GST_BIN_CLASS (parent_class)->deep_element_added (playbin, sub_bin, child);
 }
 
+/* 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", &current, NULL);
+      gst_object_unref (combiner);
+
+      if (pad == current) {
+        gst_object_unref (current);
+        ret = i;
+        break;
+      }
+
+      if (current)
+        gst_object_unref (current);
+    }
+  }
+
+  return ret;
+}
+
 static void
 combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
     GstPlayBin3 * playbin)
 {
-  const gchar *property;
   GstSourceCombine *combine = NULL;
   GPtrArray *channels = NULL;
   int i;
@@ -2641,7 +2610,6 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
   switch (combine->type) {
     case GST_PLAY_SINK_TYPE_VIDEO:
     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
-      property = "current-video";
       playbin->current_video = get_current_stream_number (playbin,
           combine, channels);
 
@@ -2650,12 +2618,10 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
         GST_PLAY_BIN3_UNLOCK (playbin);
         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
             "playsink-custom-video-flush-finish");
-        goto notify;
       }
       break;
     case GST_PLAY_SINK_TYPE_AUDIO:
     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
-      property = "current-audio";
       playbin->current_audio = get_current_stream_number (playbin,
           combine, channels);
 
@@ -2664,11 +2630,9 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
         GST_PLAY_BIN3_UNLOCK (playbin);
         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
             "playsink-custom-audio-flush-finish");
-        goto notify;
       }
       break;
     case GST_PLAY_SINK_TYPE_TEXT:
-      property = "current-text";
       playbin->current_text = get_current_stream_number (playbin,
           combine, channels);
 
@@ -2677,17 +2641,12 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
         GST_PLAY_BIN3_UNLOCK (playbin);
         gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
             "playsink-custom-subtitle-flush-finish");
-        goto notify;
       }
       break;
     default:
-      property = NULL;
+      break;
   }
   GST_PLAY_BIN3_UNLOCK (playbin);
-
-notify:
-  if (property)
-    g_object_notify (G_OBJECT (playbin), property);
 }
 
 static GstCaps *
@@ -2736,11 +2695,41 @@ update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps)
   return out_caps;
 }
 
+static void
+emit_about_to_finish (GstPlayBin3 * playbin)
+{
+  GST_DEBUG_OBJECT (playbin, "Emitting about-to-finish");
+
+  /* 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);
+
+  debug_groups (playbin);
+
+  /* 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);
+}
+
+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)) {
@@ -2764,6 +2753,21 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
       }
       break;
     }
+    case GST_EVENT_STREAM_START:
+    {
+      guint group_id;
+      if (gst_event_parse_group_id (event, &group_id)) {
+        GST_LOG_OBJECT (pad, "STREAM_START group_id:%u", group_id);
+        if (group->group_id == GST_GROUP_ID_INVALID)
+          group->group_id = group_id;
+        else if (group->group_id != group_id) {
+          GST_DEBUG_OBJECT (pad, "group_id changing from %u to %u",
+              group->group_id, group_id);
+          group->group_id = group_id;
+        }
+      }
+      break;
+    }
     default:
       break;
   }
@@ -2771,58 +2775,82 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
   return ret;
 }
 
-/* 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)
+control_source_pad (GstSourceGroup * group, GstPad * pad,
+    GstStreamType stream_type)
 {
-  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;
+  SourcePad *sourcepad = g_slice_new0 (SourcePad);
 
-  GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
+  sourcepad->pad = pad;
+  sourcepad->event_probe_id =
+      gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+      _decodebin_event_probe, group, NULL);
+  sourcepad->stream_type = stream_type;
+  group->source_pads = g_list_append (group->source_pads, sourcepad);
+}
 
-  pad_name = gst_object_get_name (GST_OBJECT (pad));
+static void
+remove_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
+{
+  gint n;
 
-  GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
-      GST_DEBUG_PAD_NAME (pad));
+  if (combine->combiner == NULL) {
+    GST_DEBUG_OBJECT (playbin, "No combiner element to remove");
+    return;
+  }
 
-  /* 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;
+  /* 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 (g_str_has_prefix (pad_name, "audio")) {
-    stream_type = GST_STREAM_TYPE_AUDIO;
-    pb_stream_type = PLAYBIN_STREAM_AUDIO;
+  else if (combine->stream_type == GST_STREAM_TYPE_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;
+  else if (combine->stream_type == GST_STREAM_TYPE_TEXT)
     custom_combiner = playbin->text_stream_combiner;
-  }
 
-  g_free (pad_name);
+  combine->combiner = custom_combiner;
 
-  /* no stream type found for the media type, don't bother linking it to a
-   * combiner. This will leave the pad unlinked and thus ignored. */
-  if (pb_stream_type < 0) {
-    GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
-    goto unknown_type;
+  if (!combine->combiner) {
+    gchar *concat_name;
+    GST_DEBUG_OBJECT (playbin,
+        "No custom combiner requested, using 'concat' element");
+    concat_name = g_strdup_printf ("%s-concat", combine->media_type);
+    combine->combiner = gst_element_factory_make ("concat", concat_name);
+    g_object_set (combine->combiner, "adjust-base", FALSE, NULL);
+    g_free (concat_name);
+    combine->is_concat = TRUE;
   }
 
-  combine = &playbin->combiner[pb_stream_type];
+  combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
 
-  if (custom_combiner && combine->combiner == NULL) {
-    combine->combiner = custom_combiner;
-    /* find out which properties the stream combiner supports */
+  /* We only want to use 'active-pad' if it's a regular combiner that
+   * will consume all streams, and not concat (which is just used for
+   * gapless) */
+  if (!combine->is_concat) {
     combine->has_active_pad =
         g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
         "active-pad") != NULL;
@@ -2830,222 +2858,249 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
     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);
   }
 
-  GST_PLAY_BIN3_SHUTDOWN_UNLOCK (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);
+}
 
-  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);
-    }
+static gboolean
+combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
+    GstPad * srcpad)
+{
+  GstPadLinkReturn res;
 
-    /* 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);
-  }
+  GST_DEBUG_OBJECT (playbin, "srcpad %" GST_PTR_FORMAT, srcpad);
 
-  /* 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));
+    GstPad *sinkpad =
+        gst_element_get_request_pad (combine->combiner, "sink_%u");
 
-      /* 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;
+    if (sinkpad == NULL)
+      goto request_pad_failed;
 
-      /* store the combiner for the pad */
-      g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine);
+    GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT,
+        sinkpad);
 
-      /* store the pad in the array */
-      GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
-      g_ptr_array_add (combine->channels, sinkpad);
+    /* 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 (pad, sinkpad);
-      if (GST_PAD_LINK_FAILED (res))
-        goto link_failed;
+    res = gst_pad_link (srcpad, sinkpad);
+    if (GST_PAD_LINK_FAILED (res))
+      goto failed_combiner_link;
 
-      /* store combiner pad so we can release it */
-      g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
+    GST_DEBUG_OBJECT (playbin,
+        "linked pad %" GST_PTR_FORMAT " to combiner %" GST_PTR_FORMAT, srcpad,
+        combine->combiner);
 
-      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;
+    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;
 
-    /* store the combiner for the pad */
-    g_object_set_data (G_OBJECT (pad), "playbin.combine", combine);
-  }
+    GST_DEBUG_OBJECT (playbin, "Linking new pad straight to playsink");
+    res = gst_pad_link (srcpad, combine->sinkpad);
 
-  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->selected_stream_types & ~playbin->active_stream_types &
-          (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO))
-      == 0) {
-    no_more_pads_cb (decodebin, playbin);
-  } else {
-    GST_LOG_OBJECT (playbin, "Active stream types 0x%x, want 0x%x. Waiting",
-        playbin->active_stream_types, playbin->selected_stream_types);
+    if (res != GST_PAD_LINK_OK)
+      goto failed_sinkpad_link;
   }
 
-  return;
-
-  /* ERRORS */
-unknown_type:
-  GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
-  return;
+  return TRUE;
 
-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;
-  }
+  /* 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;
-shutdown:
-  {
-    GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
-    /* not going to done as we didn't request the caps */
-    return;
+  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);
+    }
   }
 }
 
-/* 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. */
+/* Call after pad was unlinked from (potential) combiner */
 static void
-pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
+release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
 {
-  GstPad *peer;
-  GstElement *combiner;
-  GstSourceCombine *combine;
-  gulong event_probe_handler;
-  GstStreamType stream_type = GST_STREAM_TYPE_UNKNOWN;
-  gchar *pad_name;
+  SourcePad *sourcepad;
+  GList *tmp;
+  GstStreamType alltype = 0;
 
-  GST_DEBUG_OBJECT (playbin,
-      "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
+  sourcepad = find_source_pad (group, pad);
+  if (!sourcepad) {
+    GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?");
+    return;
+  }
 
-  GST_PLAY_BIN3_LOCK (playbin);
+  if (sourcepad->event_probe_id) {
+    gst_pad_remove_probe (pad, sourcepad->event_probe_id);
+    sourcepad->event_probe_id = 0;
+  }
 
-  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);
+  /* 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);
+
+  /* Update present stream types */
+  for (tmp = group->source_pads; tmp; tmp = tmp->next) {
+    SourcePad *cand = (SourcePad *) tmp->data;
+    alltype |= cand->stream_type;
   }
+  group->present_stream_types = alltype;
+}
+
+/* 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 * uridecodebin, GstPad * pad, GstSourceGroup * group)
+{
+  GstSourceCombine *combine = NULL;
+  gint pb_stream_type = -1;
+  gchar *pad_name;
+  GstPlayBin3 *playbin = group->playbin;
+
+  GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
 
   pad_name = gst_object_get_name (GST_OBJECT (pad));
 
+  GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
+      GST_DEBUG_PAD_NAME (pad));
+
+  /* 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;
   } else if (g_str_has_prefix (pad_name, "audio")) {
-    stream_type = GST_STREAM_TYPE_AUDIO;
+    pb_stream_type = PLAYBIN_STREAM_AUDIO;
   } else if (g_str_has_prefix (pad_name, "text")) {
-    stream_type = GST_STREAM_TYPE_TEXT;
+    pb_stream_type = PLAYBIN_STREAM_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;
+  /* no stream type found for the media type, don't bother linking it to a
+   * combiner. This will leave the pad unlinked and thus ignored. */
+  if (pb_stream_type < 0) {
+    GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+    goto unknown_type;
   }
 
-  /* 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;
-    }
-  }
+  combine = &playbin->combiner[pb_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);
+  combiner_control_pad (playbin, combine, pad);
 
-  gst_object_unref (combiner);
-exit:
-  GST_PLAY_BIN3_UNLOCK (playbin);
+  control_source_pad (group, pad, combine->stream_type);
 
-  if ((playbin->selected_stream_types & ~playbin->active_stream_types &
-          (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO))
-      == 0) {
-    no_more_pads_cb (decodebin, playbin);
+  /* Update present stream_types and check whether we should post a pending about-to-finish */
+  group->present_stream_types |= combine->stream_type;
+
+  if (group->playing && group->pending_about_to_finish
+      && group->present_stream_types == group->selected_stream_types) {
+    group->pending_about_to_finish = FALSE;
+    emit_about_to_finish (playbin);
   }
 
+  GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+
   return;
 
   /* ERRORS */
-not_linked:
+unknown_type:
+  GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
+  return;
+
+shutdown:
   {
-    GST_DEBUG_OBJECT (playbin, "pad not linked");
-    goto exit;
+    GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
+    /* not going to done as we didn't request the caps */
+    return;
   }
 }
 
+/* called when a pad is removed from the decodebin. We unlink the pad from
+ * the combiner. */
+static void
+pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
+{
+  GstSourceCombine *combine;
+  GstPlayBin3 *playbin = group->playbin;
+
+  GST_DEBUG_OBJECT (playbin,
+      "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
+
+  GST_PLAY_BIN3_LOCK (playbin);
+
+  /* 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;
+
+  combiner_release_pad (playbin, combine, pad);
+  release_source_pad (playbin, group, pad);
+
+  GST_PLAY_BIN3_UNLOCK (playbin);
+}
+
 
 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;
@@ -3063,190 +3118,134 @@ 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_cb (GstElement * decodebin, 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];
-
-    /* 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);
-      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));
-      }
-    }
-  }
-  GST_PLAY_BIN3_UNLOCK (playbin);
-
-  GST_SOURCE_GROUP_LOCK (group);
-  GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
-      group->pending - 1);
-
-  if (group->pending > 0)
-    group->pending--;
-
-  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];
-
+    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;
+
+    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_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_LOG_OBJECT (playbin, "Unlinking from sink");
+        if (combine->sinkpad)
+          gst_pad_unlink (combine->srcpad, combine->sinkpad);
+        gst_object_unref (combine->srcpad);
+        combine->srcpad = NULL;
       }
-    }
-    GST_PLAY_BIN3_UNLOCK (playbin);
-    gst_play_sink_reconfigure (playbin->playsink);
-  }
 
-  GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+      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;
+      }
 
-  if (configure) {
-    do_async_done (playbin);
-  }
+      /* 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);
 
-  return;
+      /* If we are shutting down, do *not* add more combiners */
+      if (g_atomic_int_get (&playbin->shutdown))
+        continue;
 
-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];
+      g_assert (combine->sinkpad == NULL);
 
-      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;
+      /* Request playsink sink pad */
+      combine->sinkpad =
+          gst_play_sink_request_pad (playbin->playsink, combine->type);
+      gst_object_ref (combine->sinkpad);
+      /* 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));
         }
+
       }
     }
-    GST_PLAY_BIN3_UNLOCK (playbin);
-    return;
   }
+
+  playbin->active_stream_types = playbin->selected_stream_types;
+
+  GST_PLAY_BIN3_UNLOCK (playbin);
+
+  gst_play_sink_reconfigure (playbin->playsink);
+
+  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;
 }
 
-#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);
+  GST_LOG_OBJECT (playbin, "selected_stream_types:%" STREAM_TYPES_FORMAT,
+      STREAM_TYPES_ARGS (group->selected_stream_types));
+  GST_LOG_OBJECT (playbin, "present_stream_types:%" STREAM_TYPES_FORMAT,
+      STREAM_TYPES_ARGS (group->present_stream_types));
 
-  /* 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);
+  if (group->selected_stream_types == 0
+      || (group->selected_stream_types != group->present_stream_types)) {
+    GST_LOG_OBJECT (playbin,
+        "Delaying emission of signal until this group is ready");
+    group->pending_about_to_finish = TRUE;
+  } else
+    emit_about_to_finish (playbin);
 }
-#endif
 
+#if 0                           /* AUTOPLUG DISABLED */
 /* Like gst_element_factory_can_sink_any_caps() but doesn't
  * allow ANY caps on the sinkpad template */
 static gboolean
@@ -3652,64 +3651,7 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
 
   return result;
 }
-
-static void
-gst_play_bin3_set_context (GstElement * element, GstContext * context)
-{
-  GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
-
-  /* Proxy contexts to the sinks, they might not be in playsink yet */
-  GST_PLAY_BIN3_LOCK (playbin);
-  if (playbin->audio_sink)
-    gst_element_set_context (playbin->audio_sink, context);
-  if (playbin->video_sink)
-    gst_element_set_context (playbin->video_sink, context);
-  if (playbin->text_sink)
-    gst_element_set_context (playbin->text_sink, context);
-
-  GST_SOURCE_GROUP_LOCK (playbin->curr_group);
-
-  if (playbin->curr_group->audio_sink)
-    gst_element_set_context (playbin->curr_group->audio_sink, context);
-  if (playbin->curr_group->video_sink)
-    gst_element_set_context (playbin->curr_group->video_sink, context);
-  if (playbin->curr_group->text_sink)
-    gst_element_set_context (playbin->curr_group->text_sink, context);
-
-  GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
-  GST_PLAY_BIN3_UNLOCK (playbin);
-
-  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
-}
-
-/* Pass sink messages to the application, e.g. NEED_CONTEXT messages */
-static void
-gst_play_bin3_update_context (GstPlayBin3 * playbin, GstContext * context)
-{
-  GList *l;
-  const gchar *context_type;
-
-  GST_OBJECT_LOCK (playbin);
-  context_type = gst_context_get_context_type (context);
-  for (l = playbin->contexts; l; l = l->next) {
-    GstContext *tmp = l->data;
-    const gchar *tmp_type = gst_context_get_context_type (tmp);
-
-    /* Always store newest context but never replace
-     * a persistent one by a non-persistent one */
-    if (strcmp (context_type, tmp_type) == 0 &&
-        (gst_context_is_persistent (context) ||
-            !gst_context_is_persistent (tmp))) {
-      gst_context_replace ((GstContext **) & l->data, context);
-      break;
-    }
-  }
-  /* Not found? Add */
-  if (l == NULL)
-    playbin->contexts =
-        g_list_prepend (playbin->contexts, gst_context_ref (context));
-  GST_OBJECT_UNLOCK (playbin);
-}
+#endif
 
 static GstBusSyncReply
 activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
@@ -3732,37 +3674,6 @@ activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
       gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
     else
       gst_message_unref (msg);
-  } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
-    const gchar *context_type;
-    GList *l;
-
-    gst_message_parse_context_type (msg, &context_type);
-    GST_OBJECT_LOCK (playbin);
-    for (l = playbin->contexts; l; l = l->next) {
-      GstContext *tmp = l->data;
-      const gchar *tmp_type = gst_context_get_context_type (tmp);
-
-      if (strcmp (context_type, tmp_type) == 0) {
-        gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
-        break;
-      }
-    }
-    GST_OBJECT_UNLOCK (playbin);
-
-    /* Forward if we couldn't answer the message */
-    if (l == NULL) {
-      gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
-    } else {
-      gst_message_unref (msg);
-    }
-  } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
-    GstContext *context;
-
-    gst_message_parse_have_context (msg, &context);
-    gst_play_bin3_update_context (playbin, context);
-    gst_context_unref (context);
-
-    gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
   } else {
     gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
   }
@@ -3814,6 +3725,7 @@ done:
   return ret;
 }
 
+#if 0                           /* AUTOPLUG DISABLED */
 /* autoplug-continue decides, if a pad has raw caps that can be exposed
  * directly or if further decoding is necessary. We use this to expose
  * supported subtitles directly */
@@ -4437,29 +4349,7 @@ autoplug_query_cb (GstElement * uridecodebin, GstPad * pad,
       return FALSE;
   }
 }
-
-static void
-notify_source_cb (GstElement * urisourcebin, GParamSpec * pspec,
-    GstSourceGroup * group)
-{
-  GstPlayBin3 *playbin;
-  GstElement *source;
-
-  playbin = group->playbin;
-
-  g_object_get (urisourcebin, "source", &source, NULL);
-
-  GST_OBJECT_LOCK (playbin);
-  if (playbin->source)
-    gst_object_unref (playbin->source);
-  playbin->source = source;
-  GST_OBJECT_UNLOCK (playbin);
-
-  g_object_notify (G_OBJECT (playbin), "source");
-
-  g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP],
-      0, playbin->source);
-}
+#endif
 
 /* must be called with the group lock */
 static gboolean
@@ -4468,10 +4358,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;
 }
@@ -4501,160 +4389,20 @@ 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,
+source_setup_cb (GstElement * element, GstElement * source,
     GstSourceGroup * group)
 {
+  g_signal_emit (group->playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0,
+      source);
 }
 
 /* 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_no_more_pads_id = g_signal_connect (decodebin, "no-more-pads",
-      G_CALLBACK (no_more_pads_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_no_more_pads_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_no_more_pads_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;
-  }
-}
-
-/* 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;
@@ -4721,13 +4469,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 */
@@ -4741,99 +4489,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->notify_source_id = g_signal_connect (urisrcbin, "notify::source",
-      G_CALLBACK (notify_source_cb), group);
-
-  /* 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);
-
-  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);
-
-    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);
-
-    /* we have 2 pending no-more-pads */
-    group->pending = 2;
-    group->sub_pending = TRUE;
-  } else {
-    group->sub_pending = FALSE;
-  }
+  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 (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);
-      REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_continue_id);
-      REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_query_id);
-      /* 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 */
@@ -4844,26 +4523,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;
   }
@@ -4875,6 +4554,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 */
@@ -4900,17 +4581,21 @@ 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->notify_source_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);
       REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
       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);
@@ -4919,13 +4604,10 @@ error_cleanup:
   }
 }
 
-/* unlink a group of urisrcbin from the decodebin.
- * must be called with PLAY_BIN_LOCK */
+/* 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);
 
@@ -4933,32 +4615,17 @@ 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->playing = FALSE;
+  group->group_id = GST_GROUP_ID_INVALID;
 
-    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);
+  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);
 
-      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 */
@@ -4988,30 +4655,28 @@ 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->notify_source_id);
+  if (group->uridecodebin) {
+    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);
+
+    gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
+    gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
+
+    REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
+    REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_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);
-    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);
-    REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_continue_id);
-    REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_query_id);
-
-    /* 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);
+#endif
   }
 
   GST_SOURCE_GROUP_UNLOCK (group);
 
+  GST_DEBUG_OBJECT (playbin, "Done");
+
   return TRUE;
 }
 
@@ -5019,55 +4684,36 @@ 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;
+  GstSourceGroup *new_group;
   GstStateChangeReturn state_ret;
 
-  GST_DEBUG_OBJECT (playbin, "setup sources");
+  GST_DEBUG_OBJECT (playbin, "setup next source");
+
+  debug_groups (playbin);
 
   /* see if there is a next group */
   GST_PLAY_BIN3_LOCK (playbin);
   new_group = playbin->next_group;
-  if (!new_group || !new_group->valid)
+  if (!new_group || !new_group->valid || new_group->active)
     goto no_next_group;
 
-  /* first unlink the current source, if any */
-  old_group = playbin->curr_group;
-  if (old_group && old_group->valid && old_group->active) {
-    new_group->stream_changed_pending = TRUE;
-
-    gst_play_bin3_update_cached_duration (playbin);
-    /* unlink our pads with the sink */
-    deactivate_group (playbin, old_group);
-    old_group->valid = FALSE;
-  }
-
-  /* swap old and new */
-  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);
 
+  debug_groups (playbin);
+
   return state_ret;
 
   /* ERRORS */
 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;
   }
@@ -5126,6 +4772,117 @@ groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked)
   return TRUE;
 }
 
+static void
+gst_play_bin3_check_group_status (GstPlayBin3 * playbin)
+{
+  if (playbin->activation_task)
+    gst_task_start (playbin->activation_task);
+}
+
+static void
+gst_play_bin3_activation_thread (GstPlayBin3 * playbin)
+{
+  GST_DEBUG_OBJECT (playbin, "starting");
+
+  debug_groups (playbin);
+
+  /* Check if next_group needs to be deactivated */
+  GST_PLAY_BIN3_LOCK (playbin);
+  if (playbin->next_group->active) {
+    deactivate_group (playbin, playbin->next_group);
+    playbin->next_group->valid = FALSE;
+  }
+
+  /* Is there a pending about-to-finish to be emitted ? */
+  GST_SOURCE_GROUP_LOCK (playbin->curr_group);
+  if (playbin->curr_group->pending_about_to_finish) {
+    GST_LOG_OBJECT (playbin, "Propagating about-to-finish");
+    playbin->curr_group->pending_about_to_finish = FALSE;
+    GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
+    /* This will activate the next source afterwards */
+    emit_about_to_finish (playbin);
+  } else
+    GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
+
+  GST_LOG_OBJECT (playbin, "Pausing task");
+  if (playbin->activation_task)
+    gst_task_pause (playbin->activation_task);
+  GST_PLAY_BIN3_UNLOCK (playbin);
+
+  GST_DEBUG_OBJECT (playbin, "done");
+  return;
+}
+
+static gboolean
+gst_play_bin3_start (GstPlayBin3 * playbin)
+{
+  GST_DEBUG_OBJECT (playbin, "starting");
+
+  GST_PLAY_BIN3_LOCK (playbin);
+
+  if (playbin->activation_task == NULL) {
+    playbin->activation_task =
+        gst_task_new ((GstTaskFunction) gst_play_bin3_activation_thread,
+        playbin, NULL);
+    if (playbin->activation_task == NULL)
+      goto task_error;
+    gst_task_set_lock (playbin->activation_task, &playbin->activation_lock);
+  }
+  GST_LOG_OBJECT (playbin, "clearing shutdown flag");
+  g_atomic_int_set (&playbin->shutdown, 0);
+  do_async_start (playbin);
+
+  GST_PLAY_BIN3_UNLOCK (playbin);
+
+  return TRUE;
+
+task_error:
+  {
+    GST_PLAY_BIN3_UNLOCK (playbin);
+    GST_ERROR_OBJECT (playbin, "Failed to create task");
+    return FALSE;
+  }
+}
+
+static void
+gst_play_bin3_stop (GstPlayBin3 * playbin)
+{
+  GstTask *task;
+
+  GST_DEBUG_OBJECT (playbin, "stopping");
+
+  /* FIXME unlock our waiting groups */
+  GST_LOG_OBJECT (playbin, "setting shutdown flag");
+  g_atomic_int_set (&playbin->shutdown, 1);
+
+  /* wait for all callbacks to end by taking the lock.
+   * No dynamic (critical) new callbacks will
+   * be able to happen as we set the shutdown flag. */
+  GST_PLAY_BIN3_DYN_LOCK (playbin);
+  GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
+  GST_PLAY_BIN3_DYN_UNLOCK (playbin);
+
+  /* Stop the activation task */
+  GST_PLAY_BIN3_LOCK (playbin);
+  if ((task = playbin->activation_task)) {
+    playbin->activation_task = NULL;
+    GST_PLAY_BIN3_UNLOCK (playbin);
+
+    gst_task_stop (task);
+
+    /* Make sure task is not running */
+    g_rec_mutex_lock (&playbin->activation_lock);
+    g_rec_mutex_unlock (&playbin->activation_lock);
+
+    /* Wait for task to finish and unref it */
+    gst_task_join (task);
+    gst_object_unref (task);
+
+    GST_PLAY_BIN3_LOCK (playbin);
+  }
+  GST_PLAY_BIN3_UNLOCK (playbin);
+}
+
 static GstStateChangeReturn
 gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
 {
@@ -5136,39 +4893,24 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
   playbin = GST_PLAY_BIN3 (element);
 
   switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      memset (&playbin->duration, 0, sizeof (playbin->duration));
-      break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      GST_LOG_OBJECT (playbin, "clearing shutdown flag");
-      memset (&playbin->duration, 0, sizeof (playbin->duration));
-      g_atomic_int_set (&playbin->shutdown, 0);
-      do_async_start (playbin);
+      if (!gst_play_bin3_start (playbin))
+        return GST_STATE_CHANGE_FAILURE;
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
     async_down:
-      /* FIXME unlock our waiting groups */
-      GST_LOG_OBJECT (playbin, "setting shutdown flag");
-      g_atomic_int_set (&playbin->shutdown, 1);
-      memset (&playbin->duration, 0, sizeof (playbin->duration));
-
-      /* wait for all callbacks to end by taking the lock.
-       * No dynamic (critical) new callbacks will
-       * be able to happen as we set the shutdown flag. */
-      GST_PLAY_BIN3_DYN_LOCK (playbin);
-      GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
-      GST_PLAY_BIN3_DYN_UNLOCK (playbin);
+      gst_play_bin3_stop (playbin);
       if (!do_save)
         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;
       }
-      memset (&playbin->duration, 0, sizeof (playbin->duration));
 
       /* unlock so that all groups go to NULL */
       groups_set_locked_state (playbin, FALSE);
@@ -5183,9 +4925,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;
@@ -5201,40 +4941,24 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_READY_TO_NULL:
     {
       guint i;
-      GList *l;
 
       /* 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 */
@@ -5255,31 +4979,6 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
       /* make sure the groups don't perform a state change anymore until we
        * enable them again */
       groups_set_locked_state (playbin, TRUE);
-
-      /* Remove all non-persistent contexts */
-      GST_OBJECT_LOCK (playbin);
-      for (l = playbin->contexts; l;) {
-        GstContext *context = l->data;
-
-        if (!gst_context_is_persistent (context)) {
-          GList *next;
-
-          gst_context_unref (context);
-
-          next = l->next;
-          playbin->contexts = g_list_delete_link (playbin->contexts, l);
-          l = next;
-        } else {
-          l = l->next;
-        }
-      }
-
-      if (playbin->source) {
-        gst_object_unref (playbin->source);
-        playbin->source = NULL;
-      }
-
-      GST_OBJECT_UNLOCK (playbin);
       break;
     }
     default:
index ead2e68..89cd225 100644 (file)
@@ -3550,7 +3550,6 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
       goto no_chain;
 
     if (!playsink->audio_sinkpad_stream_synchronizer) {
-      GstPad *audio_queue_srcpad;
       GValue item = { 0, };
       GstIterator *it;
 
@@ -3565,8 +3564,19 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
       g_value_unset (&item);
       g_assert (playsink->audio_srcpad_stream_synchronizer);
       gst_iterator_free (it);
+    }
+
+    if (need_vis) {
+      GstPad *audio_queue_srcpad;
+
+      if (gst_pad_is_linked (playsink->audio_sinkpad_stream_synchronizer)) {
+        GstPad *peer_pad =
+            gst_pad_get_peer (playsink->audio_sinkpad_stream_synchronizer);
+        gst_pad_unlink (peer_pad, playsink->audio_sinkpad_stream_synchronizer);
+        gst_object_unref (peer_pad);
+      }
 
-      if (need_vis) {
+      if (!playsink->audio_ssync_queue) {
         GST_DEBUG_OBJECT (playsink, "adding audio stream synchronizer queue");
         playsink->audio_ssync_queue =
             gst_element_factory_make ("queue", "audiossyncqueue");
@@ -3582,14 +3592,15 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
         gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_ssync_queue);
         playsink->audio_ssync_queue_sinkpad =
             gst_element_get_static_pad (playsink->audio_ssync_queue, "sink");
-        audio_queue_srcpad =
-            gst_element_get_static_pad (playsink->audio_ssync_queue, "src");
-        gst_element_sync_state_with_parent (playsink->audio_ssync_queue);
-        gst_pad_link_full (audio_queue_srcpad,
-            playsink->audio_sinkpad_stream_synchronizer,
-            GST_PAD_LINK_CHECK_NOTHING);
-        gst_object_unref (audio_queue_srcpad);
       }
+
+      audio_queue_srcpad =
+          gst_element_get_static_pad (playsink->audio_ssync_queue, "src");
+      gst_pad_link_full (audio_queue_srcpad,
+          playsink->audio_sinkpad_stream_synchronizer,
+          GST_PAD_LINK_CHECK_NOTHING);
+      gst_object_unref (audio_queue_srcpad);
+      gst_element_sync_state_with_parent (playsink->audio_ssync_queue);
     }
 
     if (playsink->audiochain) {
@@ -3667,7 +3678,26 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
     if (playsink->vischain) {
       GST_DEBUG_OBJECT (playsink, "setting up vis chain");
 
-      /* Just change vis plugin or set up chain? */
+      /* Lazily add and activate chain */
+      if (!playsink->vischain->chain.added) {
+        srcpad =
+            gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
+        add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
+        activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
+        if (playsink->audio_tee_vissrc == NULL) {
+          playsink->audio_tee_vissrc =
+              gst_element_get_request_pad (playsink->audio_tee, "src_%u");
+        }
+        gst_pad_link_full (playsink->audio_tee_vissrc,
+            playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
+        gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
+            GST_PAD_LINK_CHECK_NOTHING);
+        gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
+            playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
+        gst_object_unref (srcpad);
+      }
+
+      /* Is a reconfiguration required? */
       if (playsink->vischain->vis != playsink->visualisation) {
         /* unlink the old plugin and unghost the pad */
         gst_pad_unlink (playsink->vischain->vispeerpad,
@@ -3697,22 +3727,6 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
             playsink->vischain->vissinkpad, GST_PAD_LINK_CHECK_NOTHING);
         gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->vischain->
                 srcpad), playsink->vischain->vissrcpad);
-      } else {
-        srcpad =
-            gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
-        add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
-        activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
-        if (playsink->audio_tee_vissrc == NULL) {
-          playsink->audio_tee_vissrc =
-              gst_element_get_request_pad (playsink->audio_tee, "src_%u");
-        }
-        gst_pad_link_full (playsink->audio_tee_vissrc,
-            playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
-        gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
-            GST_PAD_LINK_CHECK_NOTHING);
-        gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
-            playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
-        gst_object_unref (srcpad);
       }
     }
   } else {
index 45e9251..0774c73 100644 (file)
@@ -455,8 +455,6 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * When download buffering is activated and used for the current media
    * type, this property does nothing. Otherwise perform buffering on the
    * demuxed or parsed media.
-   *
-   * Since: 0.10.26
    */
   g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
       g_param_spec_boolean ("use-buffering", "Use Buffering",
@@ -472,8 +470,6 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * caps (see 'caps' property) will have a pad exposed. Streams that do not
    * match those caps but could have been decoded will not have decoder plugged
    * in internally and will not have a pad exposed.
-   *
-   * Since: 0.10.30
    */
   g_object_class_install_property (gobject_class, PROP_EXPOSE_ALL_STREAMS,
       g_param_spec_boolean ("expose-all-streams", "Expose All Streams",
@@ -486,8 +482,6 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    *
    * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
    * buffer is disabled. Default is 0.
-   *
-   * Since: 0.10.31
    */
   g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
       g_param_spec_uint64 ("ring-buffer-max-size",
@@ -522,11 +516,11 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * emitted before looking for any elements that can handle that stream.
    *
    * >   Invocation of signal handlers stops after the first signal handler
-   * >   returns #FALSE. Signal handlers are invoked in the order they were
+   * >   returns %FALSE. Signal handlers are invoked in the order they were
    * >   connected in.
    *
-   * Returns: #TRUE if you wish uridecodebin to look for elements that can
-   * handle the given @caps. If #FALSE, those caps will be considered as
+   * Returns: %TRUE if you wish uridecodebin to look for elements that can
+   * handle the given @caps. If %FALSE, those caps will be considered as
    * final and the pad will be exposed as such (see 'pad-added' signal of
    * #GstElement).
    */
@@ -579,18 +573,16 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * the application to perform additional sorting or filtering on the element
    * factory array.
    *
-   * The callee should copy and modify @factories or return #NULL if the
+   * The callee should copy and modify @factories or return %NULL if the
    * order should not change.
    *
    * >   Invocation of signal handlers stops after one signal handler has
-   * >   returned something else than #NULL. Signal handlers are invoked in
+   * >   returned something else than %NULL. Signal handlers are invoked in
    * >   the order they were connected in.
    * >   Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
    * >   signal, they will never be invoked!
    *
    * Returns: A new sorted array of #GstElementFactory objects.
-   *
-   * Since: 0.10.33
    */
   gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SORT] =
       g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass),
@@ -652,7 +644,7 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * be used to tell the element about the downstream supported caps
    * for example.
    *
-   * Returns: #TRUE if the query was handled, #FALSE otherwise.
+   * Returns: %TRUE if the query was handled, %FALSE otherwise.
    */
   gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
       g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
@@ -682,8 +674,6 @@ gst_uri_decode_bin_class_init (GstURIDecodeBinClass * klass)
    * proxy server for an http source, or set the device and read speed for
    * an audio cd source). This is functionally equivalent to connecting to
    * the notify::source signal, but more convenient.
-   *
-   * Since: 0.10.33
    */
   gst_uri_decode_bin_signals[SIGNAL_SOURCE_SETUP] =
       g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
@@ -1640,6 +1630,11 @@ remove_decoders (GstURIDecodeBin * bin, gboolean force)
     GstElement *decoder = GST_ELEMENT_CAST (walk->data);
 
     GST_DEBUG_OBJECT (bin, "removing old decoder element");
+
+    /* Even if we reuse this decodebin, the previous topology will
+     * be irrelevant */
+    g_object_set_data (G_OBJECT (decoder), "uridecodebin-topology", NULL);
+
     if (force) {
       gst_element_set_state (decoder, GST_STATE_NULL);
       gst_bin_remove (GST_BIN_CAST (bin), decoder);
@@ -2392,6 +2387,54 @@ handle_redirect_message (GstURIDecodeBin * dec, GstMessage * msg)
   return new_msg;
 }
 
+static GstMessage *
+make_topology_message (GstURIDecodeBin * dec)
+{
+  GSList *tmp;
+  GstStructure *aggregated_topology = NULL;
+  GValue list = G_VALUE_INIT;
+  GstCaps *caps = NULL;
+  gchar *name, *proto;
+
+  aggregated_topology = gst_structure_new_empty ("stream-topology");
+  g_value_init (&list, GST_TYPE_LIST);
+
+  for (tmp = dec->decodebins; tmp; tmp = tmp->next) {
+    GValue item = G_VALUE_INIT;
+    GstStructure *dec_topology =
+        g_object_get_data (G_OBJECT (tmp->data), "uridecodebin-topology");
+
+    g_value_init (&item, GST_TYPE_STRUCTURE);
+    gst_value_set_structure (&item, dec_topology);
+    gst_value_list_append_and_take_value (&list, &item);
+  }
+
+  gst_structure_take_value (aggregated_topology, "next", &list);
+
+  /* This is a bit wacky, but that's the best way I can find to express
+   * uridecodebin 'caps' as subsequently shown by gst-discoverer */
+  proto = gst_uri_get_protocol (dec->uri);
+  name = g_strdup_printf ("application/%s", proto);
+  g_free (proto);
+
+  caps = gst_caps_new_empty_simple (name);
+  g_free (name);
+
+  gst_structure_set (aggregated_topology, "caps", GST_TYPE_CAPS, caps, NULL);
+  gst_caps_unref (caps);
+
+  return gst_message_new_element (GST_OBJECT (dec), aggregated_topology);
+}
+
+static void
+check_topology (gpointer data, gpointer user_data)
+{
+  gboolean *has_topo = user_data;
+
+  if (g_object_get_data (data, "uridecodebin-topology") == NULL)
+    *has_topo = FALSE;
+}
+
 static void
 handle_message (GstBin * bin, GstMessage * msg)
 {
@@ -2399,7 +2442,32 @@ handle_message (GstBin * bin, GstMessage * msg)
 
   switch (GST_MESSAGE_TYPE (msg)) {
     case GST_MESSAGE_ELEMENT:{
-      if (gst_message_has_name (msg, "redirect")) {
+
+      if (gst_message_has_name (msg, "stream-topology")) {
+        GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (msg));
+        gboolean has_all_topo = TRUE;
+
+        if (dec->pending || (dec->decodebins && dec->decodebins->next != NULL)) {
+          const GstStructure *structure;
+
+          /* If there is only one, just let it through, so this case is if
+           * there is more than one.
+           */
+
+          structure = gst_message_get_structure (msg);
+
+          g_object_set_data_full (G_OBJECT (element), "uridecodebin-topology",
+              gst_structure_copy (structure),
+              (GDestroyNotify) gst_structure_free);
+
+          gst_message_unref (msg);
+          msg = NULL;
+
+          g_slist_foreach (dec->decodebins, check_topology, &has_all_topo);
+          if (has_all_topo)
+            msg = make_topology_message (dec);
+        }
+      } else if (gst_message_has_name (msg, "redirect")) {
         /* sort redirect messages based on the connection speed. This simplifies
          * the user of this element as it can in most cases just pick the first item
          * of the sorted list as a good redirection candidate. It can of course
diff --git a/gst/playback/gsturidecodebin3.c b/gst/playback/gsturidecodebin3.c
new file mode 100644 (file)
index 0000000..8b1840d
--- /dev/null
@@ -0,0 +1,1087 @@
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO/FIXME:
+ *
+ * * BUFFERING MESSAGES
+ * ** How/Where do we deal with buffering messages from a new/prerolling
+ *    source ? Ideally we want to re-use the same sourcebin ?
+ * ** Remember last buffering messages per source handler, if the SourceEntry
+ *    group_id is the one being currently outputted on the source ghostpads,
+ *    post the (last) buffering messages.
+ *    If no group_id is being outputted (still prerolling), then output
+ *    the messages directly
+ *
+ * * ASYNC HANDLING
+ * ** URIDECODEBIN3 is not async-aware.
+ *
+ * * GAPLESS HANDLING
+ * ** Correlate group_id and URI to know when/which stream is being outputted/started
+ */
+
+/**
+ * SECTION:element-uridecodebin3
+ * @title: uridecodebin3
+ *
+ * Decodes data from a URI into raw media. It selects a source element that can
+ * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/gst-i18n-plugin.h>
+#include <gst/pbutils/missing-plugins.h>
+
+#include "gstplay-enum.h"
+#include "gstrawcaps.h"
+#include "gstplayback.h"
+#include "gstplaybackutils.h"
+
+#define GST_TYPE_URI_DECODE_BIN3 \
+  (gst_uri_decode_bin3_get_type())
+#define GST_URI_DECODE_BIN3(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
+#define GST_URI_DECODE_BIN3_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
+#define GST_IS_URI_DECODE_BIN3(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
+#define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
+#define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
+
+typedef struct _GstSourceGroup GstSourceGroup;
+typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
+typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
+
+#define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
+#define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
+
+typedef struct _GstPlayItem GstPlayItem;
+typedef struct _GstSourceItem GstSourceItem;
+typedef struct _GstSourceHandler GstSourceHandler;
+typedef struct _OutputPad OutputPad;
+
+/* A structure describing a play item, which travels through the elements
+ * over time. */
+struct _GstPlayItem
+{
+  GstURIDecodeBin3 *uridecodebin;
+
+  /* Main URI */
+  GstSourceItem *main_item;
+
+  /* Auxiliary URI */
+  /* FIXME : Replace by a list later */
+  GstSourceItem *sub_item;
+
+  /* The group_id used to identify this play item via STREAM_START events
+   * This is the group_id which will be used externally (i.e. rewritten
+   * to outgoing STREAM_START events and in emitted signals).
+   * The urisourcebin-specific group_id is located in GstSourceItem */
+  guint group_id;
+
+  /* Is this play item the one being currently outputted by decodebin3
+   * and on our source ghostpads */
+  gboolean currently_outputted;
+};
+
+struct _GstSourceItem
+{
+  /* The GstPlayItem to which this GstSourceItem belongs to */
+  GstPlayItem *play_item;
+
+  gchar *uri;
+
+  /* The urisourcebin controlling this uri
+   * Can be NULL */
+  GstSourceHandler *handler;
+
+  /* Last buffering information */
+  gint last_perc;
+  GstMessage *last_buffering_message;
+
+  /* The groupid created by urisourcebin for this uri */
+  guint internal_groupid;
+
+  /* FIXME : Add tag lists and other uri-specific items here ? */
+};
+
+/* Structure wrapping everything related to a urisourcebin */
+struct _GstSourceHandler
+{
+  GstURIDecodeBin3 *uridecodebin;
+
+  GstElement *urisourcebin;
+
+  /* Signal handlers */
+  gulong pad_added_id;
+  gulong pad_removed_id;
+  gulong source_setup_id;
+  gulong about_to_finish_id;
+
+  /* TRUE if the controlled urisourcebin was added to uridecodebin */
+  gboolean active;
+
+  /* whether urisourcebin is drained or not.
+   * Reset if/when setting a new URI */
+  gboolean drained;
+
+  /* Whether urisourcebin posted EOS on all pads and
+   * there is no pending entry */
+  gboolean is_eos;
+
+  /* TRUE if the urisourcebin handles main item */
+  gboolean is_main_source;
+
+  /* buffering message stored for after switching */
+  GstMessage *pending_buffering_msg;
+};
+
+/* Controls an output source pad */
+struct _OutputPad
+{
+  GstURIDecodeBin3 *uridecodebin;
+
+  GstPad *target_pad;
+  GstPad *ghost_pad;
+
+  /* Downstream event probe id */
+  gulong probe_id;
+
+  /* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */
+  gboolean is_eos;
+
+  /* The last seen (i.e. current) group_id
+   * Can be (guint)-1 if no group_id was seen yet */
+  guint current_group_id;
+};
+
+/**
+ * GstURIDecodeBin3
+ *
+ * uridecodebin3 element struct
+ */
+struct _GstURIDecodeBin3
+{
+  GstBin parent_instance;
+
+  GMutex lock;                  /* lock for constructing */
+
+  /* Properties */
+  GstElement *source;
+  guint64 connection_speed;     /* In bits/sec (0 = unknown) */
+  GstCaps *caps;
+  guint64 buffer_duration;      /* When buffering, buffer duration (ns) */
+  guint buffer_size;            /* When buffering, buffer size (bytes) */
+  gboolean download;
+  gboolean use_buffering;
+  guint64 ring_buffer_max_size;
+
+  GList *play_items;            /* List of GstPlayItem ordered by time of
+                                 * creation. Head of list is therefore the
+                                 * current (or pending if initial) one being
+                                 * outputted */
+  GstPlayItem *current;         /* Currently active GstPlayItem. Can be NULL
+                                 * if no entry is active yet (i.e. no source
+                                 * pads) */
+
+  /* sources.
+   * FIXME : Replace by a more modular system later on */
+  GstSourceHandler *main_handler;
+  GstSourceHandler *sub_handler;
+
+  /* URI handling
+   * FIXME : Switch to a playlist-based API */
+  gchar *uri;
+  gboolean uri_changed;         /* TRUE if uri changed */
+  gchar *suburi;
+  gboolean suburi_changed;      /* TRUE if suburi changed */
+
+  /* A global decodebin3 that's used to actually do decoding */
+  GstElement *decodebin;
+
+  /* db3 signals */
+  gulong db_pad_added_id;
+  gulong db_pad_removed_id;
+  gulong db_select_stream_id;
+  gulong db_about_to_finish_id;
+
+  GList *output_pads;           /* List of OutputPad */
+
+  GList *source_handlers;       /* List of SourceHandler */
+
+  /* Whether we already signalled about-to-finish or not
+   * FIXME: Track this by group-id ! */
+  gboolean posted_about_to_finish;
+};
+
+struct _GstURIDecodeBin3Class
+{
+  GstBinClass parent_class;
+
+    gint (*select_stream) (GstURIDecodeBin3 * dbin,
+      GstStreamCollection * collection, GstStream * stream);
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
+#define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
+
+/* signals */
+enum
+{
+  SIGNAL_SELECT_STREAM,
+  SIGNAL_SOURCE_SETUP,
+  SIGNAL_ABOUT_TO_FINISH,
+  LAST_SIGNAL
+};
+
+#if 0
+static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
+static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
+#endif
+
+/* properties */
+#define DEFAULT_PROP_URI            NULL
+#define DEFAULT_PROP_SUBURI            NULL
+#define DEFAULT_PROP_SOURCE         NULL
+#define DEFAULT_CONNECTION_SPEED    0
+#define DEFAULT_CAPS                (gst_static_caps_get (&default_raw_caps))
+#define DEFAULT_BUFFER_DURATION     -1
+#define DEFAULT_BUFFER_SIZE         -1
+#define DEFAULT_DOWNLOAD            FALSE
+#define DEFAULT_USE_BUFFERING       FALSE
+#define DEFAULT_RING_BUFFER_MAX_SIZE 0
+
+enum
+{
+  PROP_0,
+  PROP_URI,
+  PROP_CURRENT_URI,
+  PROP_SUBURI,
+  PROP_CURRENT_SUBURI,
+  PROP_SOURCE,
+  PROP_CONNECTION_SPEED,
+  PROP_BUFFER_SIZE,
+  PROP_BUFFER_DURATION,
+  PROP_DOWNLOAD,
+  PROP_USE_BUFFERING,
+  PROP_RING_BUFFER_MAX_SIZE,
+  PROP_CAPS
+};
+
+static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
+
+static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video_%u",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate audio_src_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%u",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate text_src_template =
+GST_STATIC_PAD_TEMPLATE ("text_%u",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+GType gst_uri_decode_bin3_get_type (void);
+#define gst_uri_decode_bin3_parent_class parent_class
+G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
+
+#define REMOVE_SIGNAL(obj,id)            \
+if (id) {                                \
+  g_signal_handler_disconnect (obj, id); \
+  id = 0;                                \
+}
+
+static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_finalize (GObject * obj);
+
+static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
+    element, GstStateChange transition);
+
+static gboolean
+_gst_int_accumulator (GSignalInvocationHint * ihint,
+    GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+  gint res = g_value_get_int (handler_return);
+
+  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+    g_value_set_int (return_accu, res);
+
+  if (res == -1)
+    return TRUE;
+
+  return FALSE;
+}
+
+
+static void
+gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gstelement_class = GST_ELEMENT_CLASS (klass);
+
+  gobject_class->set_property = gst_uri_decode_bin3_set_property;
+  gobject_class->get_property = gst_uri_decode_bin3_get_property;
+  gobject_class->finalize = gst_uri_decode_bin3_finalize;
+
+  g_object_class_install_property (gobject_class, PROP_URI,
+      g_param_spec_string ("uri", "URI", "URI to decode",
+          DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
+      g_param_spec_string ("current-uri", "Current URI",
+          "The currently playing URI", NULL,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SUBURI,
+      g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
+          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
+      g_param_spec_string ("current-suburi", "Current .sub-URI",
+          "The currently playing URI of a subtitle",
+          NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_SOURCE,
+      g_param_spec_object ("source", "Source", "Source object used",
+          GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
+      g_param_spec_uint64 ("connection-speed", "Connection Speed",
+          "Network connection speed in kbps (0 = unknown)",
+          0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
+      g_param_spec_int ("buffer-size", "Buffer size (bytes)",
+          "Buffer size when buffering streams (-1 default value)",
+          -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
+      g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
+          "Buffer duration when buffering streams (-1 default value)",
+          -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstURIDecodeBin3::download:
+   *
+   * For certain media type, enable download buffering.
+   */
+  g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
+      g_param_spec_boolean ("download", "Download",
+          "Attempt download buffering when buffering network streams",
+          DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstURIDecodeBin3::use-buffering:
+   *
+   * Emit BUFFERING messages based on low-/high-percent thresholds of the
+   * demuxed or parsed data.
+   * When download buffering is activated and used for the current media
+   * type, this property does nothing. Otherwise perform buffering on the
+   * demuxed or parsed media.
+   */
+  g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
+      g_param_spec_boolean ("use-buffering", "Use Buffering",
+          "Perform buffering on demuxed/parsed media",
+          DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstURIDecodeBin3::ring-buffer-max-size
+   *
+   * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
+   * buffer is disabled. Default is 0.
+   */
+  g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
+      g_param_spec_uint64 ("ring-buffer-max-size",
+          "Max. ring buffer size (bytes)",
+          "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
+          0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_CAPS,
+      g_param_spec_boxed ("caps", "Caps",
+          "The caps on which to stop decoding. (NULL = default)",
+          GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstURIDecodebin3::select-stream
+   * @decodebin: a #GstURIDecodebin3
+   * @collection: a #GstStreamCollection
+   * @stream: a #GstStream
+   *
+   * This signal is emitted whenever @decodebin needs to decide whether
+   * to expose a @stream of a given @collection.
+   *
+   * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
+   * A value of -1 (default) lets @decodebin decide what to do with the stream.
+   * */
+  gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
+      g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
+      _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
+      G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
+
+  /**
+   * GstURIDecodeBin3::source-setup:
+   * @bin: the uridecodebin.
+   * @source: source element
+   *
+   * This signal is emitted after a source element has been created, so
+   * it can be configured by setting additional properties (e.g. set a
+   * proxy server for an http source, or set the device and read speed for
+   * an audio cd source).
+   */
+  gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
+      g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+  /**
+   * GstURIDecodeBin3::about-to-finish:
+   *
+   * This signal is emitted when the data for the selected URI is
+   * entirely buffered and it is safe to specify anothe URI.
+   */
+  gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
+      g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+      0, G_TYPE_NONE);
+
+
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &video_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &audio_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &text_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class, &src_template);
+  gst_element_class_set_static_metadata (gstelement_class,
+      "URI Decoder", "Generic/Bin/Decoder",
+      "Autoplug and decode an URI to raw media",
+      "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
+
+  gstelement_class->change_state = gst_uri_decode_bin3_change_state;
+
+}
+
+static GstPadProbeReturn
+db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
+{
+  /* FIXME : IMPLEMENT */
+
+  /* EOS : Mark pad as EOS */
+
+  /* STREAM_START : Store group_id and check if currently active
+   *  PlayEntry changed */
+
+  return GST_PAD_PROBE_OK;
+}
+
+static OutputPad *
+add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
+{
+  OutputPad *output;
+  gchar *pad_name;
+
+  output = g_slice_new0 (OutputPad);
+
+  GST_LOG_OBJECT (dec, "Created output %p", output);
+
+  output->uridecodebin = dec;
+  output->target_pad = target_pad;
+  output->current_group_id = (guint) - 1;
+  pad_name = gst_pad_get_name (target_pad);
+  output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
+  g_free (pad_name);
+
+  gst_pad_set_active (output->ghost_pad, TRUE);
+  gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
+
+  output->probe_id =
+      gst_pad_add_probe (output->target_pad,
+      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
+      output, NULL);
+
+  /* FIXME: LOCK TO PROTECT PAD LIST */
+  dec->output_pads = g_list_append (dec->output_pads, output);
+
+  return output;
+}
+
+static void
+db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+  GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  if (GST_PAD_IS_SRC (pad))
+    add_output_pad (dec, pad);
+}
+
+static void
+db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+  GList *tmp;
+  OutputPad *output = NULL;
+
+  if (!GST_PAD_IS_SRC (pad))
+    return;
+
+  GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+  /* FIXME: LOCK for list access */
+
+  for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
+    OutputPad *cand = (OutputPad *) tmp->data;
+
+    if (cand->target_pad == pad) {
+      output = cand;
+      dec->output_pads = g_list_delete_link (dec->output_pads, tmp);
+      break;
+    }
+  }
+
+  if (output) {
+    GST_LOG_OBJECT (element, "Removing output %p", output);
+    /* Remove source ghost pad */
+    gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
+    gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
+
+    /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
+
+    /* Remove event probe */
+    gst_pad_remove_probe (output->target_pad, output->probe_id);
+
+    g_slice_free (OutputPad, output);
+  }
+}
+
+static gint
+db_select_stream_cb (GstElement * decodebin,
+    GstStreamCollection * collection, GstStream * stream,
+    GstURIDecodeBin3 * uridecodebin)
+{
+  gint response = -1;
+
+  g_signal_emit (uridecodebin,
+      gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
+      &response);
+  return response;
+}
+
+static void
+db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
+{
+  if (!uridecodebin->posted_about_to_finish) {
+    uridecodebin->posted_about_to_finish = TRUE;
+    g_signal_emit (uridecodebin,
+        gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+  }
+}
+
+static void
+gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
+{
+  g_mutex_init (&dec->lock);
+
+  dec->caps = gst_static_caps_get (&default_raw_caps);
+
+  dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
+  gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
+  dec->db_pad_added_id =
+      g_signal_connect (dec->decodebin, "pad-added",
+      G_CALLBACK (db_pad_added_cb), dec);
+  dec->db_pad_removed_id =
+      g_signal_connect (dec->decodebin, "pad-removed",
+      G_CALLBACK (db_pad_removed_cb), dec);
+  dec->db_select_stream_id =
+      g_signal_connect (dec->decodebin, "select-stream",
+      G_CALLBACK (db_select_stream_cb), dec);
+  dec->db_about_to_finish_id =
+      g_signal_connect (dec->decodebin, "about-to-finish",
+      G_CALLBACK (db_about_to_finish_cb), dec);
+
+  GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE);
+  gst_bin_set_suppressed_flags (GST_BIN (dec),
+      GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
+}
+
+static void
+gst_uri_decode_bin3_finalize (GObject * obj)
+{
+  GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
+
+  g_mutex_clear (&dec->lock);
+  g_free (dec->uri);
+  g_free (dec->suburi);
+
+  G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static GstStateChangeReturn
+activate_source_item (GstSourceItem * item)
+{
+  GstSourceHandler *handler = item->handler;
+
+  if (handler == NULL) {
+    GST_WARNING ("Can't activate item without a handler");
+    return GST_STATE_CHANGE_FAILURE;
+  }
+
+  g_object_set (handler->urisourcebin, "uri", item->uri, NULL);
+  if (!handler->active) {
+    gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin);
+    /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */
+    /*   return GST_STATE_CHANGE_FAILURE; */
+    handler->active = TRUE;
+  }
+
+  return GST_STATE_CHANGE_SUCCESS;
+}
+
+static void
+src_pad_added_cb (GstElement * element, GstPad * pad,
+    GstSourceHandler * handler)
+{
+  GstURIDecodeBin3 *uridecodebin;
+  GstPad *sinkpad = NULL;
+  GstPadLinkReturn res;
+
+  uridecodebin = handler->uridecodebin;
+
+  GST_DEBUG_OBJECT (uridecodebin,
+      "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
+
+  /* FIXME: Add probe to unify group_id and detect EOS */
+
+  /* Try to link to main sink pad only if it's from a main handler */
+  if (handler->is_main_source) {
+    sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink");
+    if (gst_pad_is_linked (sinkpad)) {
+      gst_object_unref (sinkpad);
+      sinkpad = NULL;
+    }
+  }
+
+  if (sinkpad == NULL)
+    sinkpad = gst_element_get_request_pad (uridecodebin->decodebin, "sink_%u");
+
+  if (sinkpad) {
+    GST_DEBUG_OBJECT (uridecodebin,
+        "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, 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 (uridecodebin,
+        "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
+src_pad_removed_cb (GstElement * element, GstPad * pad,
+    GstSourceHandler * handler)
+{
+  /* FIXME : IMPLEMENT */
+}
+
+static void
+src_source_setup_cb (GstElement * element, GstElement * source,
+    GstSourceHandler * handler)
+{
+  g_signal_emit (handler->uridecodebin,
+      gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL);
+}
+
+static void
+src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler)
+{
+  /* FIXME : check if all sources are done */
+  if (!handler->uridecodebin->posted_about_to_finish) {
+    handler->uridecodebin->posted_about_to_finish = TRUE;
+    g_signal_emit (handler->uridecodebin,
+        gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+  }
+}
+
+static GstSourceHandler *
+new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
+{
+  GstSourceHandler *handler;
+
+  handler = g_slice_new0 (GstSourceHandler);
+
+  handler->uridecodebin = uridecodebin;
+  handler->is_main_source = is_main;
+  handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
+  /* Set pending properties */
+  g_object_set (handler->urisourcebin,
+      "connection-speed", uridecodebin->connection_speed / 1000,
+      "download", uridecodebin->download,
+      "use-buffering", uridecodebin->use_buffering,
+      "buffer-duration", uridecodebin->buffer_duration,
+      "buffer-size", uridecodebin->buffer_size,
+      "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL);
+
+  handler->pad_added_id =
+      g_signal_connect (handler->urisourcebin, "pad-added",
+      (GCallback) src_pad_added_cb, handler);
+  handler->pad_removed_id =
+      g_signal_connect (handler->urisourcebin, "pad-removed",
+      (GCallback) src_pad_removed_cb, handler);
+  handler->source_setup_id =
+      g_signal_connect (handler->urisourcebin, "source-setup",
+      (GCallback) src_source_setup_cb, handler);
+  handler->about_to_finish_id =
+      g_signal_connect (handler->urisourcebin, "about-to-finish",
+      (GCallback) src_about_to_finish_cb, handler);
+
+  uridecodebin->source_handlers =
+      g_list_append (uridecodebin->source_handlers, handler);
+
+  return handler;
+}
+
+static void
+gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+  switch (prop_id) {
+    case PROP_URI:
+      if (dec->uri)
+        g_free (dec->uri);
+      dec->uri = g_value_dup_string (value);
+      break;
+    case PROP_SUBURI:
+      if (dec->suburi)
+        g_free (dec->suburi);
+      dec->suburi = g_value_dup_string (value);
+      break;
+    case PROP_CONNECTION_SPEED:
+      GST_URI_DECODE_BIN3_LOCK (dec);
+      dec->connection_speed = g_value_get_uint64 (value) * 1000;
+      GST_URI_DECODE_BIN3_UNLOCK (dec);
+      break;
+    case PROP_BUFFER_SIZE:
+      dec->buffer_size = g_value_get_int (value);
+      break;
+    case PROP_BUFFER_DURATION:
+      dec->buffer_duration = g_value_get_int64 (value);
+      break;
+    case PROP_DOWNLOAD:
+      dec->download = g_value_get_boolean (value);
+      break;
+    case PROP_USE_BUFFERING:
+      dec->use_buffering = g_value_get_boolean (value);
+      break;
+    case PROP_RING_BUFFER_MAX_SIZE:
+      dec->ring_buffer_max_size = g_value_get_uint64 (value);
+      break;
+    case PROP_CAPS:
+      GST_OBJECT_LOCK (dec);
+      if (dec->caps)
+        gst_caps_unref (dec->caps);
+      dec->caps = g_value_dup_boxed (value);
+      GST_OBJECT_UNLOCK (dec);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+  switch (prop_id) {
+    case PROP_URI:
+    {
+      g_value_set_string (value, dec->uri);
+      break;
+    }
+    case PROP_CURRENT_URI:
+    {
+      g_value_set_string (value, dec->suburi);
+      break;
+    }
+    case PROP_SUBURI:
+    {
+      /* FIXME : Return current uri */
+      g_value_set_string (value, dec->uri);
+      break;
+    }
+    case PROP_CURRENT_SUBURI:
+    {
+      /* FIXME : Return current suburi */
+      g_value_set_string (value, dec->suburi);
+      break;
+    }
+    case PROP_SOURCE:
+    {
+      GST_OBJECT_LOCK (dec);
+      g_value_set_object (value, dec->source);
+      GST_OBJECT_UNLOCK (dec);
+      break;
+    }
+    case PROP_CONNECTION_SPEED:
+      GST_URI_DECODE_BIN3_LOCK (dec);
+      g_value_set_uint64 (value, dec->connection_speed / 1000);
+      GST_URI_DECODE_BIN3_UNLOCK (dec);
+      break;
+    case PROP_BUFFER_SIZE:
+      GST_OBJECT_LOCK (dec);
+      g_value_set_int (value, dec->buffer_size);
+      GST_OBJECT_UNLOCK (dec);
+      break;
+    case PROP_BUFFER_DURATION:
+      GST_OBJECT_LOCK (dec);
+      g_value_set_int64 (value, dec->buffer_duration);
+      GST_OBJECT_UNLOCK (dec);
+      break;
+    case PROP_DOWNLOAD:
+      g_value_set_boolean (value, dec->download);
+      break;
+    case PROP_USE_BUFFERING:
+      g_value_set_boolean (value, dec->use_buffering);
+      break;
+    case PROP_RING_BUFFER_MAX_SIZE:
+      g_value_set_uint64 (value, dec->ring_buffer_max_size);
+      break;
+    case PROP_CAPS:
+      GST_OBJECT_LOCK (dec);
+      g_value_set_boxed (value, dec->caps);
+      GST_OBJECT_UNLOCK (dec);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+free_source_handler (GstURIDecodeBin3 * uridecodebin,
+    GstSourceHandler * handler)
+{
+  GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
+  if (handler->active) {
+    GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
+        handler->urisourcebin);
+    gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
+    gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
+  }
+  uridecodebin->source_handlers =
+      g_list_remove (uridecodebin->source_handlers, handler);
+  g_slice_free (GstSourceHandler, handler);
+}
+
+static GstSourceItem *
+new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri)
+{
+  GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem);
+
+  sourceitem->play_item = item;
+  sourceitem->uri = uri;
+
+  return sourceitem;
+}
+
+static void
+free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item)
+{
+  GST_LOG_OBJECT (uridecodebin, "source item %p", item);
+  if (item->handler)
+    free_source_handler (uridecodebin, item->handler);
+  g_slice_free (GstSourceItem, item);
+}
+
+static GstPlayItem *
+new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
+{
+  GstPlayItem *item = g_slice_new0 (GstPlayItem);
+
+  item->uridecodebin = dec;
+  item->main_item = new_source_item (dec, item, uri);
+  if (suburi)
+    item->sub_item = new_source_item (dec, item, suburi);
+
+  return item;
+}
+
+static void
+free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+  GST_LOG_OBJECT (dec, "play item %p", item);
+  if (item->main_item)
+    free_source_item (dec, item->main_item);
+  if (item->sub_item)
+    free_source_item (dec, item->sub_item);
+
+  g_slice_free (GstPlayItem, item);
+}
+
+/* Sync source handlers for the given play item. Might require creating/removing some
+ * and/or configure the handlers accordingly */
+static GstStateChangeReturn
+assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  /* FIXME : Go over existing handlers to see if we can assign some to the
+   * given item */
+
+  /* Create missing handlers */
+  if (item->main_item->handler == NULL) {
+    item->main_item->handler = new_source_handler (dec, TRUE);
+    ret = activate_source_item (item->main_item);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+      return ret;
+  }
+
+  if (item->sub_item && item->sub_item->handler) {
+    item->sub_item->handler = new_source_handler (dec, FALSE);
+    ret = activate_source_item (item->sub_item);
+  }
+
+  return ret;
+}
+
+/* Called to activate the next play item */
+static GstStateChangeReturn
+activate_next_play_item (GstURIDecodeBin3 * dec)
+{
+  GstPlayItem *item;
+  GstStateChangeReturn ret;
+
+  /* If there is no current play entry, create one from the uri/suburi
+   * FIXME : Use a playlist API in the future */
+  item = new_play_item (dec, dec->uri, dec->suburi);
+
+  ret = assign_handlers_to_item (dec, item);
+  if (ret == GST_STATE_CHANGE_FAILURE) {
+    free_play_item (dec, item);
+    return ret;
+  }
+
+  dec->play_items = g_list_append (dec->play_items, item);
+
+  return ret;
+}
+
+static void
+free_play_items (GstURIDecodeBin3 * dec)
+{
+  GList *tmp;
+
+  for (tmp = dec->play_items; tmp; tmp = tmp->next) {
+    GstPlayItem *item = (GstPlayItem *) tmp->data;
+    free_play_item (dec, item);
+  }
+
+  g_list_free (dec->play_items);
+  dec->play_items = NULL;
+}
+
+static GstStateChangeReturn
+gst_uri_decode_bin3_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstStateChangeReturn ret;
+  GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      ret = activate_next_play_item (uridecodebin);
+      if (ret == GST_STATE_CHANGE_FAILURE)
+        goto failure;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    goto failure;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* FIXME: Cleanup everything */
+      free_play_items (uridecodebin);
+      /* Free play item */
+      uridecodebin->posted_about_to_finish = FALSE;
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+
+  /* ERRORS */
+failure:
+  {
+    if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
+      free_play_items (uridecodebin);
+    return ret;
+  }
+}
+
+
+gboolean
+gst_uri_decode_bin3_plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0,
+      "URI decoder element 3");
+
+  return gst_element_register (plugin, "uridecodebin3", GST_RANK_NONE,
+      GST_TYPE_URI_DECODE_BIN3);
+}
index 03da4a9..2713852 100644 (file)
@@ -151,8 +151,6 @@ struct _GstURISourceBin
   /* for dynamic sources */
   guint src_np_sig_id;          /* new-pad signal id */
 
-  gboolean async_pending;       /* async-start has been emitted */
-
   guint64 ring_buffer_max_size; /* 0 means disabled */
 
   GList *pending_pads;          /* Pads we have blocked pending assignment
@@ -169,28 +167,12 @@ struct _GstURISourceBinClass
 {
   GstBinClass parent_class;
 
-  /* signal fired when we found a pad that we cannot decode */
-  void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps);
-
-  /* signal fired to know if we continue trying to decode the given caps */
-    gboolean (*autoplug_continue) (GstElement * element, GstPad * pad,
-      GstCaps * caps);
-  /* signal fired to get a list of factories to try to autoplug */
-  GValueArray *(*autoplug_factories) (GstElement * element, GstPad * pad,
-      GstCaps * caps);
-  /* signal fired to sort the factories */
-  GValueArray *(*autoplug_sort) (GstElement * element, GstPad * pad,
-      GstCaps * caps, GValueArray * factories);
-  /* signal fired to select from the proposed list of factories */
-    GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
-      GstPad * pad, GstCaps * caps, GstElementFactory * factory);
-  /* signal fired when a autoplugged element that is not linked downstream
-   * or exposed wants to query something */
-    gboolean (*autoplug_query) (GstElement * element, GstPad * pad,
-      GstQuery * query);
-
-  /* emitted when all data is decoded */
+  /* emitted when all data has been drained out
+   * FIXME : What do we need this for ?? */
   void (*drained) (GstElement * element);
+  /* emitted when all data has been fed into buffering slots (i.e the
+   * actual sources are done) */
+  void (*about_to_finish) (GstElement * element);
 };
 
 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
@@ -206,13 +188,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_uri_source_bin_debug);
 /* signals */
 enum
 {
-  SIGNAL_UNKNOWN_TYPE,
-  SIGNAL_AUTOPLUG_CONTINUE,
-  SIGNAL_AUTOPLUG_FACTORIES,
-  SIGNAL_AUTOPLUG_SELECT,
-  SIGNAL_AUTOPLUG_SORT,
-  SIGNAL_AUTOPLUG_QUERY,
   SIGNAL_DRAINED,
+  SIGNAL_ABOUT_TO_FINISH,
   SIGNAL_SOURCE_SETUP,
   LAST_SIGNAL
 };
@@ -241,6 +218,21 @@ enum
   PROP_RING_BUFFER_MAX_SIZE
 };
 
+#define CUSTOM_EOS_QUARK _custom_eos_quark_get ()
+#define CUSTOM_EOS_QUARK_DATA "custom-eos"
+static GQuark
+_custom_eos_quark_get (void)
+{
+  static gsize g_quark;
+
+  if (g_once_init_enter (&g_quark)) {
+    gsize quark =
+        (gsize) g_quark_from_static_string ("urisourcebin-custom-eos");
+    g_once_init_leave (&g_quark, quark);
+  }
+  return g_quark;
+}
+
 static void post_missing_plugin_error (GstElement * dec,
     const gchar * element_name);
 
@@ -273,172 +265,6 @@ static void free_output_slot_async (GstURISourceBin * urisrc,
 static GstPad *create_output_pad (GstURISourceBin * urisrc, GstPad * pad);
 static void remove_buffering_msgs (GstURISourceBin * bin, GstObject * src);
 
-static gboolean
-_gst_boolean_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  gboolean myboolean;
-
-  myboolean = g_value_get_boolean (handler_return);
-  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
-    g_value_set_boolean (return_accu, myboolean);
-
-  /* stop emission if FALSE */
-  return myboolean;
-}
-
-static gboolean
-_gst_boolean_or_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  gboolean myboolean;
-  gboolean retboolean;
-
-  myboolean = g_value_get_boolean (handler_return);
-  retboolean = g_value_get_boolean (return_accu);
-
-  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
-    g_value_set_boolean (return_accu, myboolean || retboolean);
-
-  return TRUE;
-}
-
-static gboolean
-_gst_array_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  gpointer array;
-
-  array = g_value_get_boxed (handler_return);
-  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
-    g_value_set_boxed (return_accu, array);
-
-  return FALSE;
-}
-
-static gboolean
-_gst_select_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  GstAutoplugSelectResult res;
-
-  res = g_value_get_enum (handler_return);
-  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
-    g_value_set_enum (return_accu, res);
-
-  /* Call the next handler in the chain (if any) when the current callback
-   * returns TRY. This makes it possible to register separate autoplug-select
-   * handlers that implement different TRY/EXPOSE/SKIP strategies.
-   */
-  if (res == GST_AUTOPLUG_SELECT_TRY)
-    return TRUE;
-
-  return FALSE;
-}
-
-static gboolean
-_gst_array_hasvalue_accumulator (GSignalInvocationHint * ihint,
-    GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
-  gpointer array;
-
-  array = g_value_get_boxed (handler_return);
-  if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
-    g_value_set_boxed (return_accu, array);
-
-  if (array != NULL)
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-gst_uri_source_bin_autoplug_continue (GstElement * element, GstPad * pad,
-    GstCaps * caps)
-{
-  /* by default we always continue */
-  return TRUE;
-}
-
-/* Must be called with factories lock! */
-static void
-gst_uri_source_bin_update_factories_list (GstURISourceBin * dec)
-{
-  guint32 cookie;
-
-  cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
-  if (!dec->factories || dec->factories_cookie != cookie) {
-    if (dec->factories)
-      gst_plugin_feature_list_free (dec->factories);
-    dec->factories =
-        gst_element_factory_list_get_elements
-        (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
-    dec->factories =
-        g_list_sort (dec->factories, gst_playback_utils_compare_factories_func);
-    dec->factories_cookie = cookie;
-  }
-}
-
-static GValueArray *
-gst_uri_source_bin_autoplug_factories (GstElement * element, GstPad * pad,
-    GstCaps * caps)
-{
-  GList *list, *tmp;
-  GValueArray *result;
-  GstURISourceBin *dec = GST_URI_SOURCE_BIN_CAST (element);
-
-  GST_DEBUG_OBJECT (element, "finding factories");
-
-  /* return all compatible factories for caps */
-  g_mutex_lock (&dec->factories_lock);
-  gst_uri_source_bin_update_factories_list (dec);
-  list =
-      gst_element_factory_list_filter (dec->factories, caps, GST_PAD_SINK,
-      gst_caps_is_fixed (caps));
-  g_mutex_unlock (&dec->factories_lock);
-
-  result = g_value_array_new (g_list_length (list));
-  for (tmp = list; tmp; tmp = tmp->next) {
-    GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
-    GValue val = { 0, };
-
-    g_value_init (&val, G_TYPE_OBJECT);
-    g_value_set_object (&val, factory);
-    g_value_array_append (result, &val);
-    g_value_unset (&val);
-  }
-  gst_plugin_feature_list_free (list);
-
-  GST_DEBUG_OBJECT (element, "autoplug-factories returns %p", result);
-
-  return result;
-}
-
-static GValueArray *
-gst_uri_source_bin_autoplug_sort (GstElement * element, GstPad * pad,
-    GstCaps * caps, GValueArray * factories)
-{
-  return NULL;
-}
-
-static GstAutoplugSelectResult
-gst_uri_source_bin_autoplug_select (GstElement * element, GstPad * pad,
-    GstCaps * caps, GstElementFactory * factory)
-{
-  GST_DEBUG_OBJECT (element, "default autoplug-select returns TRY");
-
-  /* Try factory. */
-  return GST_AUTOPLUG_SELECT_TRY;
-}
-
-static gboolean
-gst_uri_source_bin_autoplug_query (GstElement * element, GstPad * pad,
-    GstQuery * query)
-{
-  /* No query handled here */
-  return FALSE;
-}
-
 static void
 gst_uri_source_bin_class_init (GstURISourceBinClass * klass)
 {
@@ -520,171 +346,6 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * GstURISourceBin::unknown-type:
-   * @bin: The urisourcebin.
-   * @pad: the new pad containing caps that cannot be resolved to a 'final'.
-   * stream type.
-   * @caps: the #GstCaps of the pad that cannot be resolved.
-   *
-   * This signal is emitted when a pad for which there is no further possible
-   * decoding is added to the urisourcebin.
-   */
-  gst_uri_source_bin_signals[SIGNAL_UNKNOWN_TYPE] =
-      g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, unknown_type),
-      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2,
-      GST_TYPE_PAD, GST_TYPE_CAPS);
-
-  /**
-   * GstURISourceBin::autoplug-continue:
-   * @bin: The urisourcebin.
-   * @pad: The #GstPad.
-   * @caps: The #GstCaps found.
-   *
-   * This signal is emitted whenever urisourcebin finds a new stream. It is
-   * emitted before looking for any elements that can handle that stream.
-   *
-   * >   Invocation of signal handlers stops after the first signal handler
-   * >   returns #FALSE. Signal handlers are invoked in the order they were
-   * >   connected in.
-   *
-   * Returns: #TRUE if you wish urisourcebin to look for elements that can
-   * handle the given @caps. If #FALSE, those caps will be considered as
-   * final and the pad will be exposed as such (see 'pad-added' signal of
-   * #GstElement).
-   */
-  gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] =
-      g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
-          autoplug_continue), _gst_boolean_accumulator, NULL,
-      g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 2, GST_TYPE_PAD,
-      GST_TYPE_CAPS);
-
-  /**
-   * GstURISourceBin::autoplug-factories:
-   * @bin: The urisourcebin.
-   * @pad: The #GstPad.
-   * @caps: The #GstCaps found.
-   *
-   * This function is emitted when an array of possible factories for @caps on
-   * @pad is needed. urisourcebin will by default return an array with all
-   * compatible factories, sorted by rank.
-   *
-   * If this function returns NULL, @pad will be exposed as a final caps.
-   *
-   * If this function returns an empty array, the pad will be considered as
-   * having an unhandled type media type.
-   *
-   * >   Only the signal handler that is connected first will ever by invoked.
-   * >   Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
-   * >   signal, they will never be invoked!
-   *
-   * Returns: a #GValueArray* with a list of factories to try. The factories are
-   * by default tried in the returned order or based on the index returned by
-   * "autoplug-select".
-   */
-  gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_FACTORIES] =
-      g_signal_new ("autoplug-factories", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
-          autoplug_factories), _gst_array_accumulator, NULL,
-      g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 2,
-      GST_TYPE_PAD, GST_TYPE_CAPS);
-
-  /**
-   * GstURISourceBin::autoplug-sort:
-   * @bin: The urisourcebin.
-   * @pad: The #GstPad.
-   * @caps: The #GstCaps.
-   * @factories: A #GValueArray of possible #GstElementFactory to use.
-   *
-   * Once decodebin has found the possible #GstElementFactory objects to try
-   * for @caps on @pad, this signal is emited. The purpose of the signal is for
-   * the application to perform additional sorting or filtering on the element
-   * factory array.
-   *
-   * The callee should copy and modify @factories or return #NULL if the
-   * order should not change.
-   *
-   * >   Invocation of signal handlers stops after one signal handler has
-   * >   returned something else than #NULL. Signal handlers are invoked in
-   * >   the order they were connected in.
-   * >   Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
-   * >   signal, they will never be invoked!
-   *
-   * Returns: A new sorted array of #GstElementFactory objects.
-   *
-   * Since: 0.10.33
-   */
-  gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SORT] =
-      g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_sort),
-      _gst_array_hasvalue_accumulator, NULL,
-      g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 3, GST_TYPE_PAD,
-      GST_TYPE_CAPS, G_TYPE_VALUE_ARRAY | G_SIGNAL_TYPE_STATIC_SCOPE);
-
-  /**
-   * GstURISourceBin::autoplug-select:
-   * @bin: The urisourcebin.
-   * @pad: The #GstPad.
-   * @caps: The #GstCaps.
-   * @factory: A #GstElementFactory to use.
-   *
-   * This signal is emitted once urisourcebin has found all the possible
-   * #GstElementFactory that can be used to handle the given @caps. For each of
-   * those factories, this signal is emitted.
-   *
-   * The signal handler should return a #GST_TYPE_AUTOPLUG_SELECT_RESULT enum
-   * value indicating what decodebin should do next.
-   *
-   * A value of #GST_AUTOPLUG_SELECT_TRY will try to autoplug an element from
-   * @factory.
-   *
-   * A value of #GST_AUTOPLUG_SELECT_EXPOSE will expose @pad without plugging
-   * any element to it.
-   *
-   * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the
-   * next factory.
-   *
-   * >   The signal handler will not be invoked if any of the previously
-   * >   registered signal handlers (if any) return a value other than
-   * >   GST_AUTOPLUG_SELECT_TRY. Which also means that if you return
-   * >   GST_AUTOPLUG_SELECT_TRY from one signal handler, handlers that get
-   * >   registered next (again, if any) can override that decision.
-   *
-   * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required
-   * operation. The default handler will always return
-   * #GST_AUTOPLUG_SELECT_TRY.
-   */
-  gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SELECT] =
-      g_signal_new ("autoplug-select", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
-          autoplug_select), _gst_select_accumulator, NULL,
-      g_cclosure_marshal_generic,
-      GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
-      GST_TYPE_ELEMENT_FACTORY);
-
-  /**
-   * GstDecodeBin::autoplug-query:
-   * @bin: The decodebin.
-   * @child: The child element doing the query
-   * @pad: The #GstPad.
-   * @query: The #GstQuery.
-   *
-   * This signal is emitted whenever an autoplugged element that is
-   * not linked downstream yet and not exposed does a query. It can
-   * be used to tell the element about the downstream supported caps
-   * for example.
-   *
-   * Returns: #TRUE if the query was handled, #FALSE otherwise.
-   */
-  gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
-      g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_query),
-      _gst_boolean_or_accumulator, NULL, g_cclosure_marshal_generic,
-      G_TYPE_BOOLEAN, 3, GST_TYPE_PAD, GST_TYPE_ELEMENT,
-      GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE);
-
-  /**
    * GstURISourceBin::drained:
    *
    * This signal is emitted when the data for the current uri is played.
@@ -695,6 +356,17 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass)
       G_STRUCT_OFFSET (GstURISourceBinClass, drained), NULL, NULL,
       g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
 
+    /**
+   * GstURISourceBin::about-to-finish:
+   *
+   * This signal is emitted when the data for the current uri is played.
+   */
+  gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
+      g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstURISourceBinClass, about_to_finish), NULL, NULL,
+      g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+
   /**
    * GstURISourceBin::source-setup:
    * @bin: the urisourcebin.
@@ -725,15 +397,6 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass)
       GST_DEBUG_FUNCPTR (gst_uri_source_bin_change_state);
 
   gstbin_class->handle_message = GST_DEBUG_FUNCPTR (handle_message);
-
-  klass->autoplug_continue =
-      GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_continue);
-  klass->autoplug_factories =
-      GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_factories);
-  klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_sort);
-  klass->autoplug_select =
-      GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_select);
-  klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_query);
 }
 
 static void
@@ -770,6 +433,8 @@ gst_uri_source_bin_finalize (GObject * obj)
   remove_demuxer (urisrc);
   g_mutex_clear (&urisrc->lock);
   g_mutex_clear (&urisrc->factories_lock);
+  g_mutex_clear (&urisrc->buffering_lock);
+  g_mutex_clear (&urisrc->buffering_post_lock);
   g_free (urisrc->uri);
   if (urisrc->factories)
     gst_plugin_feature_list_free (urisrc->factories);
@@ -863,32 +528,6 @@ gst_uri_source_bin_get_property (GObject * object, guint prop_id,
   }
 }
 
-static void
-do_async_start (GstURISourceBin * dbin)
-{
-  GstMessage *message;
-
-  dbin->async_pending = TRUE;
-
-  message = gst_message_new_async_start (GST_OBJECT_CAST (dbin));
-  GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message);
-}
-
-static void
-do_async_done (GstURISourceBin * dbin)
-{
-  GstMessage *message;
-
-  if (dbin->async_pending) {
-    GST_DEBUG_OBJECT (dbin, "posting ASYNC_DONE");
-    message =
-        gst_message_new_async_done (GST_OBJECT_CAST (dbin),
-        GST_CLOCK_TIME_NONE);
-    GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message);
-
-    dbin->async_pending = FALSE;
-  }
-}
 
 #define DEFAULT_QUEUE_SIZE          (3 * GST_SECOND)
 #define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100)
@@ -1074,6 +713,7 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot)
       slot->is_eos = FALSE;
       BUFFERING_UNLOCK (urisrc);
       res = TRUE;
+      slot->is_eos = FALSE;
       urisrc->pending_pads =
           g_list_remove (urisrc->pending_pads, out_info->demux_src_pad);
     } else {
@@ -1086,6 +726,20 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot)
   return res;
 }
 
+/* Called with lock held */
+static gboolean
+all_slots_are_eos (GstURISourceBin * urisrc)
+{
+  GSList *tmp;
+
+  for (tmp = urisrc->out_slots; tmp; tmp = tmp->next) {
+    OutputSlotInfo *slot = (OutputSlotInfo *) tmp->data;
+    if (slot->is_eos == FALSE)
+      return FALSE;
+  }
+  return TRUE;
+}
+
 static GstPadProbeReturn
 demux_pad_events (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
 {
@@ -1108,36 +762,36 @@ demux_pad_events (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
   switch (GST_EVENT_TYPE (ev)) {
     case GST_EVENT_EOS:
     {
-      GstEvent *event;
-      GstStructure *s;
-      guint32 seqnum = gst_event_get_seqnum (ev);
+      gboolean all_streams_eos;
 
       GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad);
 
-      /* never forward actual EOS to slot */
-      ret = GST_PAD_PROBE_DROP;
-
       if ((urisrc->pending_pads &&
               link_pending_pad_to_output (urisrc, child_info->output_slot))) {
         /* Found a new source pad to give this slot data - no need to send EOS */
         GST_URI_SOURCE_BIN_UNLOCK (urisrc);
+        ret = GST_PAD_PROBE_DROP;
         goto done;
       }
 
       BUFFERING_LOCK (urisrc);
       /* Mark that we fed an EOS to this slot */
       child_info->output_slot->is_eos = TRUE;
+      all_streams_eos = all_slots_are_eos (urisrc);
       BUFFERING_UNLOCK (urisrc);
 
       /* EOS means this element is no longer buffering */
       remove_buffering_msgs (urisrc,
           GST_OBJECT_CAST (child_info->output_slot->queue));
 
-      /* Actually feed a custom EOS event to avoid marking pads as EOSed */
-      s = gst_structure_new_empty ("urisourcebin-custom-eos");
-      event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
-      gst_event_set_seqnum (event, seqnum);
-      gst_pad_send_event (child_info->output_slot->sinkpad, event);
+      /* Mark this custom EOS */
+      gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev), CUSTOM_EOS_QUARK,
+          (gchar *) CUSTOM_EOS_QUARK_DATA, NULL);
+      if (all_streams_eos) {
+        GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH");
+        g_signal_emit (urisrc,
+            gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+      }
     }
       break;
     case GST_EVENT_CAPS:
@@ -1163,6 +817,28 @@ done:
   return ret;
 }
 
+static GstPadProbeReturn
+pre_queue_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+  GstURISourceBin *urisrc = GST_URI_SOURCE_BIN (user_data);
+  GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+  GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
+
+  switch (GST_EVENT_TYPE (ev)) {
+    case GST_EVENT_EOS:
+    {
+      GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad);
+      GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH");
+      g_signal_emit (urisrc,
+          gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+    }
+      break;
+    default:
+      break;
+  }
+  return ret;
+}
+
 /* Called with lock held */
 static OutputSlotInfo *
 get_output_slot (GstURISourceBin * urisrc, gboolean do_download,
@@ -1186,6 +862,7 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download,
         if (cur_caps == NULL || gst_caps_is_equal (caps, cur_caps)) {
           GST_LOG_OBJECT (urisrc, "Found existing slot %p to link to", slot);
           gst_caps_unref (cur_caps);
+          slot->is_eos = FALSE;
           return slot;
         }
         gst_caps_unref (cur_caps);
@@ -1299,11 +976,16 @@ source_pad_event_probe (GstPad * pad, GstPadProbeInfo * info,
 
   GST_LOG_OBJECT (pad, "%s, urisrc %p", GST_EVENT_TYPE_NAME (event), event);
 
-  if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM &&
-      gst_event_has_name (event, "urisourcebin-custom-eos")) {
+  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS &&
+      gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (event),
+          CUSTOM_EOS_QUARK)) {
     OutputSlotInfo *slot;
     GST_DEBUG_OBJECT (pad, "we received EOS");
 
+    /* remove custom-eos */
+    gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event), CUSTOM_EOS_QUARK,
+        NULL, NULL);
+
     GST_URI_SOURCE_BIN_LOCK (urisrc);
 
     slot = g_object_get_data (G_OBJECT (pad), "urisourcebin.slotinfo");
@@ -1313,8 +995,17 @@ source_pad_event_probe (GstPad * pad, GstPadProbeInfo * info,
       guint32 seqnum;
 
       if (slot->linked_info) {
-        /* Do not clear output slot yet. A new input was
-         * connected. We should just drop this EOS */
+        if (slot->is_eos) {
+          /* linked_info is old input which is stil linked without removal */
+          GST_DEBUG_OBJECT (pad, "push actual EOS");
+          seqnum = gst_event_get_seqnum (event);
+          eos = gst_event_new_eos ();
+          gst_event_set_seqnum (eos, seqnum);
+          gst_pad_push_event (slot->srcpad, eos);
+        } else {
+          /* Do not clear output slot yet. A new input was
+           * connected. We should just drop this EOS */
+        }
         GST_URI_SOURCE_BIN_UNLOCK (urisrc);
         return GST_PAD_PROBE_DROP;
       }
@@ -1359,6 +1050,9 @@ create_output_pad (GstURISourceBin * urisrc, GstPad * pad)
   gst_object_unref (pad_tmpl);
   g_free (padname);
 
+  GST_DEBUG_OBJECT (urisrc, "Created output pad %s:%s for pad %s:%s",
+      GST_DEBUG_PAD_NAME (newpad), GST_DEBUG_PAD_NAME (pad));
+
   return newpad;
 }
 
@@ -1377,9 +1071,6 @@ expose_output_pad (GstURISourceBin * urisrc, GstPad * pad)
 
   gst_pad_set_active (pad, TRUE);
   gst_element_add_pad (GST_ELEMENT_CAST (urisrc), pad);
-
-  /* Once we expose a pad, we're no longer async */
-  do_async_done (urisrc);
 }
 
 static void
@@ -1431,8 +1122,10 @@ pad_removed_cb (GstElement * element, GstPad * pad, GstURISourceBin * urisrc)
     GST_LOG_OBJECT (element,
         "Pad %" GST_PTR_FORMAT " was removed without EOS. Sending.", pad);
 
-    s = gst_structure_new_empty ("urisourcebin-custom-eos");
-    event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+    event = gst_event_new_eos ();
+    s = gst_event_writable_structure (event);
+    gst_structure_set (s, "urisourcebin-custom-eos", G_TYPE_BOOLEAN, TRUE,
+        NULL);
     gst_pad_send_event (slot->sinkpad, event);
   } else {
     GST_LOG_OBJECT (urisrc, "Removed pad has no output slot");
@@ -1696,7 +1389,6 @@ post_missing_plugin_error (GstElement * dec, const gchar * element_name)
   GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
       (_("Missing element '%s' - check your GStreamer installation."),
           element_name), (NULL));
-  do_async_done (GST_URI_SOURCE_BIN (dec));
 }
 
 /**
@@ -1777,13 +1469,16 @@ analyse_source (GstURISourceBin * urisrc, gboolean * is_raw,
             gst_object_unref (pad);
             pad = slot->srcpad;
           } else {
-            pad = create_output_pad (urisrc, pad);
+            GstPad *tmppad = create_output_pad (urisrc, pad);
+            gst_object_unref (pad);
+
+            pad = tmppad;
           }
           GST_URI_SOURCE_BIN_UNLOCK (urisrc);
           expose_output_pad (urisrc, pad);
+        } else {
           gst_object_unref (pad);
         }
-        gst_object_unref (pad);
         g_value_reset (&item);
         break;
     }
@@ -1906,7 +1601,6 @@ no_demuxer:
     /* FIXME: Fire the right error */
     GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL),
         ("No demuxer element, check your installation"));
-    do_async_done (urisrc);
     return NULL;
   }
 }
@@ -1981,7 +1675,7 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps)
       gst_query_unref (query);
     }
 
-    GST_DEBUG_OBJECT (urisrc, "check media-type %s, %d", media_type,
+    GST_DEBUG_OBJECT (urisrc, "check media-type %s, do_download:%d", media_type,
         do_download);
 
     GST_URI_SOURCE_BIN_LOCK (urisrc);
@@ -1990,6 +1684,9 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps)
     if (slot == NULL || gst_pad_link (srcpad, slot->sinkpad) != GST_PAD_LINK_OK)
       goto could_not_link;
 
+    gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+        pre_queue_event_probe, urisrc, NULL);
+
     expose_output_pad (urisrc, slot->srcpad);
     GST_URI_SOURCE_BIN_UNLOCK (urisrc);
   }
@@ -2006,7 +1703,6 @@ no_demuxer_sink:
   {
     GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
         (NULL), ("Adaptive demuxer element has no 'sink' pad"));
-    do_async_done (urisrc);
     return;
   }
 could_not_link:
@@ -2014,7 +1710,6 @@ could_not_link:
     GST_URI_SOURCE_BIN_UNLOCK (urisrc);
     GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
         (NULL), ("Can't link typefind to adaptive demuxer element"));
-    do_async_done (urisrc);
     return;
   }
 }
@@ -2083,7 +1778,6 @@ no_typefind:
     post_missing_plugin_error (GST_ELEMENT_CAST (urisrc), "typefind");
     GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL),
         ("No typefind element, check your installation"));
-    do_async_done (urisrc);
     return FALSE;
   }
 could_not_link:
@@ -2091,7 +1785,6 @@ could_not_link:
     GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
         (NULL), ("Can't link source to typefind element"));
     gst_bin_remove (GST_BIN_CAST (urisrc), typefind);
-    do_async_done (urisrc);
     return FALSE;
   }
 }
@@ -2259,7 +1952,6 @@ setup_source (GstURISourceBin * urisrc)
     /* source provides raw data, we added the pads and we can now signal a
      * no_more pads because we are done. */
     gst_element_no_more_pads (GST_ELEMENT_CAST (urisrc));
-    do_async_done (urisrc);
     return TRUE;
   }
   if (!have_out && !is_dynamic) {
@@ -2893,12 +2585,6 @@ done:
   return res;
 }
 
-static void
-sync_slot_queue (OutputSlotInfo * slot)
-{
-  gst_element_sync_state_with_parent (slot->queue);
-}
-
 static GstStateChangeReturn
 gst_uri_source_bin_change_state (GstElement * element,
     GstStateChange transition)
@@ -2908,7 +2594,9 @@ gst_uri_source_bin_change_state (GstElement * element,
 
   switch (transition) {
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      do_async_start (urisrc);
+      GST_DEBUG ("ready to paused");
+      if (!setup_source (urisrc))
+        goto source_failed;
       break;
     default:
       break;
@@ -2917,44 +2605,14 @@ gst_uri_source_bin_change_state (GstElement * element,
   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
   if (ret == GST_STATE_CHANGE_FAILURE)
     goto setup_failed;
-  else if (ret == GST_STATE_CHANGE_NO_PREROLL)
-    do_async_done (urisrc);
 
   switch (transition) {
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      GST_DEBUG ("ready to paused");
-      if (!setup_source (urisrc))
-        goto source_failed;
-
-      ret = GST_STATE_CHANGE_ASYNC;
-
-      /* And now sync the states of everything we added */
-      g_slist_foreach (urisrc->out_slots, (GFunc) sync_slot_queue, NULL);
-      if (urisrc->typefinds) {
-        GList *iter;
-        for (iter = urisrc->typefinds; iter; iter = iter->next) {
-          GstElement *typefind = iter->data;
-          ret = gst_element_set_state (typefind, GST_STATE_PAUSED);
-          if (ret == GST_STATE_CHANGE_FAILURE)
-            goto setup_failed;
-        }
-      }
-      if (urisrc->source)
-        ret = gst_element_set_state (urisrc->source, GST_STATE_PAUSED);
-      if (ret == GST_STATE_CHANGE_FAILURE)
-        goto setup_failed;
-
-      if (ret == GST_STATE_CHANGE_SUCCESS)
-        ret = GST_STATE_CHANGE_ASYNC;
-      else if (ret == GST_STATE_CHANGE_NO_PREROLL)
-        do_async_done (urisrc);
-
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       GST_DEBUG ("paused to ready");
       remove_demuxer (urisrc);
       remove_source (urisrc);
-      do_async_done (urisrc);
       g_list_free_full (urisrc->buffering_status,
           (GDestroyNotify) gst_message_unref);
       urisrc->buffering_status = NULL;
@@ -2973,13 +2631,11 @@ gst_uri_source_bin_change_state (GstElement * element,
   /* ERRORS */
 source_failed:
   {
-    do_async_done (urisrc);
     return GST_STATE_CHANGE_FAILURE;
   }
 setup_failed:
   {
     /* clean up leftover groups */
-    do_async_done (urisrc);
     return GST_STATE_CHANGE_FAILURE;
   }
 }