diff --git a/ChangeLog b/ChangeLog index 821206eeae..8bfab449e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2008-09-01 Wim Taymans + + Based on patch by: Olivier Crete + + * docs/gst/gstreamer-sections.txt: + * win32/common/libgstreamer.def: + * gst/gstpad.c: (gst_pad_init), + (gst_pad_set_iterate_internal_links_function), + (int_link_iter_data_free), (iterate_pad), + (gst_pad_iterate_internal_links_default), + (gst_pad_iterate_internal_links), (gst_pad_get_internal_links): + * gst/gstpad.h: + Add threadsafe replacement functions for getting internal links of an + element. Deprecate the old internal links functions. + API:GstPad::gst_pad_set_iterate_internal_links_function() + API:GstPad::GstPadIterIntLinkFunction + API:GstPad::gst_pad_iterate_internal_links() + API:GstPad::gst_pad_iterate_internal_links_default() + + * gst/gstghostpad.c: (gst_proxy_pad_do_iterate_internal_links), + (gst_proxy_pad_init): + Implement threadsafe internal links. + + * tests/check/elements/tee.c: (GST_START_TEST), (tee_suite): + Unit test for internal links on tee. See #549504. + 2008-08-30 Edward Hervey * tests/check/Makefile.am: diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 97b74a6af4..71cf495540 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -1338,10 +1338,16 @@ GstPadQueryTypeFunction gst_pad_get_query_types gst_pad_get_query_types_default +gst_pad_set_iterate_internal_links_function +GstPadIterIntLinkFunction +gst_pad_iterate_internal_links +gst_pad_iterate_internal_links_default + gst_pad_set_internal_link_function GstPadIntLinkFunction gst_pad_get_internal_links gst_pad_get_internal_links_default + gst_pad_load_and_link gst_pad_dispatcher @@ -1433,6 +1439,7 @@ GST_PAD_FIXATECAPSFUNC GST_PAD_GETCAPSFUNC GST_PAD_GETRANGEFUNC GST_PAD_INTLINKFUNC +GST_PAD_ITERINTLINKFUNC GST_PAD_IS_FLUSHING GST_PAD_LINKFUNC GST_PAD_QUERYFUNC diff --git a/gst/gstghostpad.c b/gst/gstghostpad.c index 2aba5d2fcf..e727cec996 100644 --- a/gst/gstghostpad.c +++ b/gst/gstghostpad.c @@ -172,6 +172,20 @@ gst_proxy_pad_do_internal_link (GstPad * pad) return res; } +static GstIterator * +gst_proxy_pad_do_iterate_internal_links (GstPad * pad) +{ + GstIterator *res = NULL; + GstPad *target = gst_proxy_pad_get_target (pad); + + if (target) { + res = gst_pad_iterate_internal_links (target); + gst_object_unref (target); + } + + return res; +} + static GstFlowReturn gst_proxy_pad_do_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) @@ -415,6 +429,8 @@ gst_proxy_pad_init (GstProxyPad * ppad) 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_iterate_internal_links_function (pad, + GST_DEBUG_FUNCPTR (gst_proxy_pad_do_iterate_internal_links)); gst_pad_set_getcaps_function (pad, GST_DEBUG_FUNCPTR (gst_proxy_pad_do_getcaps)); diff --git a/gst/gstpad.c b/gst/gstpad.c index b8e240521b..dad0115bc7 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -347,6 +347,9 @@ gst_pad_init (GstPad * pad) GST_PAD_QUERYFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_query_default); GST_PAD_INTLINKFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_get_internal_links_default); + GST_PAD_ITERINTLINKFUNC (pad) = + GST_DEBUG_FUNCPTR (gst_pad_iterate_internal_links_default); + GST_PAD_ACCEPTCAPSFUNC (pad) = GST_DEBUG_FUNCPTR (gst_pad_acceptcaps_default); pad->do_buffer_signals = 0; @@ -1344,13 +1347,36 @@ gst_pad_get_query_types_default (GstPad * pad) return result; } +/** + * gst_pad_set_iterate_internal_links_function: + * @pad: a #GstPad of either direction. + * @iterintlink: the #GstPadIterIntLinkFunction to set. + * + * Sets the given internal link iterator function for the pad. + * + * Since: 0.10.21 + */ +void +gst_pad_set_iterate_internal_links_function (GstPad * pad, + GstPadIterIntLinkFunction iterintlink) +{ + g_return_if_fail (GST_IS_PAD (pad)); + + GST_PAD_ITERINTLINKFUNC (pad) = iterintlink; + GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link iterator set to %s", + GST_DEBUG_FUNCPTR_NAME (iterintlink)); +} + /** * gst_pad_set_internal_link_function: * @pad: a #GstPad of either direction. * @intlink: the #GstPadIntLinkFunction to set. * * Sets the given internal link function for the pad. + * + * Deprecated: Use the thread-safe gst_pad_set_iterate_internal_links_function() */ +#ifndef GST_REMOVE_DEPRECATED void gst_pad_set_internal_link_function (GstPad * pad, GstPadIntLinkFunction intlink) { @@ -1360,6 +1386,7 @@ gst_pad_set_internal_link_function (GstPad * pad, GstPadIntLinkFunction intlink) GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "internal link set to %s", GST_DEBUG_FUNCPTR_NAME (intlink)); } +#endif /* GST_REMOVE_DEPRECATED */ /** * gst_pad_set_link_function: @@ -2892,6 +2919,148 @@ gst_pad_alloc_buffer_and_set_caps (GstPad * pad, guint64 offset, gint size, return gst_pad_alloc_buffer_full (pad, offset, size, caps, buf, TRUE); } + +#ifndef GST_REMOVE_DEPRECATED +typedef struct +{ + GList *list; + guint32 cookie; +} IntLinkIterData; + +static void +int_link_iter_data_free (IntLinkIterData * data) +{ + g_list_free (data->list); + g_free (data); +} +#endif + +static GstIteratorItem +iterate_pad (GstIterator * it, GstPad * pad) +{ + gst_object_ref (pad); + return GST_ITERATOR_ITEM_PASS; +} + +/** + * gst_pad_iterate_internal_links_default: + * @pad: the #GstPad to get the internal links of. + * + * Iterate the list of pads to which the given pad is linked to inside of + * the parent element. + * This is the default handler, and thus returns an iterator of all of the + * pads inside the parent element with opposite direction. + * + * The caller must free this iterator after use with gst_iterator_free(). + * + * Returns: a #GstIterator of #GstPad, or NULL if @pad has no parent. Unref each + * returned pad with gst_object_unref(). + * + * Since: 0.10.21 + */ +GstIterator * +gst_pad_iterate_internal_links_default (GstPad * pad) +{ + GstIterator *res; + GstElement *parent = NULL; + GList **padlist; + guint32 *cookie; + gpointer owner; + GstIteratorDisposeFunction dispose; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + +#ifndef GST_REMOVE_DEPRECATED + /* when we get here, the default handler for the iterate links is called, + * which means that the user has not installed a custom one. We first check if + * there is maybe a custom legacy function we can call. */ + if (GST_PAD_INTLINKFUNC (pad) && + GST_PAD_INTLINKFUNC (pad) != gst_pad_get_internal_links_default) { + IntLinkIterData *data; + + /* make an iterator for the list. We can't protect the list with a + * cookie. If we would take the cookie of the parent element, we need to + * have a parent, which is not required for GST_PAD_INTLINKFUNC(). We could + * cache the per-pad list and invalidate the list when a new call to + * INTLINKFUNC() returned a different list but then this would only work if + * two concurrent iterators were used and the last iterator would still be + * thread-unsafe. Just don't use this method anymore. */ + data = g_new0 (IntLinkIterData, 1); + data->list = GST_PAD_INTLINKFUNC (pad) (pad); + data->cookie = 0; + + GST_WARNING_OBJECT (pad, "Making unsafe iterator"); + + cookie = &data->cookie; + padlist = &data->list; + owner = data; + dispose = (GstIteratorDisposeFunction) int_link_iter_data_free; + } else +#endif + { + GST_OBJECT_LOCK (pad); + parent = GST_PAD_PARENT (pad); + if (!parent || !GST_IS_ELEMENT (parent)) + goto no_parent; + + gst_object_ref (parent); + GST_OBJECT_UNLOCK (pad); + + if (pad->direction == GST_PAD_SRC) + padlist = &parent->sinkpads; + else + padlist = &parent->srcpads; + + GST_DEBUG_OBJECT (pad, "Making iterator"); + + cookie = &parent->pads_cookie; + owner = parent; + dispose = (GstIteratorDisposeFunction) gst_object_unref; + } + + res = gst_iterator_new_list (GST_TYPE_PAD, + GST_OBJECT_GET_LOCK (parent), + cookie, padlist, owner, (GstIteratorItemFunction) iterate_pad, dispose); + + return res; + + /* ERRORS */ +no_parent: + { + GST_OBJECT_UNLOCK (pad); + GST_DEBUG_OBJECT (pad, "no parent element"); + return NULL; + } +} + +/** + * gst_pad_iterate_internal_links: + * @pad: the GstPad to get the internal links of. + * + * Gets an iterator for the pads to which the given pad is linked to inside + * of the parent element. + * + * Each #GstPad element yielded by the iterator will have its refcount increased, + * so unref after use. + * + * Returns: a new #GstIterator of #GstPad or %NULL when the pad does not have an + * iterator function configured. Use gst_iterator_free() after usage. + * + * Since: 0.10.21 + */ +GstIterator * +gst_pad_iterate_internal_links (GstPad * pad) +{ + GstIterator *res = NULL; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + if (GST_PAD_ITERINTLINKFUNC (pad)) + res = GST_PAD_ITERINTLINKFUNC (pad) (pad); + + return res; +} + /** * gst_pad_get_internal_links_default: * @pad: the #GstPad to get the internal links of. @@ -2906,7 +3075,11 @@ gst_pad_alloc_buffer_and_set_caps (GstPad * pad, guint64 offset, gint size, * Returns: a newly allocated #GList of pads, or NULL if the pad has no parent. * * Not MT safe. + * + * Deprecated: use the thread-safe gst_pad_iterate_internal_links() functions + * instead. */ +#ifndef GST_REMOVE_DEPRECATED GList * gst_pad_get_internal_links_default (GstPad * pad) { @@ -2947,6 +3120,7 @@ no_parent: return NULL; } } +#endif /* GST_REMOVE_DEPRECATED */ /** * gst_pad_get_internal_links: @@ -2959,7 +3133,10 @@ no_parent: * Returns: a newly allocated #GList of pads. * * Not MT safe. + * + * Deprecated: Use the thread-safe gst_pad_iterate_internal_links() instead. */ +#ifndef GST_REMOVE_DEPRECATED GList * gst_pad_get_internal_links (GstPad * pad) { @@ -2967,12 +3144,14 @@ gst_pad_get_internal_links (GstPad * pad) g_return_val_if_fail (GST_IS_PAD (pad), NULL); + GST_WARNING_OBJECT (pad, "Calling unsafe internal links"); + if (GST_PAD_INTLINKFUNC (pad)) res = GST_PAD_INTLINKFUNC (pad) (pad); return res; } - +#endif /* GST_REMOVE_DEPRECATED */ static gboolean gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event) diff --git a/gst/gstpad.h b/gst/gstpad.h index 9879beeecc..50da287adc 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -308,11 +308,28 @@ typedef gboolean (*GstPadCheckGetRangeFunction) (GstPad *pad); * The signature of the internal pad link function. * * Returns: a newly allocated #GList of pads that are linked to the given pad on - * the inside of the parent element. - * The caller must call g_list_free() on it after use. + * the inside of the parent element. + * + * The caller must call g_list_free() on it after use. + * + * Deprecated: use the threadsafe #GstPadIterIntLinkFunction instead. */ typedef GList* (*GstPadIntLinkFunction) (GstPad *pad); +/** + * GstPadIterIntLinkFunction: + * @pad: The #GstPad to query. + * + * The signature of the internal pad link iterator function. + * + * Returns: a new #GstIterator that will iterate over all pads that are + * linked to the given pad on the inside of the parent element. + * + * the caller must call gst_iterator_free() after usage. + * + * Since 0.10.21 + */ +typedef GstIterator* (*GstPadIterIntLinkFunction) (GstPad *pad); /* generic query function */ /** @@ -540,6 +557,7 @@ typedef struct _GstPadTemplate GstPadTemplate; * @bufferallocfunc: function to allocate a buffer for this pad * @do_buffer_signals: counter counting installed buffer signals * @do_event_signals: counter counting installed event signals + * @iterintlinkfunc: get the internal links iterator of this pad * * The #GstPad structure. Use the functions to update the variables. */ @@ -607,8 +625,12 @@ struct _GstPad { gint do_buffer_signals; gint do_event_signals; + /* ABI added */ + /* iterate internal links */ + GstPadIterIntLinkFunction iterintlinkfunc; + /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING - 1]; }; struct _GstPadClass { @@ -645,6 +667,7 @@ struct _GstPadClass { #define GST_PAD_QUERYTYPEFUNC(pad) (GST_PAD_CAST(pad)->querytypefunc) #define GST_PAD_QUERYFUNC(pad) (GST_PAD_CAST(pad)->queryfunc) #define GST_PAD_INTLINKFUNC(pad) (GST_PAD_CAST(pad)->intlinkfunc) +#define GST_PAD_ITERINTLINKFUNC(pad) (GST_PAD_CAST(pad)->iterintlinkfunc) #define GST_PAD_PEER(pad) (GST_PAD_CAST(pad)->peer) #define GST_PAD_LINKFUNC(pad) (GST_PAD_CAST(pad)->linkfunc) @@ -865,6 +888,12 @@ void gst_pad_set_internal_link_function (GstPad *pad, GstPadIntLinkFunction in GList* gst_pad_get_internal_links (GstPad *pad); GList* gst_pad_get_internal_links_default (GstPad *pad); +void gst_pad_set_iterate_internal_links_function (GstPad * pad, + GstPadIterIntLinkFunction iterintlink); +GstIterator * gst_pad_iterate_internal_links (GstPad * pad); +GstIterator * gst_pad_iterate_internal_links_default (GstPad * pad); + + /* generic query function */ void gst_pad_set_query_type_function (GstPad *pad, GstPadQueryTypeFunction type_func); G_CONST_RETURN GstQueryType* diff --git a/tests/check/elements/tee.c b/tests/check/elements/tee.c index bc017dd07e..d62a975d99 100644 --- a/tests/check/elements/tee.c +++ b/tests/check/elements/tee.c @@ -345,6 +345,113 @@ GST_START_TEST (test_release_while_second_buffer_alloc) GST_END_TEST; +/* Check the internal pads of tee */ +GST_START_TEST (test_internal_links) +{ + GstElement *tee; + GstPad *sinkpad, *srcpad1, *srcpad2; + GstIterator *it; + GstIteratorResult res; + gpointer val1, val2; + + tee = gst_check_setup_element ("tee"); + + sinkpad = gst_element_get_static_pad (tee, "sink"); + fail_unless (sinkpad != NULL); + it = gst_pad_iterate_internal_links (sinkpad); + fail_unless (it != NULL); + + /* iterator should not return anything */ + val1 = NULL; + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_DONE); + fail_unless (val1 == NULL); + + srcpad1 = gst_element_get_request_pad (tee, "src%d"); + fail_unless (srcpad1 != NULL); + + /* iterator should resync */ + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_RESYNC); + fail_unless (val1 == NULL); + gst_iterator_resync (it); + + /* we should get something now */ + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_OK); + fail_unless (GST_PAD_CAST (val1) == srcpad1); + + gst_object_unref (val1); + + val1 = NULL; + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_DONE); + fail_unless (val1 == NULL); + + srcpad2 = gst_element_get_request_pad (tee, "src%d"); + fail_unless (srcpad2 != NULL); + + /* iterator should resync */ + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_RESYNC); + fail_unless (val1 == NULL); + gst_iterator_resync (it); + + /* we should get one of the 2 pads now */ + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_OK); + fail_unless (GST_PAD_CAST (val1) == srcpad1 + || GST_PAD_CAST (val1) == srcpad2); + + /* and the other */ + res = gst_iterator_next (it, &val2); + fail_unless (res == GST_ITERATOR_OK); + fail_unless (GST_PAD_CAST (val2) == srcpad1 + || GST_PAD_CAST (val2) == srcpad2); + fail_unless (val1 != val2); + gst_object_unref (val1); + gst_object_unref (val2); + + val1 = NULL; + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_DONE); + fail_unless (val1 == NULL); + + gst_iterator_free (it); + + /* get an iterator for the other direction */ + it = gst_pad_iterate_internal_links (srcpad1); + fail_unless (it != NULL); + + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_OK); + fail_unless (GST_PAD_CAST (val1) == sinkpad); + gst_object_unref (val1); + + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_DONE); + gst_iterator_free (it); + + it = gst_pad_iterate_internal_links (srcpad2); + fail_unless (it != NULL); + + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_OK); + fail_unless (GST_PAD_CAST (val1) == sinkpad); + gst_object_unref (val1); + + res = gst_iterator_next (it, &val1); + fail_unless (res == GST_ITERATOR_DONE); + + gst_iterator_free (it); + gst_object_unref (srcpad1); + gst_object_unref (srcpad2); + gst_object_unref (sinkpad); + gst_object_unref (tee); +} + +GST_END_TEST; + static Suite * tee_suite (void) { @@ -356,6 +463,7 @@ tee_suite (void) tcase_add_test (tc_chain, test_stress); tcase_add_test (tc_chain, test_release_while_buffer_alloc); tcase_add_test (tc_chain, test_release_while_second_buffer_alloc); + tcase_add_test (tc_chain, test_internal_links); return s; } diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index af8e36e538..2f55dbb7b1 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -546,6 +546,8 @@ EXPORTS gst_pad_is_blocked gst_pad_is_blocking gst_pad_is_linked + gst_pad_iterate_internal_links + gst_pad_iterate_internal_links_default gst_pad_link gst_pad_link_return_get_type gst_pad_load_and_link @@ -591,6 +593,7 @@ EXPORTS gst_pad_set_getcaps_function gst_pad_set_getrange_function gst_pad_set_internal_link_function + gst_pad_set_iterate_internal_links_function gst_pad_set_link_function gst_pad_set_query_function gst_pad_set_query_type_function