decodebin2: Prune old groups before switching to the new one
authorYouness Alaoui <youness.alaoui@collabora.co.uk>
Tue, 13 Sep 2011 23:14:10 +0000 (23:14 +0000)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 19 Jan 2012 08:04:52 +0000 (09:04 +0100)
In order to allow for proper functionality when a decoder only supports
one instance at a time (dsp), we must block the demuxer pads when they
get created if they are not part of the active group, preventing buffers
from being sent to the decoder (and initializing it through setcaps),
then after we switch to a new group, we unblock the demuxer pads for
the active groups. In the callback for the unblock, we prune the old
groups, making sure the previous decoder instance is destroyed before
we push a buffer to the new instance.

gst/playback/gstdecodebin2.c

index 8bcadde..78b6291 100644 (file)
@@ -446,6 +446,7 @@ static gboolean gst_decode_chain_is_complete (GstDecodeChain * chain);
 static gboolean gst_decode_chain_expose (GstDecodeChain * chain,
     GList ** endpads, gboolean * missing_plugin);
 static gboolean gst_decode_chain_is_drained (GstDecodeChain * chain);
+static void gst_decode_chain_prune (GstDecodeChain * chain);
 static gboolean gst_decode_group_is_complete (GstDecodeGroup * group);
 static GstPad *gst_decode_group_control_demuxer_pad (GstDecodeGroup * group,
     GstPad * pad);
@@ -2320,6 +2321,16 @@ pad_event_cb (GstPad * pad, GstEvent * event, gpointer data)
 }
 
 static void
+demuxer_pad_blocked_cb (GstPad * pad, gboolean blocked, GstDecodeChain * chain)
+{
+  GstDecodeBin *dbin;
+
+  dbin = chain->dbin;
+  if (!blocked)
+    gst_decode_chain_prune (dbin->decode_chain);
+}
+
+static void
 pad_added_cb (GstElement * element, GstPad * pad, GstDecodeChain * chain)
 {
   GstCaps *caps;
@@ -2335,6 +2346,27 @@ pad_added_cb (GstElement * element, GstPad * pad, GstDecodeChain * chain)
     gst_caps_unref (caps);
 
   EXPOSE_LOCK (dbin);
+  CHAIN_MUTEX_LOCK (chain);
+  if (chain->demuxer &&
+      ((GstDecodeElement *) chain->elements->data)->element == element) {
+    GList *l;
+
+    for (l = chain->next_groups; l; l = l->next) {
+      GstDecodeGroup *group = l->data;
+      GList *l2;
+
+      for (l2 = group->children; l2; l2 = l2->next) {
+        GstDecodeChain *child_chain = l2->data;
+
+        if (!gst_pad_is_blocked (child_chain->pad)) {
+          GST_DEBUG_OBJECT (pad, "blocking next group's pad %p", pad);
+          gst_pad_set_blocked_async (child_chain->pad, TRUE,
+              (GstPadBlockCallback) demuxer_pad_blocked_cb, chain);
+        }
+      }
+    }
+  }
+  CHAIN_MUTEX_UNLOCK (chain);
   if (gst_decode_chain_is_complete (dbin->decode_chain)) {
     GST_LOG_OBJECT (dbin,
         "That was the last dynamic object, now attempting to expose the group");
@@ -2867,6 +2899,76 @@ gst_decode_group_hide (GstDecodeGroup * group)
   gst_decode_group_free_internal (group, TRUE);
 }
 
+static void
+gst_decode_group_prune (GstDecodeGroup * group)
+{
+  GList *l;
+
+  GST_DEBUG_OBJECT (group->dbin, "Pruning group %p", group);
+
+  for (l = group->children; l; l = l->next) {
+    GstDecodeChain *chain = (GstDecodeChain *) l->data;
+
+    gst_decode_chain_prune (chain);
+  }
+
+  GST_DEBUG_OBJECT (group->dbin, "Pruned group %p", group);
+}
+
+static void
+gst_decode_chain_prune (GstDecodeChain * chain)
+{
+  GList *l;
+
+  CHAIN_MUTEX_LOCK (chain);
+
+  GST_DEBUG_OBJECT (chain->dbin, "Pruning chain %p", chain);
+
+  if (chain->active_group)
+    gst_decode_group_prune (chain->active_group);
+
+  for (l = chain->next_groups; l; l = l->next) {
+    gst_decode_group_prune ((GstDecodeGroup *) l->data);
+  }
+
+  for (l = chain->old_groups; l; l = l->next) {
+    GstDecodeGroup *group = l->data;
+
+    /* This calls set_state(NULL) on the old elements and we're
+     * the streaming thread but this is one of the few cases
+     * when this is possible. It's guaranteed at this point
+     * that *this* streaming thread is not inside the elements
+     * currently, because it's *here* now or a different streaming
+     * thread is used for the elements.
+     */
+    gst_decode_group_free (group);
+  }
+  g_list_free (chain->old_groups);
+  chain->old_groups = NULL;
+
+  GST_DEBUG_OBJECT (chain->dbin, "Pruned chain %p", chain);
+  CHAIN_MUTEX_UNLOCK (chain);
+}
+
+static void
+unblock_demuxer_pads (GstDecodeChain * chain)
+{
+  GList *l;
+
+  GST_DEBUG_OBJECT (chain->dbin, "Unblocking demuxer pad for chain %p", chain);
+
+  if (gst_pad_is_blocked (chain->pad))
+    gst_pad_set_blocked_async (chain->pad, FALSE,
+        (GstPadBlockCallback) demuxer_pad_blocked_cb, chain);
+  if (chain->active_group) {
+    for (l = chain->active_group->children; l; l = l->next) {
+      GstDecodeChain *child_chain = l->data;
+
+      unblock_demuxer_pads (child_chain);
+    }
+  }
+}
+
 /* configure queue sizes, this depends on the buffering method and if we are
  * playing or prerolling. */
 static void
@@ -3241,6 +3343,7 @@ gst_decode_pad_handle_eos (GstDecodePad * pad)
     EXPOSE_LOCK (dbin);
     if (gst_decode_chain_is_complete (dbin->decode_chain))
       gst_decode_bin_expose (dbin);
+    unblock_demuxer_pads (dbin->decode_chain);
     EXPOSE_UNLOCK (dbin);
   }