diff --git a/ChangeLog b/ChangeLog index 9fdfe5341e..290471f7e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,49 @@ +2006-07-11 Edward Hervey + + * gst/gstbin.c: (activate_pads), + (iterator_activate_fold_with_resync), (gst_bin_src_pads_activate), + (gst_bin_change_state_func): + (de)activate src pads before calling state_change on the childs. + This is to avoid the case where a src ghostpad is blocked (holding the + stream lock), which would block the deactivation of the ghostpad's + target pad. + * gst/gstghostpad.c: (gst_proxy_pad_do_query_type), + (gst_proxy_pad_do_event), (gst_proxy_pad_do_query), + (gst_proxy_pad_do_internal_link), (gst_proxy_pad_do_bufferalloc), + (gst_proxy_pad_do_chain), (gst_proxy_pad_do_getrange), + (gst_proxy_pad_do_checkgetrange), (gst_proxy_pad_do_getcaps), + (gst_proxy_pad_do_acceptcaps), (gst_proxy_pad_do_fixatecaps), + (gst_proxy_pad_do_setcaps), (gst_proxy_pad_set_target_unlocked), + (gst_proxy_pad_set_target), (gst_proxy_pad_get_internal), + (gst_proxy_pad_dispose), (gst_proxy_pad_init), + (gst_ghost_pad_parent_set), (gst_ghost_pad_parent_unset), + (gst_ghost_pad_class_init), + (gst_ghost_pad_internal_do_activate_push), + (gst_ghost_pad_internal_do_activate_pull), + (gst_ghost_pad_do_activate_push), (gst_ghost_pad_do_activate_pull), + (gst_ghost_pad_do_link), (gst_ghost_pad_do_unlink), + (gst_ghost_pad_dispose), (gst_ghost_pad_new_no_target), + (gst_ghost_pad_new), (gst_ghost_pad_set_target): + GhostPads now create their internal GstProxyPad at creation (and not + when they're linked, as it was being done previously). + The internal and target pads are linked straight away. + The data will also travel through the other pad in order to make + pad blocking and probes non-hackish (the probe/block now really happens + on the GhostPad and not on the target). + * gst/gstpad.c: (gst_pad_set_blocked_async), + (gst_pad_link_prepare), (gst_pad_push_event): + Remove previous ghostpad cruft. + * gst/gstutils.c: (gst_pad_add_data_probe), + (gst_pad_add_event_probe), (gst_pad_add_buffer_probe), + (gst_pad_remove_data_probe), (gst_pad_remove_event_probe), + (gst_pad_remove_buffer_probe): + Remove previous ghost pad cruft. + Added more detailed debug statements. + * tests/check/gst/gstghostpad.c: (GST_START_TEST): + Fix the testsuite for refcounting changes. + The comments about who has references were correct, but the refcount + being checked wasn't the same (!?!). + 2006-07-10 Stefan Kost * docs/gst/gstreamer-sections.txt: diff --git a/gst/gstbin.c b/gst/gstbin.c index 32c0d127ec..e7e4a4f728 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -1762,6 +1762,87 @@ done: return ret; } +/* gst_iterator_fold functions for pads_activate + * Note how we don't stop the iterator when we fail an activation. This is + * probably a FIXME since when one pad activation fails, we don't want to + * continue our state change. */ +static gboolean +activate_pads (GstPad * pad, GValue * ret, gboolean * active) +{ + if (!gst_pad_set_active (pad, *active)) + g_value_set_boolean (ret, FALSE); + else if (!*active) + gst_pad_set_caps (pad, NULL); + + /* unref the object that was reffed for us by _fold */ + gst_object_unref (pad); + return TRUE; +} + +/* returns false on error or early cutout (will never happen because the fold + * function always returns TRUE, see FIXME above) of the fold, true if all + * pads in @iter were (de)activated successfully. */ +static gboolean +iterator_activate_fold_with_resync (GstIterator * iter, gpointer user_data) +{ + GstIteratorResult ires; + GValue ret = { 0 }; + + /* no need to unset this later, it's just a boolean */ + g_value_init (&ret, G_TYPE_BOOLEAN); + g_value_set_boolean (&ret, TRUE); + + while (1) { + ires = gst_iterator_fold (iter, (GstIteratorFoldFunction) activate_pads, + &ret, user_data); + switch (ires) { + case GST_ITERATOR_RESYNC: + /* need to reset the result again */ + g_value_set_boolean (&ret, TRUE); + gst_iterator_resync (iter); + break; + case GST_ITERATOR_DONE: + /* all pads iterated, return collected value */ + goto done; + default: + /* iterator returned _ERROR or premature end with _OK, + * mark an error and exit */ + g_value_set_boolean (&ret, FALSE); + goto done; + } + } +done: + /* return collected value */ + return g_value_get_boolean (&ret); +} + +/* is called with STATE_LOCK + */ +static gboolean +gst_bin_src_pads_activate (GstBin * bin, gboolean active) +{ + GstIterator *iter; + gboolean fold_ok; + + GST_DEBUG_OBJECT (bin, "src_pads_activate with active %d", active); + + iter = gst_element_iterate_src_pads ((GstElement *) bin); + fold_ok = iterator_activate_fold_with_resync (iter, &active); + gst_iterator_free (iter); + if (G_UNLIKELY (!fold_ok)) + goto failed; + + GST_DEBUG_OBJECT (bin, "pads_activate successful"); + + return TRUE; + + /* ERRORS */ +failed: + { + GST_DEBUG_OBJECT (bin, "source pads_activate failed"); + return FALSE; + } +} static GstStateChangeReturn gst_bin_change_state_func (GstElement * element, GstStateChange transition) { @@ -1791,6 +1872,9 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (element, "clearing EOS elements"); bin_remove_messages (bin, NULL, GST_MESSAGE_EOS); GST_OBJECT_UNLOCK (bin); + if (current == GST_STATE_READY) + if (!(gst_bin_src_pads_activate (bin, TRUE))) + goto activate_failure; break; case GST_STATE_READY: /* Clear message list on next READY */ @@ -1798,6 +1882,14 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (element, "clearing all cached messages"); bin_remove_messages (bin, NULL, GST_MESSAGE_ANY); GST_OBJECT_UNLOCK (bin); + if (current == GST_STATE_PAUSED) + if (!(gst_bin_src_pads_activate (bin, FALSE))) + goto activate_failure; + break; + case GST_STATE_NULL: + if (current == GST_STATE_READY) + if (!(gst_bin_src_pads_activate (bin, FALSE))) + goto activate_failure; break; default: break; @@ -1897,6 +1989,11 @@ done: gst_element_state_get_name (GST_STATE (element)), ret); return ret; + +activate_failure: + GST_CAT_WARNING_OBJECT (GST_CAT_STATES, element, + "failure (de)activating src pads"); + return GST_STATE_CHANGE_FAILURE; } /* diff --git a/gst/gstghostpad.c b/gst/gstghostpad.c index 89ab3208ad..fc261eb0e1 100644 --- a/gst/gstghostpad.c +++ b/gst/gstghostpad.c @@ -2,6 +2,7 @@ * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * 2005 Andy Wingo + * 2006 Edward Hervey * * gstghostpad.c: Proxy pads * @@ -51,6 +52,7 @@ #define GST_PROXY_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PROXY_PAD, GstProxyPad)) #define GST_PROXY_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PROXY_PAD, GstProxyPadClass)) #define GST_PROXY_PAD_TARGET(pad) (GST_PROXY_PAD (pad)->target) +#define GST_PROXY_PAD_INTERNAL(pad) (GST_PROXY_PAD (pad)->internal) typedef struct _GstProxyPad GstProxyPad; @@ -67,9 +69,7 @@ struct _GstProxyPad /* with PROXY_LOCK */ GMutex *proxy_lock; GstPad *target; - - /*< private > */ - gpointer _gst_reserved[1]; + GstPad *internal; }; struct _GstProxyPadClass @@ -84,6 +84,7 @@ struct _GstProxyPadClass G_DEFINE_TYPE (GstProxyPad, gst_proxy_pad, GST_TYPE_PAD); static GstPad *gst_proxy_pad_get_target (GstPad * pad); +static GstPad *gst_proxy_pad_get_internal (GstPad * pad); static void gst_proxy_pad_dispose (GObject * object); static void gst_proxy_pad_finalize (GObject * object); @@ -116,26 +117,25 @@ const GstQueryType * gst_proxy_pad_do_query_type (GstPad * pad) { GstPad *target = gst_proxy_pad_get_target (pad); - const GstQueryType *res; - - g_return_val_if_fail (target != NULL, NULL); - - res = gst_pad_get_query_types (target); - gst_object_unref (target); + const GstQueryType *res = NULL; + if (target) { + res = gst_pad_get_query_types (target); + gst_object_unref (target); + } return res; } static gboolean gst_proxy_pad_do_event (GstPad * pad, GstEvent * event) { - gboolean res; - GstPad *target = gst_proxy_pad_get_target (pad); + gboolean res = FALSE; + GstPad *internal = gst_proxy_pad_get_internal (pad); - g_return_val_if_fail (target != NULL, FALSE); + g_return_val_if_fail (internal != NULL, FALSE); - res = gst_pad_send_event (target, event); - gst_object_unref (target); + res = gst_pad_push_event (internal, event); + gst_object_unref (internal); return res; } @@ -143,13 +143,13 @@ gst_proxy_pad_do_event (GstPad * pad, GstEvent * event) static gboolean gst_proxy_pad_do_query (GstPad * pad, GstQuery * query) { - gboolean res; + gboolean res = FALSE; GstPad *target = gst_proxy_pad_get_target (pad); - g_return_val_if_fail (target != NULL, FALSE); - - res = gst_pad_query (target, query); - gst_object_unref (target); + if (target) { + res = gst_pad_query (target, query); + gst_object_unref (target); + } return res; } @@ -158,12 +158,12 @@ static GList * gst_proxy_pad_do_internal_link (GstPad * pad) { GstPad *target = gst_proxy_pad_get_target (pad); - GList *res; + GList *res = NULL; - g_return_val_if_fail (target != NULL, NULL); - - res = gst_pad_get_internal_links (target); - gst_object_unref (target); + if (target) { + res = gst_pad_get_internal_links (target); + gst_object_unref (target); + } return res; } @@ -173,23 +173,12 @@ gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) { GstFlowReturn result; - GstPad *target = gst_proxy_pad_get_target (pad); - GstPad *peer; + GstPad *internal = gst_proxy_pad_get_internal (pad); - g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED); + g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED); - peer = gst_pad_get_peer (target); - if (peer) { - GST_DEBUG ("buffer alloc on %s:%s", GST_DEBUG_PAD_NAME (target)); - - result = gst_pad_alloc_buffer (peer, offset, size, caps, buf); - - gst_object_unref (peer); - } else { - result = GST_FLOW_NOT_LINKED; - } - - gst_object_unref (target); + result = gst_pad_alloc_buffer (internal, offset, size, caps, buf); + gst_object_unref (internal); return result; } @@ -197,13 +186,13 @@ gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size, static GstFlowReturn gst_proxy_pad_do_chain (GstPad * pad, GstBuffer * buffer) { - GstPad *target = gst_proxy_pad_get_target (pad); + GstPad *internal = gst_proxy_pad_get_internal (pad); GstFlowReturn res; - g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED); + g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED); - res = gst_pad_chain (target, buffer); - gst_object_unref (target); + res = gst_pad_push (internal, buffer); + gst_object_unref (internal); return res; } @@ -212,13 +201,13 @@ static GstFlowReturn gst_proxy_pad_do_getrange (GstPad * pad, guint64 offset, guint size, GstBuffer ** buffer) { - GstPad *target = gst_proxy_pad_get_target (pad); + GstPad *internal = gst_proxy_pad_get_internal (pad); GstFlowReturn res; - g_return_val_if_fail (target != NULL, GST_FLOW_NOT_LINKED); + g_return_val_if_fail (internal != NULL, GST_FLOW_NOT_LINKED); - res = gst_pad_get_range (target, offset, size, buffer); - gst_object_unref (target); + res = gst_pad_pull_range (internal, offset, size, buffer); + gst_object_unref (internal); return res; } @@ -227,19 +216,12 @@ static gboolean gst_proxy_pad_do_checkgetrange (GstPad * pad) { gboolean result; - GstPad *target = gst_proxy_pad_get_target (pad); - GstPad *peer; + GstPad *internal = gst_proxy_pad_get_internal (pad); - g_return_val_if_fail (target != NULL, FALSE); + g_return_val_if_fail (internal != NULL, FALSE); - peer = gst_pad_get_peer (target); - if (peer) { - result = gst_pad_check_pull_range (peer); - gst_object_unref (peer); - } else { - result = FALSE; - } - gst_object_unref (target); + result = gst_pad_check_pull_range (internal); + gst_object_unref (internal); return result; } @@ -250,11 +232,12 @@ gst_proxy_pad_do_getcaps (GstPad * pad) GstPad *target = gst_proxy_pad_get_target (pad); GstCaps *res; - g_return_val_if_fail (target != NULL, NULL); - - res = gst_pad_get_caps (target); - gst_object_unref (target); - + if (target) { + res = gst_pad_get_caps (target); + gst_object_unref (target); + } else { + res = gst_caps_new_any (); + } return res; } @@ -262,12 +245,12 @@ static gboolean gst_proxy_pad_do_acceptcaps (GstPad * pad, GstCaps * caps) { GstPad *target = gst_proxy_pad_get_target (pad); - gboolean res; + gboolean res = FALSE; - g_return_val_if_fail (target != NULL, FALSE); - - res = gst_pad_accept_caps (target, caps); - gst_object_unref (target); + if (target) { + res = gst_pad_accept_caps (target, caps); + gst_object_unref (target); + } return res; } @@ -277,10 +260,10 @@ gst_proxy_pad_do_fixatecaps (GstPad * pad, GstCaps * caps) { GstPad *target = gst_proxy_pad_get_target (pad); - g_return_if_fail (target != NULL); - - gst_pad_fixate_caps (target, caps); - gst_object_unref (target); + if (target) { + gst_pad_fixate_caps (target, caps); + gst_object_unref (target); + } } static gboolean @@ -289,11 +272,17 @@ gst_proxy_pad_do_setcaps (GstPad * pad, GstCaps * caps) GstPad *target = gst_proxy_pad_get_target (pad); gboolean res; - g_return_val_if_fail (target != NULL, FALSE); - - res = gst_pad_set_caps (target, caps); - gst_object_unref (target); - + if (target) { + res = gst_pad_set_caps (target, caps); + gst_object_unref (target); + } else { + /* + We don't have any target, but we shouldn't return FALSE since this + would stop the actual push of a buffer (which might trigger a pad block + or probe, or properly return GST_FLOW_NOT_LINKED. + */ + res = TRUE; + } return res; } @@ -301,48 +290,38 @@ static gboolean gst_proxy_pad_set_target_unlocked (GstPad * pad, GstPad * target) { GstPad *oldtarget; + GstPadTemplate **template_p; - GST_DEBUG ("set target %s:%s on %s:%s", - GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (pad)); + if (target) { + GST_LOG_OBJECT (pad, "setting target %s:%s", GST_DEBUG_PAD_NAME (target)); + if (G_UNLIKELY (GST_PAD_DIRECTION (pad) != GST_PAD_DIRECTION (target))) { + GST_ERROR_OBJECT (pad, + "target pad doesn't have the same direction as ourself"); + return FALSE; + } + } else + GST_LOG_OBJECT (pad, "clearing target"); /* clear old target */ if ((oldtarget = GST_PROXY_PAD_TARGET (pad))) { - gst_object_unref (oldtarget); + + /* Clear previous pad template */ + template_p = &GST_PAD_PAD_TEMPLATE (pad); + gst_object_replace ((GstObject **) template_p, NULL); + + /* Get rid of target */ GST_PROXY_PAD_TARGET (pad) = NULL; + gst_object_unref (oldtarget); } if (target) { /* set and ref new target if any */ GST_PROXY_PAD_TARGET (pad) = gst_object_ref (target); - gst_pad_set_query_type_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query_type)); - gst_pad_set_event_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_event)); - gst_pad_set_query_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query)); - gst_pad_set_internal_link_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_internal_link)); - gst_pad_set_getcaps_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps)); - gst_pad_set_acceptcaps_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_acceptcaps)); - gst_pad_set_fixatecaps_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_fixatecaps)); - gst_pad_set_setcaps_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_setcaps)); - - if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { - gst_pad_set_bufferalloc_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc)); - gst_pad_set_chain_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain)); - } else { - gst_pad_set_getrange_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange)); - gst_pad_set_checkgetrange_function (pad, - GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange)); - } + /* Set new pad template */ + template_p = &GST_PAD_PAD_TEMPLATE (pad); + gst_object_replace ((GstObject **) template_p, + (GstObject *) GST_PAD_PAD_TEMPLATE (target)); } return TRUE; } @@ -373,10 +352,18 @@ gst_proxy_pad_get_target (GstPad * pad) return target; } -static void -gst_proxy_pad_init (GstProxyPad * pad) +static GstPad * +gst_proxy_pad_get_internal (GstPad * pad) { - pad->proxy_lock = g_mutex_new (); + GstPad *internal; + + GST_PROXY_LOCK (pad); + internal = GST_PROXY_PAD_INTERNAL (pad); + if (internal) + gst_object_ref (internal); + GST_PROXY_UNLOCK (pad); + + return internal; } static void @@ -386,8 +373,13 @@ gst_proxy_pad_dispose (GObject * object) GstPad **target_p; GST_PROXY_LOCK (pad); + /* remove and unref the target */ target_p = &GST_PROXY_PAD_TARGET (pad); gst_object_replace ((GstObject **) target_p, NULL); + /* + The internal is only cleared by GstGhostPad::dispose, since it is the + parent of non-ghost GstProxyPad and owns the refcount on the internal. + */ GST_PROXY_UNLOCK (pad); G_OBJECT_CLASS (gst_proxy_pad_parent_class)->dispose (object); @@ -404,6 +396,30 @@ gst_proxy_pad_finalize (GObject * object) G_OBJECT_CLASS (gst_proxy_pad_parent_class)->finalize (object); } +static void +gst_proxy_pad_init (GstProxyPad * ppad) +{ + GstPad *pad = (GstPad *) ppad; + + ppad->proxy_lock = g_mutex_new (); + + gst_pad_set_query_type_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query_type)); + gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_event)); + gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_query)); + gst_pad_set_internal_link_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_internal_link)); + gst_pad_set_getcaps_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps)); + gst_pad_set_acceptcaps_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_acceptcaps)); + gst_pad_set_fixatecaps_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_fixatecaps)); + gst_pad_set_setcaps_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_setcaps)); + +} + #ifndef GST_DISABLE_LOADSAVE /** * gst_proxy_pad_save_thyself: @@ -444,7 +460,6 @@ struct _GstGhostPad GstProxyPad pad; /* with PROXY_LOCK */ - GstPad *internal; gulong notify_id; /*< private > */ @@ -462,9 +477,6 @@ struct _GstGhostPadClass G_DEFINE_TYPE (GstGhostPad, gst_ghost_pad, GST_TYPE_PROXY_PAD); -static gboolean gst_ghost_pad_set_internal (GstGhostPad * pad, - GstPad * internal); - static void gst_ghost_pad_dispose (GObject * object); /* Work around g_logv's use of G_GNUC_PRINTF because gcc chokes on %P, which we @@ -479,26 +491,65 @@ gst_critical (const gchar * format, ...) va_end (args); } -static GstPad * -gst_ghost_pad_get_internal (GstPad * pad) +/* + * The parent_set and parent_unset are used to make sure that: + * _ the internal and target are only linked when the GhostPad has a parent, + * _ the internal and target are unlinked as soon as the GhostPad is removed + * from it's parent. + */ + +static void +gst_ghost_pad_parent_set (GstObject * object, GstObject * parent) { - GstPad *internal; + GstPad *gpad = GST_PAD (object); + GstPad *target = gst_proxy_pad_get_target (gpad); + GstPad *internal = gst_proxy_pad_get_internal (gpad); - GST_PROXY_LOCK (pad); - internal = GST_GHOST_PAD (pad)->internal; - if (internal) - gst_object_ref (internal); - GST_PROXY_UNLOCK (pad); + if (target) { + if (GST_PAD_IS_SRC (internal)) + gst_pad_link (internal, target); + else + gst_pad_link (target, internal); + gst_object_unref (target); + } + gst_object_unref (internal); - return internal; + if (GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_set) + GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_set (object, parent); } +static void +gst_ghost_pad_parent_unset (GstObject * object, GstObject * parent) +{ + GstPad *gpad = GST_PAD (object); + GstPad *target = gst_proxy_pad_get_target (gpad); + GstPad *internal = gst_proxy_pad_get_internal (gpad); + + if (target) { + if (GST_PAD_IS_SRC (internal)) + gst_pad_unlink (internal, target); + else + gst_pad_unlink (target, internal); + gst_object_unref (target); + } + gst_object_unref (internal); + + if (GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_unset) + GST_OBJECT_CLASS (gst_ghost_pad_parent_class)->parent_unset (object, + parent); +} + + static void gst_ghost_pad_class_init (GstGhostPadClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstObjectClass *gstobject_class = (GstObjectClass *) klass; gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_ghost_pad_dispose); + gstobject_class->parent_set = GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_set); + gstobject_class->parent_unset = + GST_DEBUG_FUNCPTR (gst_ghost_pad_parent_unset); } /* see gstghostpad design docs */ @@ -508,7 +559,7 @@ gst_ghost_pad_internal_do_activate_push (GstPad * pad, gboolean active) gboolean ret; if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { - GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad))); + GstPad *parent = gst_proxy_pad_get_internal (pad); if (parent) { g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE); @@ -548,7 +599,7 @@ gst_ghost_pad_internal_do_activate_pull (GstPad * pad, gboolean active) ret = FALSE; } } else { - GstPad *parent = GST_PAD (gst_object_get_parent (GST_OBJECT (pad))); + GstPad *parent = gst_proxy_pad_get_internal (pad); if (parent) { g_return_val_if_fail (GST_IS_GHOST_PAD (parent), FALSE); @@ -570,7 +621,7 @@ gst_ghost_pad_do_activate_push (GstPad * pad, gboolean active) gboolean ret; if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { - GstPad *internal = gst_ghost_pad_get_internal (pad); + GstPad *internal = gst_proxy_pad_get_internal (pad); if (internal) { ret = gst_pad_activate_push (internal, active); @@ -600,7 +651,7 @@ gst_ghost_pad_do_activate_pull (GstPad * pad, gboolean active) ret = FALSE; } } else { - GstPad *internal = gst_ghost_pad_get_internal (pad); + GstPad *internal = gst_proxy_pad_get_internal (pad); if (internal) { ret = gst_pad_activate_pull (internal, active); @@ -617,25 +668,13 @@ static GstPadLinkReturn gst_ghost_pad_do_link (GstPad * pad, GstPad * peer) { GstPad *internal, *target; - GstPadLinkReturn ret; + GstPadLinkReturn ret = GST_PAD_LINK_OK; target = gst_proxy_pad_get_target (pad); - g_return_val_if_fail (target != NULL, GST_PAD_LINK_NOSCHED); - /* proxy the peer into the bin */ - internal = g_object_new (GST_TYPE_PROXY_PAD, - "name", NULL, - "direction", GST_PAD_DIRECTION (peer), - "template", GST_PAD_PAD_TEMPLATE (peer), NULL); - + internal = gst_proxy_pad_get_internal (pad); gst_proxy_pad_set_target (internal, peer); - gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), internal); - - if (GST_PAD_IS_SRC (internal)) - ret = gst_pad_link (internal, target); - else - ret = gst_pad_link (target, internal); /* if we are a source pad, we should call the peer link function * if the peer has one */ @@ -645,11 +684,7 @@ gst_ghost_pad_do_link (GstPad * pad, GstPad * peer) } gst_object_unref (target); - - if (ret == GST_PAD_LINK_OK) - gst_pad_set_active (internal, GST_PAD_ACTIVATE_MODE (pad)); - else - gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL); + gst_object_unref (internal); return ret; } @@ -658,17 +693,20 @@ static void gst_ghost_pad_do_unlink (GstPad * pad) { GstPad *target = gst_proxy_pad_get_target (pad); + GstPad *internal = gst_proxy_pad_get_internal (pad); g_return_if_fail (target != NULL); GST_DEBUG_OBJECT (pad, "unlinking ghostpad"); + /* The target of the internal pad is no longer valid */ + gst_proxy_pad_set_target (internal, NULL); + if (target->unlinkfunc) target->unlinkfunc (target); - gst_ghost_pad_set_internal (GST_GHOST_PAD (pad), NULL); - gst_object_unref (target); + gst_object_unref (internal); } static void @@ -687,63 +725,6 @@ on_int_notify (GstPad * internal, GParamSpec * unused, GstGhostPad * pad) gst_caps_unref (caps); } -static gboolean -gst_ghost_pad_set_internal (GstGhostPad * pad, GstPad * internal) -{ - GST_PROXY_LOCK (pad); - /* first remove old internal pad */ - if (pad->internal) { - GstPad *intpeer; - - gst_pad_set_activatepull_function (pad->internal, NULL); - gst_pad_set_activatepush_function (pad->internal, NULL); - - g_signal_handler_disconnect (pad->internal, pad->notify_id); - - intpeer = gst_pad_get_peer (pad->internal); - if (intpeer) { - if (GST_PAD_IS_SRC (pad->internal)) - gst_pad_unlink (pad->internal, intpeer); - else - gst_pad_unlink (intpeer, pad->internal); - gst_object_unref (intpeer); - } - /* should dispose it */ - gst_object_unparent (GST_OBJECT_CAST (pad->internal)); - } - - /* then set new internal pad */ - if (internal) { - if (!gst_object_set_parent (GST_OBJECT_CAST (internal), - GST_OBJECT_CAST (pad))) - goto could_not_set; - - /* could be more general here, iterating over all writable properties... - * taking the short road for now tho */ - pad->notify_id = g_signal_connect (internal, "notify::caps", - G_CALLBACK (on_int_notify), pad); - on_int_notify (internal, NULL, pad); - gst_pad_set_activatepull_function (GST_PAD (internal), - GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull)); - gst_pad_set_activatepush_function (GST_PAD (internal), - GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push)); - /* a ref was taken by set_parent */ - } - pad->internal = internal; - - GST_PROXY_UNLOCK (pad); - - return TRUE; - - /* ERRORS */ -could_not_set: - { - gst_critical ("Could not set internal pad %" GST_PTR_FORMAT, internal); - GST_PROXY_UNLOCK (pad); - return FALSE; - } -} - static void gst_ghost_pad_init (GstGhostPad * pad) { @@ -756,7 +737,35 @@ gst_ghost_pad_init (GstGhostPad * pad) static void gst_ghost_pad_dispose (GObject * object) { - gst_ghost_pad_set_internal (GST_GHOST_PAD (object), NULL); + GstPad *pad = GST_PAD (object); + GstPad *internal; + GstPad *intpeer; + + GST_PROXY_LOCK (pad); + internal = GST_PROXY_PAD_INTERNAL (pad); + + gst_pad_set_activatepull_function (internal, NULL); + gst_pad_set_activatepush_function (internal, NULL); + + g_signal_handler_disconnect (internal, GST_GHOST_PAD (pad)->notify_id); + + intpeer = gst_pad_get_peer (internal); + if (intpeer) { + if (GST_PAD_IS_SRC (internal)) + gst_pad_unlink (internal, intpeer); + else + gst_pad_unlink (intpeer, internal); + gst_object_unref (intpeer); + } + + GST_PROXY_PAD_INTERNAL (internal) = NULL; + /* + disposes of the internal pad, since the ghostpad is the only possible object + that has a refcount on the internal pad. + */ + gst_object_unparent (GST_OBJECT_CAST (internal)); + + GST_PROXY_UNLOCK (pad); G_OBJECT_CLASS (gst_ghost_pad_parent_class)->dispose (object); } @@ -778,15 +787,84 @@ GstPad * gst_ghost_pad_new_no_target (const gchar * name, GstPadDirection dir) { GstPad *ret; + GstPad *internal; + + GST_LOG ("name:%s, direction:%d", name, dir); + + /* OBJECT CREATION */ ret = g_object_new (GST_TYPE_GHOST_PAD, "name", name, "direction", dir, NULL); - gst_pad_set_activatepush_function (ret, - GST_DEBUG_FUNCPTR (gst_ghost_pad_do_activate_push)); + /* Set directional padfunctions for ghostpad */ + if (dir == GST_PAD_SINK) { + gst_pad_set_bufferalloc_function (ret, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc)); + gst_pad_set_chain_function (ret, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain)); + } else { + gst_pad_set_getrange_function (ret, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange)); + gst_pad_set_checkgetrange_function (ret, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange)); + } + gst_pad_set_link_function (ret, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_link)); gst_pad_set_unlink_function (ret, GST_DEBUG_FUNCPTR (gst_ghost_pad_do_unlink)); + + /* INTERNAL PAD */ + + internal = + g_object_new (GST_TYPE_PROXY_PAD, "name", NULL, + "direction", (dir == GST_PAD_SRC) ? GST_PAD_SINK : GST_PAD_SRC, NULL); + + /* Set directional padfunctions for internal pad */ + if (dir == GST_PAD_SRC) { + gst_pad_set_bufferalloc_function (internal, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_bufferalloc)); + gst_pad_set_chain_function (internal, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_chain)); + } else { + gst_pad_set_getrange_function (internal, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getrange)); + gst_pad_set_checkgetrange_function (internal, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_checkgetrange)); + } + + GST_PROXY_LOCK (ret); + + if (!gst_object_set_parent (GST_OBJECT_CAST (internal), + GST_OBJECT_CAST (ret))) { + gst_critical ("Could not set internal pad%" GST_PTR_FORMAT, internal); + goto beach; + } + + /* + The ghostpad is the parent of the internal pad and is the only object that + can have a refcount on the internal pad. + At this point, the GstGhostPad has a refcount of 1, and the internal pad has + a refcount of 1. + When the refcount of the GstGhostPad drops to 0, the ghostpad will dispose + it's refcount on the internal pad in the dispose method by un-parenting it. + This is why we don't take extra refcounts in the assignments below + */ + GST_PROXY_PAD_INTERNAL (ret) = internal; + GST_PROXY_PAD_INTERNAL (GST_PROXY_PAD_INTERNAL (ret)) = GST_PAD (ret); + + /* could be more general here, iterating over all writable properties... + * taking the short road for now tho */ + GST_GHOST_PAD (ret)->notify_id = g_signal_connect (internal, "notify::caps", + G_CALLBACK (on_int_notify), ret); + on_int_notify (internal, NULL, GST_GHOST_PAD (ret)); + gst_pad_set_activatepull_function (GST_PAD (internal), + GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_pull)); + gst_pad_set_activatepush_function (GST_PAD (internal), + GST_DEBUG_FUNCPTR (gst_ghost_pad_internal_do_activate_push)); + +beach: + GST_PROXY_UNLOCK (ret); + return ret; } @@ -807,12 +885,12 @@ gst_ghost_pad_new (const gchar * name, GstPad * target) { GstPad *ret; + GST_LOG ("name:%s, target:%s:%s", name, GST_DEBUG_PAD_NAME (target)); + g_return_val_if_fail (GST_IS_PAD (target), NULL); g_return_val_if_fail (!gst_pad_is_linked (target), NULL); if ((ret = gst_ghost_pad_new_no_target (name, GST_PAD_DIRECTION (target)))) { - g_object_set (G_OBJECT (ret), - "template", GST_PAD_PAD_TEMPLATE (target), NULL); gst_ghost_pad_set_target (GST_GHOST_PAD (ret), target); } return ret; @@ -850,15 +928,15 @@ gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget) { GstPad *internal; GstPad *oldtarget; + GstObject *parent; gboolean result; g_return_val_if_fail (GST_IS_GHOST_PAD (gpad), FALSE); GST_PROXY_LOCK (gpad); - internal = gpad->internal; + internal = GST_PROXY_PAD_INTERNAL (GST_PAD (gpad)); - GST_DEBUG ("set target %s:%s on %s:%s", - GST_DEBUG_PAD_NAME (newtarget), GST_DEBUG_PAD_NAME (gpad)); + GST_DEBUG_OBJECT (gpad, "set target %s:%s", GST_DEBUG_PAD_NAME (newtarget)); /* clear old target */ if ((oldtarget = GST_PROXY_PAD_TARGET (gpad))) { @@ -874,12 +952,17 @@ gst_ghost_pad_set_target (GstGhostPad * gpad, GstPad * newtarget) result = gst_proxy_pad_set_target_unlocked (GST_PAD_CAST (gpad), newtarget); if (result && newtarget) { - /* and link to internal pad if we have one */ - if (internal) { + /* and link to internal pad if we are not unparent-ed */ + if ((parent = gst_object_get_parent (GST_OBJECT (gpad)))) { if (GST_PAD_IS_SRC (internal)) result = gst_pad_link (internal, newtarget); else result = gst_pad_link (newtarget, internal); + gst_object_unref (parent); + } else { + /* we need to connect the internal pad once we have a parent */ + GST_DEBUG_OBJECT (gpad, + "GhostPad doesn't have a parent, will connect internal pad later"); } } GST_PROXY_UNLOCK (gpad); diff --git a/gst/gstpad.c b/gst/gstpad.c index e2ddda8cce..12e0306645 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -63,7 +63,6 @@ #include "gst_private.h" #include "gstpad.h" -#include "gstghostpad.h" #include "gstpadtemplate.h" #include "gstenumtypes.h" #include "gstmarshal.h" @@ -939,18 +938,10 @@ gboolean gst_pad_set_blocked_async (GstPad * pad, gboolean blocked, GstPadBlockCallback callback, gpointer user_data) { - gboolean was_blocked, was_ghost = FALSE; + gboolean was_blocked = FALSE; g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return FALSE; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); was_blocked = GST_PAD_IS_BLOCKED (pad); @@ -990,10 +981,6 @@ gst_pad_set_blocked_async (GstPad * pad, gboolean blocked, } GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } - return TRUE; had_right_state: @@ -1003,9 +990,6 @@ had_right_state: was_blocked); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } return FALSE; } } @@ -1727,8 +1711,9 @@ not_srcpad: } src_was_linked: { - GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked", - GST_DEBUG_PAD_NAME (srcpad)); + GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was already linked to %s:%s", + GST_DEBUG_PAD_NAME (srcpad), + GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad))); /* we do not emit a warning in this case because unlinking cannot * be made MT safe.*/ GST_OBJECT_UNLOCK (srcpad); @@ -1743,8 +1728,9 @@ not_sinkpad: } sink_was_linked: { - GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked", - GST_DEBUG_PAD_NAME (sinkpad)); + GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was already linked to %s:%s", + GST_DEBUG_PAD_NAME (sinkpad), + GST_DEBUG_PAD_NAME (GST_PAD_PEER (sinkpad))); /* we do not emit a warning in this case because unlinking cannot * be made MT safe.*/ GST_OBJECT_UNLOCK (sinkpad); @@ -3870,6 +3856,7 @@ dropping: } not_linked: { + GST_DEBUG_OBJECT (pad, "Dropping event because pad is not linked"); gst_event_unref (event); GST_OBJECT_UNLOCK (pad); return FALSE; diff --git a/gst/gstutils.c b/gst/gstutils.c index bbc749c99d..ac1bf337c3 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -2783,19 +2783,10 @@ gulong gst_pad_add_data_probe (GstPad * pad, GCallback handler, gpointer data) { gulong sigid; - gboolean was_ghost = FALSE; g_return_val_if_fail (GST_IS_PAD (pad), 0); g_return_val_if_fail (handler != NULL, 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return 0; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); sigid = g_signal_connect (pad, "have-data", handler, data); GST_PAD_DO_EVENT_SIGNALS (pad)++; @@ -2805,10 +2796,6 @@ gst_pad_add_data_probe (GstPad * pad, GCallback handler, gpointer data) GST_PAD_DO_BUFFER_SIGNALS (pad), GST_PAD_DO_EVENT_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } - return sigid; } @@ -2827,19 +2814,10 @@ gulong gst_pad_add_event_probe (GstPad * pad, GCallback handler, gpointer data) { gulong sigid; - gboolean was_ghost = FALSE; g_return_val_if_fail (GST_IS_PAD (pad), 0); g_return_val_if_fail (handler != NULL, 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return 0; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); sigid = g_signal_connect (pad, "have-data::event", handler, data); GST_PAD_DO_EVENT_SIGNALS (pad)++; @@ -2847,10 +2825,6 @@ gst_pad_add_event_probe (GstPad * pad, GCallback handler, gpointer data) GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } - return sigid; } @@ -2869,19 +2843,10 @@ gulong gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data) { gulong sigid; - gboolean was_ghost = FALSE; g_return_val_if_fail (GST_IS_PAD (pad), 0); g_return_val_if_fail (handler != NULL, 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return 0; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); sigid = g_signal_connect (pad, "have-data::buffer", handler, data); GST_PAD_DO_BUFFER_SIGNALS (pad)++; @@ -2889,10 +2854,6 @@ gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data) GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_BUFFER_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } - return sigid; } @@ -2906,19 +2867,9 @@ gst_pad_add_buffer_probe (GstPad * pad, GCallback handler, gpointer data) void gst_pad_remove_data_probe (GstPad * pad, guint handler_id) { - gboolean was_ghost = FALSE; - g_return_if_fail (GST_IS_PAD (pad)); g_return_if_fail (handler_id > 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); g_signal_handler_disconnect (pad, handler_id); GST_PAD_DO_BUFFER_SIGNALS (pad)--; @@ -2929,9 +2880,6 @@ gst_pad_remove_data_probe (GstPad * pad, guint handler_id) GST_PAD_DO_BUFFER_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } } /** @@ -2944,29 +2892,15 @@ gst_pad_remove_data_probe (GstPad * pad, guint handler_id) void gst_pad_remove_event_probe (GstPad * pad, guint handler_id) { - gboolean was_ghost = FALSE; - g_return_if_fail (GST_IS_PAD (pad)); g_return_if_fail (handler_id > 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); g_signal_handler_disconnect (pad, handler_id); GST_PAD_DO_EVENT_SIGNALS (pad)--; GST_DEBUG ("removed event probe from pad %s:%s, now %d event probes", GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_EVENT_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - - if (was_ghost) { - gst_object_unref (pad); - } } /** @@ -2979,19 +2913,9 @@ gst_pad_remove_event_probe (GstPad * pad, guint handler_id) void gst_pad_remove_buffer_probe (GstPad * pad, guint handler_id) { - gboolean was_ghost = FALSE; - g_return_if_fail (GST_IS_PAD (pad)); g_return_if_fail (handler_id > 0); - if (GST_IS_GHOST_PAD (pad)) { - pad = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); - if (!pad) { - return; - } - was_ghost = TRUE; - } - GST_OBJECT_LOCK (pad); g_signal_handler_disconnect (pad, handler_id); GST_PAD_DO_BUFFER_SIGNALS (pad)--; @@ -2999,9 +2923,6 @@ gst_pad_remove_buffer_probe (GstPad * pad, guint handler_id) GST_DEBUG_PAD_NAME (pad), GST_PAD_DO_BUFFER_SIGNALS (pad)); GST_OBJECT_UNLOCK (pad); - if (was_ghost) { - gst_object_unref (pad); - } } /** diff --git a/tests/check/gst/gstghostpad.c b/tests/check/gst/gstghostpad.c index 2ab4247376..793dd353b6 100644 --- a/tests/check/gst/gstghostpad.c +++ b/tests/check/gst/gstghostpad.c @@ -306,27 +306,27 @@ GST_START_TEST (test_ghost_pads) while (GST_OBJECT_REFCOUNT_VALUE (fsrc) > 2) THREAD_SWITCH (); - ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 2); /* gisrc */ + ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1); ASSERT_OBJECT_REFCOUNT (gsink, "gsink", 1); ASSERT_OBJECT_REFCOUNT (gsrc, "gsink", 1); - ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 2); /* gisink */ + ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1); - ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 1); /* gsink */ + ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 2); /* gsink */ ASSERT_OBJECT_REFCOUNT (isink, "isink", 2); /* gsink */ - ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 1); /* gsrc */ + ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 2); /* gsrc */ ASSERT_OBJECT_REFCOUNT (isrc, "isrc", 2); /* gsrc */ gst_object_unref (gsink); ASSERT_OBJECT_REFCOUNT (isink, "isink", 1); ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 1); - ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 2); /* gisrc */ + ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1); gst_object_unref (gisrc); ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1); gst_object_unref (gsrc); ASSERT_OBJECT_REFCOUNT (isrc, "isrc", 1); ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 1); - ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 2); /* gisrc */ + ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1); gst_object_unref (gisink); ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1);