bin: add "deep-element-added" and "deep-element-removed" signals
authorTim-Philipp Müller <tim@centricular.com>
Sat, 14 May 2016 09:55:53 +0000 (10:55 +0100)
committerTim-Philipp Müller <tim@centricular.com>
Mon, 16 May 2016 08:10:09 +0000 (09:10 +0100)
This means applications and bin sub-classes can easily track when
a new child element is added to the pipeline sub-hierarchy or
removed.

Currently doesn't signal deep added/removed for elements inside
a bin if a bin is added/removed.

https://bugzilla.gnome.org/show_bug.cgi?id=578933

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

index 7efe57f..c53f6e0 100644 (file)
@@ -225,6 +225,10 @@ static void bin_do_eos (GstBin * bin);
 
 static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
 static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
+static void gst_bin_deep_element_added_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * element);
+static void gst_bin_deep_element_removed_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * element);
 static void gst_bin_update_context (GstBin * bin, GstContext * context);
 static void gst_bin_update_context_unlocked (GstBin * bin,
     GstContext * context);
@@ -260,6 +264,8 @@ enum
   ELEMENT_ADDED,
   ELEMENT_REMOVED,
   DO_LATENCY,
+  DEEP_ELEMENT_ADDED,
+  DEEP_ELEMENT_REMOVED,
   LAST_SIGNAL
 };
 
@@ -401,6 +407,36 @@ gst_bin_class_init (GstBinClass * klass)
       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
   /**
+   * GstBin::deep-element-added:
+   * @bin: the #GstBin
+   * @sub_bin: the #GstBin the element was added to
+   * @element: the #GstElement that was added to @sub_bin
+   *
+   * Will be emitted after the element was added to sub_bin.
+   *
+   * Since: 1.10
+   */
+  gst_bin_signals[DEEP_ELEMENT_ADDED] =
+      g_signal_new ("deep-element-added", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, deep_element_added),
+      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_BIN,
+      GST_TYPE_ELEMENT);
+  /**
+   * GstBin::deep-element-removed:
+   * @bin: the #GstBin
+   * @sub_bin: the #GstBin the element was removed from
+   * @element: the #GstElement that was removed from @sub_bin
+   *
+   * Will be emitted after the element was removed from sub_bin.
+   *
+   * Since: 1.10
+   */
+  gst_bin_signals[DEEP_ELEMENT_REMOVED] =
+      g_signal_new ("deep-element-removed", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, deep_element_removed),
+      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_BIN,
+      GST_TYPE_ELEMENT);
+  /**
    * GstBin::do-latency:
    * @bin: the #GstBin
    *
@@ -467,6 +503,9 @@ gst_bin_class_init (GstBinClass * klass)
   klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
   klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
 
+  klass->deep_element_added = gst_bin_deep_element_added_func;
+  klass->deep_element_removed = gst_bin_deep_element_removed_func;
+
   klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);
 }
 
@@ -1288,6 +1327,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);
+
   g_free (elem_name);
 
   return TRUE;
@@ -1317,6 +1358,52 @@ had_parent:
   }
 }
 
+/* signal vfunc, will be called when a new element was added */
+static void
+gst_bin_deep_element_added_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * child)
+{
+  GstBin *parent_bin;
+
+  parent_bin = (GstBin *) gst_object_get_parent (GST_OBJECT_CAST (bin));
+  if (parent_bin == NULL) {
+    GST_LOG_OBJECT (bin, "no parent, reached top-level");
+    return;
+  }
+
+  GST_LOG_OBJECT (parent_bin, "emitting deep-element-added for element "
+      "%" GST_PTR_FORMAT " which has just been added to %" GST_PTR_FORMAT,
+      sub_bin, child);
+
+  g_signal_emit (parent_bin, gst_bin_signals[DEEP_ELEMENT_ADDED], 0, sub_bin,
+      child);
+
+  gst_object_unref (parent_bin);
+}
+
+/* signal vfunc, will be called when an element was removed */
+static void
+gst_bin_deep_element_removed_func (GstBin * bin, GstBin * sub_bin,
+    GstElement * child)
+{
+  GstBin *parent_bin;
+
+  parent_bin = (GstBin *) gst_object_get_parent (GST_OBJECT_CAST (bin));
+  if (parent_bin == NULL) {
+    GST_LOG_OBJECT (bin, "no parent, reached top-level");
+    return;
+  }
+
+  GST_LOG_OBJECT (parent_bin, "emitting deep-element-removed for element "
+      "%" GST_PTR_FORMAT " which has just been removed from %" GST_PTR_FORMAT,
+      sub_bin, child);
+
+  g_signal_emit (parent_bin, gst_bin_signals[DEEP_ELEMENT_REMOVED], 0, sub_bin,
+      child);
+
+  gst_object_unref (parent_bin);
+}
+
 /**
  * gst_bin_add:
  * @bin: a #GstBin
@@ -1623,6 +1710,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);
+
   g_free (elem_name);
   /* element is really out of our control now */
   gst_object_unref (element);
