+2008-09-01 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ Based on patch by: Olivier Crete <tester at tester dot ca>
+
+ * 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 <edward.hervey@collabora.co.uk>
* tests/check/Makefile.am:
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
GST_PAD_GETCAPSFUNC
GST_PAD_GETRANGEFUNC
GST_PAD_INTLINKFUNC
+GST_PAD_ITERINTLINKFUNC
GST_PAD_IS_FLUSHING
GST_PAD_LINKFUNC
GST_PAD_QUERYFUNC
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)
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));
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;
}
/**
+ * 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)
{
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:
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.
* 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)
{
return NULL;
}
}
+#endif /* GST_REMOVE_DEPRECATED */
/**
* gst_pad_get_internal_links:
* 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)
{
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)
* 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 */
/**
* @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.
*/
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 {
#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)
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*
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)
{
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;
}
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
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