bin: emit deep-element-{added,removed} for children of newly-added/removed bin
authorTim-Philipp Müller <tim@centricular.com>
Sun, 15 May 2016 13:15:51 +0000 (14:15 +0100)
committerTim-Philipp Müller <tim@centricular.com>
Mon, 16 May 2016 08:10:09 +0000 (09:10 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=578933

gst/gstbin.c
tests/check/gst/gstbin.c

index c53f6e0..c3e6113 100644 (file)
@@ -1112,6 +1112,53 @@ unlink_pads (const GValue * item, gpointer user_data)
   }
 }
 
+static void
+bin_deep_iterator_foreach (const GValue * item, gpointer user_data)
+{
+  GQueue *queue = user_data;
+
+  g_queue_push_tail (queue, g_value_dup_object (item));
+}
+
+static void
+gst_bin_do_deep_add_remove (GstBin * bin, gint sig_id, const gchar * sig_name,
+    GstElement * element)
+{
+  g_signal_emit (bin, sig_id, 0, bin, element);
+
+  /* When removing a bin, emit deep-element-* for everything in the bin too */
+  if (GST_IS_BIN (element)) {
+    GstIterator *it;
+    GstIteratorResult ires;
+    GQueue elements = G_QUEUE_INIT;
+
+    GST_LOG_OBJECT (bin, "Recursing into bin %" GST_PTR_FORMAT " for %s",
+        element, sig_name);
+    it = gst_bin_iterate_recurse (GST_BIN_CAST (element));
+    do {
+      ires = gst_iterator_foreach (it, bin_deep_iterator_foreach, &elements);
+      if (ires != GST_ITERATOR_DONE) {
+        g_queue_foreach (&elements, (GFunc) g_object_unref, NULL);
+        g_queue_clear (&elements);
+      }
+    } while (ires == GST_ITERATOR_RESYNC);
+    if (ires != GST_ITERATOR_ERROR) {
+      GstElement *e;
+
+      while ((e = g_queue_pop_head (&elements))) {
+        GstObject *parent = gst_object_get_parent (GST_OBJECT_CAST (e));
+
+        GST_LOG_OBJECT (bin, "calling %s for element %" GST_PTR_FORMAT
+            " in bin %" GST_PTR_FORMAT, sig_name, e, parent);
+        g_signal_emit (bin, sig_id, 0, parent, e);
+        gst_object_unref (parent);
+        g_object_unref (e);
+      }
+    }
+    gst_iterator_free (it);
+  }
+}
+
 /* vmethod that adds an element to a bin
  *
  * MT safe
@@ -1327,7 +1374,8 @@ no_state_recalc:
   gst_child_proxy_child_added ((GstChildProxy *) bin, (GObject *) element,
       elem_name);
 
-  g_signal_emit (bin, gst_bin_signals[DEEP_ELEMENT_ADDED], 0, bin, element);
+  gst_bin_do_deep_add_remove (bin, gst_bin_signals[DEEP_ELEMENT_ADDED],
+      "deep-element-added", element);
 
   g_free (elem_name);
 
@@ -1710,7 +1758,8 @@ no_state_recalc:
   gst_child_proxy_child_removed ((GstChildProxy *) bin, (GObject *) element,
       elem_name);
 
-  g_signal_emit (bin, gst_bin_signals[DEEP_ELEMENT_REMOVED], 0, bin, element);
+  gst_bin_do_deep_add_remove (bin, gst_bin_signals[DEEP_ELEMENT_REMOVED],
+      "deep-element-removed", element);
 
   g_free (elem_name);
   /* element is really out of our control now */
index 9a974eb..9ac953f 100644 (file)
@@ -1571,8 +1571,6 @@ add_cb (GstBin * pipeline, GstBin * bin, GstElement * element, GList ** list)
 static void
 remove_cb (GstBin * pipeline, GstBin * bin, GstElement * element, GList ** list)
 {
-  fail_unless (GST_OBJECT_PARENT (element) == NULL);
-
   *list = g_list_prepend (*list, element);
 }
 
@@ -1597,7 +1595,7 @@ GST_START_TEST (test_deep_added_removed)
   gst_bin_remove (GST_BIN (pipe), e);
   fail_unless (element_was_removed (e));
 
-  /* let's try with a deeper hierarchy */
+  /* let's try with a deeper hierarchy, construct it from top-level down */
   bin0 = gst_bin_new (NULL);
   gst_bin_add (GST_BIN (pipe), bin0);
   bin1 = gst_bin_new (NULL);
@@ -1610,11 +1608,34 @@ GST_START_TEST (test_deep_added_removed)
   fail_unless (added == NULL);
   fail_unless (removed == NULL);
 
+  gst_object_ref (e);           /* keep e alive */
   gst_bin_remove (GST_BIN (bin1), e);
   fail_unless (element_was_removed (e));
   fail_unless (added == NULL);
   fail_unless (removed == NULL);
 
+  /* now add existing bin hierarchy to pipeline (first remove it so we can re-add it) */
+  gst_object_ref (bin0);        /* keep bin0 alive */
+  gst_bin_remove (GST_BIN (pipe), bin0);
+  fail_unless (element_was_removed (bin0));
+  fail_unless (element_was_removed (bin1));
+  fail_unless (added == NULL);
+  fail_unless (removed == NULL);
+
+  /* re-adding element to removed bin should not trigger our callbacks */
+  gst_bin_add (GST_BIN (bin1), e);
+  fail_unless (added == NULL);
+  fail_unless (removed == NULL);
+
+  gst_bin_add (GST_BIN (pipe), bin0);
+  fail_unless (element_was_added (bin0));
+  fail_unless (element_was_added (bin1));
+  fail_unless (element_was_added (e));
+  fail_unless (added == NULL);
+  fail_unless (removed == NULL);
+  gst_object_unref (bin0);
+  gst_object_unref (e);
+
   /* disconnect signals, unref will trigger remove callbacks otherwise */
   g_signal_handler_disconnect (pipe, id_added);
   g_signal_handler_disconnect (pipe, id_removed);