index b67e24f..809bb79 100644 (file)
@@ -147,6 +147,12 @@ struct _GstBin {
  * The @handle_message method can be overridden to implement custom
  * message handling.  @handle_message takes ownership of the message, just like
  * #gst_element_post_message.
+ *
+ * The @element_added_deep vfunc will be called when a new element has been
+ * added to any bin inside this bin, so it will also be called if a new child
+ * was added to a sub-bin of this bin. #GstBin implementations that override
+ * this message should chain up to the parent class implementation so the
+ * element-added-deep signal is emitted on all parents.
  */
 struct _GstBinClass {
   GstElementClass parent_class;
@@ -169,8 +175,13 @@ struct _GstBinClass {
   /* signal */
   gboolean     (*do_latency)           (GstBin *bin);
 
+  /*< public >*/
+  /* signal */
+  void          (*deep_element_added)   (GstBin *bin, GstBin *sub_bin, GstElement *child);
+  void          (*deep_element_removed) (GstBin *bin, GstBin *sub_bin, GstElement *child);
+
   /*< private >*/
-  gpointer _gst_reserved[GST_PADDING];
+  gpointer _gst_reserved[GST_PADDING-2];
 };
 
 GType          gst_bin_get_type                (void);
index 35c3ff6..9a974eb 100644 (file)
@@ -1545,7 +1545,84 @@ GST_START_TEST (test_duration_unknown_overrides)
 
 GST_END_TEST;
 
+static gboolean
+element_in_list (GList ** list, GstElement * element)
+{
+  GList *l = g_list_find (*list, element);
+
+  if (l == NULL)
+    return FALSE;
+
+  *list = g_list_delete_link (*list, l);
+  return TRUE;
+}
+
+#define element_was_added(e) element_in_list(&added,e)
+#define element_was_removed(e) element_in_list(&removed,e)
+
+static void
+add_cb (GstBin * pipeline, GstBin * bin, GstElement * element, GList ** list)
+{
+  fail_unless (GST_OBJECT_PARENT (element) == GST_OBJECT_CAST (bin));
+
+  *list = g_list_prepend (*list, element);
+}
 
+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);
+}
+
+GST_START_TEST (test_deep_added_removed)
+{
+  GstElement *pipe, *e, *bin0, *bin1;
+  gulong id_removed, id_added;
+  GList *removed = NULL;
+  GList *added = NULL;
+
+  pipe = gst_pipeline_new (NULL);
+
+  id_added = g_signal_connect (pipe, "deep-element-added",
+      G_CALLBACK (add_cb), &added);
+  id_removed = g_signal_connect (pipe, "deep-element-removed",
+      G_CALLBACK (remove_cb), &removed);
+
+  /* simple add/remove */
+  e = gst_element_factory_make ("identity", NULL);
+  gst_bin_add (GST_BIN (pipe), e);
+  fail_unless (element_was_added (e));
+  gst_bin_remove (GST_BIN (pipe), e);
+  fail_unless (element_was_removed (e));
+
+  /* let's try with a deeper hierarchy */
+  bin0 = gst_bin_new (NULL);
+  gst_bin_add (GST_BIN (pipe), bin0);
+  bin1 = gst_bin_new (NULL);
+  gst_bin_add (GST_BIN (bin0), bin1);
+  e = gst_element_factory_make ("identity", NULL);
+  gst_bin_add (GST_BIN (bin1), e);
+  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_bin_remove (GST_BIN (bin1), e);
+  fail_unless (element_was_removed (e));
+  fail_unless (added == NULL);
+  fail_unless (removed == NULL);
+
+  /* disconnect signals, unref will trigger remove callbacks otherwise */
+  g_signal_handler_disconnect (pipe, id_added);
+  g_signal_handler_disconnect (pipe, id_removed);
+
+  gst_object_unref (pipe);
+}
+
+GST_END_TEST;
 
 static Suite *
 gst_bin_suite (void)
@@ -1576,6 +1653,7 @@ gst_bin_suite (void)
   tcase_add_test (tc_chain, test_state_change_skip);
   tcase_add_test (tc_chain, test_duration_is_max);
   tcase_add_test (tc_chain, test_duration_unknown_overrides);
+  tcase_add_test (tc_chain, test_deep_added_removed);
 
   /* fails on OSX build bot for some reason, and is a bit silly anyway */
   if (0)