diff --git a/gst/gstbin.c b/gst/gstbin.c index c53f6e0bbe..c3e61138e8 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -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 */ diff --git a/tests/check/gst/gstbin.c b/tests/check/gst/gstbin.c index 9a974eb698..9ac953fbb4 100644 --- a/tests/check/gst/gstbin.c +++ b/tests/check/gst/gstbin.c @@ -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);