mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 03:01:03 +00:00
element: add gst_element_foreach_*pad()
Add convenience API that iterates over all pads, sink pads or source pads and makes sure that the foreach function is called exactly once for each pad. This is a KISS implementation. It doesn't use GstIterator and doesn't try to do clever things like resync if pads are added or removed while the function is executing. We can still do that in future if we think it's needed, but in practice it will likely make absolutely no difference whatsoever, since these things will have to be handled properly elsewhere by the element anyway if they're important. After all, it's always possible that a pad is added or removed just after the iterator finishes iterating, but before the function returns. This is also a replacement for gst_aggregator_iterate_sink_pads(). https://bugzilla.gnome.org/show_bug.cgi?id=785679
This commit is contained in:
parent
f911fe4314
commit
d106390adc
5 changed files with 269 additions and 0 deletions
|
@ -886,6 +886,9 @@ gst_element_remove_pad
|
|||
gst_element_iterate_pads
|
||||
gst_element_iterate_sink_pads
|
||||
gst_element_iterate_src_pads
|
||||
gst_element_foreach_pad
|
||||
gst_element_foreach_sink_pad
|
||||
gst_element_foreach_src_pad
|
||||
|
||||
<SUBSECTION element-linking>
|
||||
gst_element_link
|
||||
|
|
115
gst/gstelement.c
115
gst/gstelement.c
|
@ -1247,6 +1247,121 @@ gst_element_iterate_sink_pads (GstElement * element)
|
|||
return gst_element_iterate_pad_list (element, &element->sinkpads);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_element_do_foreach_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func, gpointer user_data,
|
||||
GList ** p_pads, guint16 * p_npads)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
GstPad **pads;
|
||||
guint n_pads, i;
|
||||
GList *l;
|
||||
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
|
||||
g_return_val_if_fail (func != NULL, FALSE);
|
||||
|
||||
GST_OBJECT_LOCK (element);
|
||||
n_pads = *p_npads;
|
||||
pads = g_newa (GstPad *, n_pads + 1);
|
||||
for (l = *p_pads, i = 0; l != NULL; l = l->next) {
|
||||
g_assert (i < n_pads);
|
||||
pads[i++] = gst_object_ref (l->data);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
|
||||
if (n_pads == 0)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < n_pads; ++i) {
|
||||
ret = func (element, pads[i], user_data);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_pads; ++i)
|
||||
gst_object_unref (pads[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_element_foreach_sink_pad:
|
||||
* @element: a #GstElement to iterate sink pads of
|
||||
* @func: (scope call): function to call for each sink pad
|
||||
* @user_data: (closure): user data passed to @func
|
||||
*
|
||||
* Call @func with @user_data for each of @element's sink pads. @func will be
|
||||
* called exactly once for each sink pad that exists at the time of this call,
|
||||
* unless one of the calls to @func returns %FALSE in which case we will stop
|
||||
* iterating pads and return early. If new sink pads are added or sink pads
|
||||
* are removed while the sink pads are being iterated, this will not be taken
|
||||
* into account until next time this function is used.
|
||||
*
|
||||
* Returns: %FALSE if @element had no sink pads or if one of the calls to @func
|
||||
* returned %FALSE.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_element_foreach_sink_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func, gpointer user_data)
|
||||
{
|
||||
return gst_element_do_foreach_pad (element, func, user_data,
|
||||
&element->sinkpads, &element->numsinkpads);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_element_foreach_src_pad:
|
||||
* @element: a #GstElement to iterate source pads of
|
||||
* @func: (scope call): function to call for each source pad
|
||||
* @user_data: (closure): user data passed to @func
|
||||
*
|
||||
* Call @func with @user_data for each of @element's source pads. @func will be
|
||||
* called exactly once for each source pad that exists at the time of this call,
|
||||
* unless one of the calls to @func returns %FALSE in which case we will stop
|
||||
* iterating pads and return early. If new source pads are added or source pads
|
||||
* are removed while the source pads are being iterated, this will not be taken
|
||||
* into account until next time this function is used.
|
||||
*
|
||||
* Returns: %FALSE if @element had no source pads or if one of the calls
|
||||
* to @func returned %FALSE.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_element_foreach_src_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func, gpointer user_data)
|
||||
{
|
||||
return gst_element_do_foreach_pad (element, func, user_data,
|
||||
&element->srcpads, &element->numsrcpads);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_element_foreach_pad:
|
||||
* @element: a #GstElement to iterate pads of
|
||||
* @func: (scope call): function to call for each pad
|
||||
* @user_data: (closure): user data passed to @func
|
||||
*
|
||||
* Call @func with @user_data for each of @element's pads. @func will be called
|
||||
* exactly once for each pad that exists at the time of this call, unless
|
||||
* one of the calls to @func returns %FALSE in which case we will stop
|
||||
* iterating pads and return early. If new pads are added or pads are removed
|
||||
* while pads are being iterated, this will not be taken into account until
|
||||
* next time this function is used.
|
||||
*
|
||||
* Returns: %FALSE if @element had no pads or if one of the calls to @func
|
||||
* returned %FALSE.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_element_foreach_pad (GstElement * element, GstElementForeachPadFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
return gst_element_do_foreach_pad (element, func, user_data,
|
||||
&element->pads, &element->numpads);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_element_class_add_pad_template:
|
||||
* @klass: the #GstElementClass to add the pad template to.
|
||||
|
|
|
@ -909,6 +909,35 @@ GstIterator * gst_element_iterate_src_pads (GstElement * element);
|
|||
GST_EXPORT
|
||||
GstIterator * gst_element_iterate_sink_pads (GstElement * element);
|
||||
|
||||
/**
|
||||
* GstElementForeachPadFunc:
|
||||
* @element: the #GstElement
|
||||
* @pad: a #GstPad
|
||||
* @user_data: user data passed to the foreach function
|
||||
*
|
||||
* Function called for each pad when using gst_element_foreach_sink_pad(),
|
||||
* gst_element_foreach_src_pad(), or gst_element_foreach_pad().
|
||||
*
|
||||
* Returns: %FALSE to stop iterating pads, %TRUE to continue
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
typedef gboolean (*GstElementForeachPadFunc) (GstElement * element,
|
||||
GstPad * pad,
|
||||
gpointer user_data);
|
||||
|
||||
GST_EXPORT
|
||||
gboolean gst_element_foreach_sink_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func,
|
||||
gpointer user_data);
|
||||
GST_EXPORT
|
||||
gboolean gst_element_foreach_src_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func,
|
||||
gpointer user_data);
|
||||
GST_EXPORT
|
||||
gboolean gst_element_foreach_pad (GstElement * element,
|
||||
GstElementForeachPadFunc func,
|
||||
gpointer user_data);
|
||||
/* event/query/format stuff */
|
||||
|
||||
GST_EXPORT
|
||||
|
|
|
@ -789,6 +789,124 @@ GST_START_TEST (test_request_pad_templates)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
static gboolean run_foreach_thread;
|
||||
|
||||
/* thread function that just adds/removes pads while main thread iterates pads */
|
||||
static gpointer
|
||||
thread_add_remove_pads (GstElement * e)
|
||||
{
|
||||
GPtrArray *pads;
|
||||
guint n, c = 0;
|
||||
|
||||
pads = g_ptr_array_new ();
|
||||
|
||||
THREAD_START ();
|
||||
|
||||
while (g_atomic_int_get (&run_foreach_thread)) {
|
||||
GstPad *p;
|
||||
gchar name[16];
|
||||
|
||||
/* add a new pad */
|
||||
g_snprintf (name, 16, "pad_%u", c++);
|
||||
p = gst_pad_new (name, g_random_boolean ()? GST_PAD_SRC : GST_PAD_SINK);
|
||||
g_ptr_array_add (pads, p);
|
||||
gst_element_add_pad (e, p);
|
||||
|
||||
THREAD_SWITCH ();
|
||||
|
||||
/* and remove a random pad */
|
||||
if (g_random_boolean () || pads->len > 100) {
|
||||
n = g_random_int_range (0, pads->len);
|
||||
p = g_ptr_array_remove_index (pads, n);
|
||||
gst_element_remove_pad (e, p);
|
||||
}
|
||||
|
||||
THREAD_SWITCH ();
|
||||
}
|
||||
|
||||
g_ptr_array_free (pads, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GQuark q;
|
||||
GstPadDirection dir; /* GST_PAD_UNKNOWN = both are allowed */
|
||||
gboolean func_called;
|
||||
} PadChecks;
|
||||
|
||||
static gboolean
|
||||
pad_foreach_func (GstElement * e, GstPad * pad, gpointer user_data)
|
||||
{
|
||||
PadChecks *checks = user_data;
|
||||
|
||||
/* check we haven't visited this pad already */
|
||||
fail_if (g_object_get_qdata (G_OBJECT (pad), checks->q) != NULL);
|
||||
|
||||
g_object_set_qdata (G_OBJECT (pad), checks->q, GINT_TO_POINTER (1));
|
||||
|
||||
if (checks->dir != GST_PAD_UNKNOWN) {
|
||||
fail_unless_equals_int (checks->dir, GST_PAD_DIRECTION (pad));
|
||||
}
|
||||
checks->func_called = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_foreach_pad)
|
||||
{
|
||||
PadChecks checks = { 0, GST_PAD_UNKNOWN, FALSE };
|
||||
GstElement *e;
|
||||
gint i;
|
||||
|
||||
e = gst_bin_new ("testbin");
|
||||
|
||||
/* function should not be called if there are no pads! */
|
||||
gst_element_foreach_pad (e, pad_foreach_func, &checks);
|
||||
fail_if (checks.func_called);
|
||||
|
||||
g_atomic_int_set (&run_foreach_thread, TRUE);
|
||||
|
||||
MAIN_INIT ();
|
||||
MAIN_START_THREAD_FUNCTION (0, thread_add_remove_pads, e);
|
||||
MAIN_SYNCHRONIZE ();
|
||||
|
||||
for (i = 0; i < 10000; ++i) {
|
||||
gchar num[32];
|
||||
|
||||
g_snprintf (num, 32, "foreach-test-%u", i);
|
||||
|
||||
checks.q = g_quark_from_string (num);
|
||||
checks.func_called = FALSE;
|
||||
if (g_random_boolean ()) {
|
||||
checks.dir = GST_PAD_UNKNOWN;
|
||||
gst_element_foreach_pad (e, pad_foreach_func, &checks);
|
||||
} else if (g_random_boolean ()) {
|
||||
checks.dir = GST_PAD_SRC;
|
||||
gst_element_foreach_src_pad (e, pad_foreach_func, &checks);
|
||||
} else {
|
||||
checks.dir = GST_PAD_SINK;
|
||||
gst_element_foreach_sink_pad (e, pad_foreach_func, &checks);
|
||||
}
|
||||
|
||||
THREAD_SWITCH ();
|
||||
}
|
||||
|
||||
g_atomic_int_set (&run_foreach_thread, FALSE);
|
||||
|
||||
MAIN_STOP_THREADS ();
|
||||
|
||||
/* function should be called if there are pads */
|
||||
checks.q = g_quark_from_string ("fini");
|
||||
checks.dir = GST_PAD_UNKNOWN;
|
||||
checks.func_called = FALSE;
|
||||
gst_element_foreach_pad (e, pad_foreach_func, &checks);
|
||||
fail_if (e->numpads > 0 && !checks.func_called);
|
||||
|
||||
gst_object_unref (e);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gst_element_suite (void)
|
||||
{
|
||||
|
@ -804,6 +922,7 @@ gst_element_suite (void)
|
|||
tcase_add_test (tc_chain, test_pad_templates);
|
||||
tcase_add_test (tc_chain, test_property_notify_message);
|
||||
tcase_add_test (tc_chain, test_request_pad_templates);
|
||||
tcase_add_test (tc_chain, test_foreach_pad);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -531,6 +531,9 @@ EXPORTS
|
|||
gst_element_factory_list_is_type
|
||||
gst_element_factory_make
|
||||
gst_element_flags_get_type
|
||||
gst_element_foreach_pad
|
||||
gst_element_foreach_sink_pad
|
||||
gst_element_foreach_src_pad
|
||||
gst_element_get_base_time
|
||||
gst_element_get_bus
|
||||
gst_element_get_clock
|
||||
|
|
Loading…
Reference in a new issue