From 6438f6f9b9edd3b4a014f5fd251b4b399ec6bce7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 19 Jun 2009 15:29:14 +0200 Subject: [PATCH] bufferlist: Various cleanups Add new method to iterate a bufferlist without having to allocate an iterator. Add convenience method for getting an item from the list based on the group and index. Remove redundant _do_data callback and method. Update unit-tests and add some more for the new methods. --- docs/gst/gstreamer-sections.txt | 12 ++- gst/gstbufferlist.c | 183 +++++++++++++++++++++++--------- gst/gstbufferlist.h | 69 ++++++++---- tests/check/gst/gstbufferlist.c | 169 +++++++++++++++++++++++++---- win32/common/libgstreamer.def | 4 +- 5 files changed, 336 insertions(+), 101 deletions(-) diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index a4de09216b..8c3e8e4f2c 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -215,14 +215,21 @@ gst_buffer_copy_flags_get_type GstBufferList GstBufferListIterator GstBufferListDoFunction -GstBufferListDoDataFunction + gst_buffer_list_new gst_buffer_list_ref gst_buffer_list_unref gst_buffer_list_copy gst_buffer_list_is_writable gst_buffer_list_make_writable + gst_buffer_list_n_groups + +GstBufferListItem +GstBufferListFunc +gst_buffer_list_foreach +gst_buffer_list_get + gst_buffer_list_iterate gst_buffer_list_iterator_free gst_buffer_list_iterator_n_buffers @@ -234,7 +241,6 @@ gst_buffer_list_iterator_remove gst_buffer_list_iterator_steal gst_buffer_list_iterator_take gst_buffer_list_iterator_do -gst_buffer_list_iterator_do_data gst_buffer_list_iterator_merge_group GstBufferListClass @@ -245,7 +251,9 @@ GST_IS_BUFFER_LIST GST_IS_BUFFER_LIST_CLASS GST_TYPE_BUFFER_LIST GST_BUFFER_LIST_CAST +GST_TYPE_BUFFER_LIST_ITEM +gst_buffer_list_item_get_type gst_buffer_list_get_type diff --git a/gst/gstbufferlist.c b/gst/gstbufferlist.c index 02863ee54d..20a2ef8023 100644 --- a/gst/gstbufferlist.c +++ b/gst/gstbufferlist.c @@ -289,6 +289,127 @@ gst_buffer_list_n_groups (GstBufferList * list) return n; } +/** + * gst_buffer_list_foreach: + * @list: a #GstBufferList + * @func: a #GstBufferListFunc to call + * @user_data: user data passed to @func + * + * Call @func with @data for each buffer in @list. + * + * @func can modify the passed buffer pointer or its contents. The return value + * of @func define if this function returns or if the remaining buffers in a + * group should be skipped. + */ +void +gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func, + gpointer user_data) +{ + GList *tmp, *next; + guint group, idx; + GstBufferListItem res; + + g_return_if_fail (list != NULL); + g_return_if_fail (func != NULL); + + next = list->buffers; + group = idx = 0; + while (next) { + GstBuffer *buffer; + + tmp = next; + next = g_list_next (tmp); + + buffer = tmp->data; + + if (buffer == GROUP_START) { + group++; + idx = 0; + continue; + } else if (buffer == STOLEN) + continue; + else + idx++; + + /* need to decrement the indices */ + res = func (&buffer, group - 1, idx - 1, user_data); + + if (G_UNLIKELY (buffer != tmp->data)) { + /* the function changed the buffer */ + if (buffer == NULL) { + /* we were asked to remove the item */ + list->buffers = g_list_delete_link (list->buffers, tmp); + idx--; + } else { + /* change the buffer */ + tmp->data = buffer; + } + } + + switch (res) { + case GST_BUFFER_LIST_CONTINUE: + break; + case GST_BUFFER_LIST_SKIP_GROUP: + while (next && next->data != GROUP_START) + next = g_list_next (next); + break; + case GST_BUFFER_LIST_END: + return; + } + } +} + +/** + * gst_buffer_list_get: + * @list: a #GstBufferList + * @group: the group + * @idx: the index in @group + * + * Get the buffer at @idx in @group. + * + * Note that this function is not efficient for iterating over the entire list. + * Use and iterator or gst_buffer_list_foreach() instead. + * + * Returns: the buffer at @idx in @group or NULL when there is no buffer. The + * buffer remaing valid as long as @list is valid. + */ +GstBuffer * +gst_buffer_list_get (GstBufferList * list, guint group, guint idx) +{ + GList *tmp; + guint cgroup, cidx; + + g_return_val_if_fail (list != NULL, NULL); + + tmp = list->buffers; + cgroup = 0; + while (tmp) { + if (tmp->data == GROUP_START) { + if (cgroup == group) { + /* we found the group */ + tmp = g_list_next (tmp); + cidx = 0; + while (tmp && tmp->data != GROUP_START) { + if (tmp->data != STOLEN) { + if (cidx == idx) + return GST_BUFFER_CAST (tmp->data); + else + cidx++; + } + tmp = g_list_next (tmp); + } + break; + } else { + cgroup++; + if (cgroup > group) + break; + } + } + tmp = g_list_next (tmp); + } + return NULL; +} + GType gst_buffer_list_get_type (void) { @@ -474,9 +595,10 @@ gst_buffer_list_iterator_next (GstBufferListIterator * it) return buffer; no_buffer: - it->last_returned = NULL; - - return NULL; + { + it->last_returned = NULL; + return NULL; + } } /** @@ -590,11 +712,10 @@ gst_buffer_list_iterator_steal (GstBufferListIterator * it) } /** - * gst_buffer_list_iterator_do_data: + * gst_buffer_list_iterator_do: * @it: a #GstBufferListIterator * @do_func: the function to be called - * @data: the gpointer to optional user data. - * @data_notify: function to be called when @data is no longer used + * @user_data: the gpointer to optional user data. * * Calls the given function for the last buffer returned by * gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have @@ -604,15 +725,11 @@ gst_buffer_list_iterator_steal (GstBufferListIterator * it) * * See #GstBufferListDoFunction for more details. * - * The @data_notify function is called after @do_func has returned, before this - * function returns, usually used to free @data. - * * Returns: the return value from @do_func */ GstBuffer * -gst_buffer_list_iterator_do_data (GstBufferListIterator * it, - GstBufferListDoDataFunction do_func, gpointer data, - GDestroyNotify data_notify) +gst_buffer_list_iterator_do (GstBufferListIterator * it, + GstBufferListDoFunction do_func, gpointer user_data) { GstBuffer *buffer; @@ -624,56 +741,16 @@ gst_buffer_list_iterator_do_data (GstBufferListIterator * it, g_assert (it->last_returned->data != GROUP_START); buffer = gst_buffer_list_iterator_steal (it); - buffer = do_func (buffer, data); + buffer = do_func (buffer, user_data); if (buffer == NULL) { gst_buffer_list_iterator_remove (it); } else { gst_buffer_list_iterator_take (it, buffer); } - if (data_notify != NULL) { - data_notify (data); - } - return buffer; } -static GstBuffer * -do_func_no_data (GstBuffer * buffer, GstBufferListDoFunction do_func) -{ - return do_func (buffer); -} - -/** - * gst_buffer_list_iterator_do: - * @it: a #GstBufferListIterator - * @do_func: the function to be called - * - * Calls the given function for the last buffer returned by - * gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have - * been called on @it before this function is called. - * gst_buffer_list_iterator_remove() or gst_buffer_list_iterator_steal() must - * not have been called since the last call to gst_buffer_list_iterator_next(). - * - * See #GstBufferListDoFunction for more details. - * - * Returns: the return value from @do_func - */ -GstBuffer * -gst_buffer_list_iterator_do (GstBufferListIterator * it, - GstBufferListDoFunction do_func) -{ - g_return_val_if_fail (it != NULL, NULL); - g_return_val_if_fail (it->last_returned != NULL, NULL); - g_return_val_if_fail (it->last_returned->data != STOLEN, NULL); - g_return_val_if_fail (do_func != NULL, NULL); - g_return_val_if_fail (gst_buffer_list_is_writable (it->list), NULL); - g_assert (it->last_returned->data != GROUP_START); - - return gst_buffer_list_iterator_do_data (it, - (GstBufferListDoDataFunction) do_func_no_data, do_func, NULL); -} - /** * gst_buffer_list_iterator_merge_group: * @it: a #GstBufferListIterator diff --git a/gst/gstbufferlist.h b/gst/gstbufferlist.h index 5e3385ef56..392b338ad5 100644 --- a/gst/gstbufferlist.h +++ b/gst/gstbufferlist.h @@ -42,6 +42,7 @@ typedef struct _GstBufferListIterator GstBufferListIterator; /** * GstBufferListDoFunction: * @buffer: the #GstBuffer + * @user_data: user data * * A function for accessing the last buffer returned by * gst_buffer_list_iterator_next(). The function can leave @buffer in the list, @@ -58,29 +59,49 @@ typedef struct _GstBufferListIterator GstBufferListIterator; * Returns: the buffer to replace @buffer in the list, or NULL to remove @buffer * from the list */ -typedef GstBuffer* (*GstBufferListDoFunction) (GstBuffer * buffer); +typedef GstBuffer* (*GstBufferListDoFunction) (GstBuffer * buffer, gpointer user_data); /** - * GstBufferListDoDataFunction: - * @buffer: the #GstBuffer - * @data: the gpointer to optional user data. + * GstBufferListItem: + * @GST_BUFFER_LIST_CONTINUE: Retrieve next buffer + * @GST_BUFFER_LIST_SKIP_GROUP: Skip to next group + * @GST_BUFFER_LIST_REMOVE: Remove the current buffer + * @GST_BUFFER_LIST_END: End iteration * - * A function for accessing the last buffer returned by - * gst_buffer_list_iterator_next(). The function can leave @buffer in the list, - * replace @buffer in the list or remove @buffer from the list, depending on - * the return value. If the function returns NULL, @buffer will be removed from - * the list, otherwise @buffer will be replaced with the returned buffer. - * - * The last buffer returned by gst_buffer_list_iterator_next() will be replaced - * with the buffer returned from the function. The function takes ownership of - * @buffer and if a different value than @buffer is returned, @buffer must be - * unreffed. If NULL is returned, the buffer will be removed from the list. The - * list must be writable. - * - * Returns: the buffer to replace @buffer in the list, or NULL to remove @buffer - * from the list + * The result of the #GstBufferListFunc. */ -typedef GstBuffer* (*GstBufferListDoDataFunction) (GstBuffer * buffer, gpointer data); +typedef enum { + GST_BUFFER_LIST_CONTINUE, + GST_BUFFER_LIST_SKIP_GROUP, + GST_BUFFER_LIST_END +} GstBufferListItem; + +/** + * GstBufferListFunc: + * @buffer: pointer the buffer + * @group: the group index of @buffer + * @idx: the index in @group of @buffer + * @user_data: user data passed to gst_buffer_list_foreach() + * + * A function that will be called from gst_buffer_list_foreach(). The @buffer + * field will point to a the reference of the buffer at @idx in @group. + * + * When this function returns #GST_BUFFER_LIST_CONTINUE, the next buffer will be + * returned. When #GST_BUFFER_LIST_SKIP_GROUP is returned, all remaining buffers + * in the current group will be skipped and the first buffer of the next group + * is returned (if any). When GST_BUFFER_LIST_END is returned, + * gst_buffer_list_foreach() will return. + * + * When @buffer is set to NULL, the item will be removed from the bufferlist. + * When @buffer has been made writable, the new buffer reference can be assigned + * to @buffer. This function is responsible for unreffing the old buffer when + * removing or modifying. + * + * Returns: a #GstBufferListItem + */ +typedef GstBufferListItem (*GstBufferListFunc) (GstBuffer **buffer, guint group, guint idx, + gpointer user_data); + GType gst_buffer_list_get_type (void); @@ -170,6 +191,11 @@ gst_buffer_list_copy (const GstBufferList * list) guint gst_buffer_list_n_groups (GstBufferList *list); +void gst_buffer_list_foreach (GstBufferList *list, + GstBufferListFunc func, + gpointer user_data); +GstBuffer * gst_buffer_list_get (GstBufferList *list, guint group, guint idx); + /* iterator */ GstBufferListIterator * gst_buffer_list_iterate (GstBufferList *list); void gst_buffer_list_iterator_free (GstBufferListIterator *it); @@ -184,9 +210,8 @@ void gst_buffer_list_iterator_remove (GstBufferListIte GstBuffer * gst_buffer_list_iterator_steal (GstBufferListIterator *it); void gst_buffer_list_iterator_take (GstBufferListIterator *it, GstBuffer *buffer); -GstBuffer * gst_buffer_list_iterator_do (GstBufferListIterator *it, GstBufferListDoFunction do_func); -GstBuffer * gst_buffer_list_iterator_do_data (GstBufferListIterator *it, GstBufferListDoDataFunction do_func, - gpointer data, GDestroyNotify data_notify); +GstBuffer * gst_buffer_list_iterator_do (GstBufferListIterator *it, GstBufferListDoFunction do_func, + gpointer user_data); /* conversion */ GstBuffer * gst_buffer_list_iterator_merge_group (const GstBufferListIterator *it); diff --git a/tests/check/gst/gstbufferlist.c b/tests/check/gst/gstbufferlist.c index 6372af2bf2..395309fabf 100644 --- a/tests/check/gst/gstbufferlist.c +++ b/tests/check/gst/gstbufferlist.c @@ -424,15 +424,6 @@ GST_END_TEST; static gpointer do_data_func_data; static gboolean notified; -static void -data_notify (gpointer data) -{ - fail_unless (data != NULL); - fail_unless (data == do_data_func_data); - fail_if (notified); - notified = TRUE; -} - static GstBuffer * do_data_func (GstBuffer * buffer, gpointer data) { @@ -458,16 +449,12 @@ GST_START_TEST (test_do) gchar *data; /* error handling */ - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (NULL, NULL))); + ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (NULL, NULL, NULL))); fail_unless (buf == NULL); - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (NULL, NULL, NULL, - NULL))); fail_unless (buf == NULL); it = gst_buffer_list_iterate (list); - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, NULL))); + ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, NULL, NULL))); fail_unless (buf == NULL); - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (it, NULL, NULL, - NULL))); fail_unless (buf == NULL); /* add buffers to the list */ @@ -481,28 +468,32 @@ GST_START_TEST (test_do) /* call do-function */ it = gst_buffer_list_iterate (list); fail_unless (gst_buffer_list_iterator_next_group (it)); - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, gst_buffer_ref))); + ASSERT_CRITICAL ((buf = + gst_buffer_list_iterator_do (it, + (GstBufferListDoFunction) gst_buffer_ref, NULL))); fail_unless (buf == NULL); data = "data"; - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (it, do_data_func, - data, data_notify))); + ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, do_data_func, + data))); fail_unless (buf == NULL); fail_unless (do_data_func_data != data); buf = gst_buffer_list_iterator_next (it); fail_unless (buf == buf1); ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); - buf = gst_buffer_list_iterator_do (it, gst_buffer_ref); + buf = + gst_buffer_list_iterator_do (it, (GstBufferListDoFunction) gst_buffer_ref, + NULL); fail_unless (buf == buf1); ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 3); gst_buffer_unref (buf); - buf = gst_buffer_list_iterator_do_data (it, do_data_func, data, data_notify); + buf = gst_buffer_list_iterator_do (it, do_data_func, data); fail_unless (buf == buf1); fail_unless (do_data_func_data == data); /* do-function that return a new buffer replaces the buffer in the list */ ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); buf = gst_buffer_list_iterator_do (it, - (GstBufferListDoFunction) gst_mini_object_make_writable); + (GstBufferListDoFunction) gst_mini_object_make_writable, NULL); fail_unless (buf != buf1); ASSERT_BUFFER_REFCOUNT (buf, "buf", 1); ASSERT_BUFFER_REFCOUNT (buf, "buf1", 1); @@ -510,9 +501,12 @@ GST_START_TEST (test_do) /* do-function that return NULL removes the buffer from the list */ ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); - fail_unless (gst_buffer_list_iterator_do (it, do_func_null) == NULL); + fail_unless (gst_buffer_list_iterator_do (it, + (GstBufferListDoFunction) do_func_null, NULL) == NULL); ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 1); - ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, gst_buffer_ref))); + ASSERT_CRITICAL ((buf = + gst_buffer_list_iterator_do (it, + (GstBufferListDoFunction) gst_buffer_ref, NULL))); fail_unless (buf == NULL); fail_unless (gst_buffer_list_iterator_next (it) == NULL); gst_buffer_list_iterator_free (it); @@ -645,6 +639,134 @@ GST_START_TEST (test_merge) GST_END_TEST; +typedef struct +{ + GstBuffer *buf[3][3]; + guint iter; +} ForeachData; + +static GstBufferListItem +foreach_func1 (GstBuffer ** buffer, guint group, guint idx, ForeachData * data) +{ + fail_unless (buffer != NULL); + fail_unless (*buffer == data->buf[group][idx]); + + data->iter++; + + return GST_BUFFER_LIST_CONTINUE; +} + +static GstBufferListItem +foreach_func2 (GstBuffer ** buffer, guint group, guint idx, ForeachData * data) +{ + fail_unless (idx == 0); + fail_unless (buffer != NULL); + fail_unless (*buffer == data->buf[group][idx]); + + data->iter++; + + return GST_BUFFER_LIST_SKIP_GROUP; +} + +static GstBufferListItem +foreach_func3 (GstBuffer ** buffer, guint group, guint idx, ForeachData * data) +{ + fail_unless (group == 0); + fail_unless (idx == 0); + fail_unless (buffer != NULL); + fail_unless (*buffer == data->buf[group][idx]); + + data->iter++; + + return GST_BUFFER_LIST_END; +} + +static GstBufferListItem +foreach_func4 (GstBuffer ** buffer, guint group, guint idx, ForeachData * data) +{ + fail_unless (idx == 0); + fail_unless (buffer != NULL); + fail_unless (*buffer == data->buf[group][idx]); + + *buffer = NULL; + data->iter++; + + return GST_BUFFER_LIST_SKIP_GROUP; +} + +static GstBufferListItem +foreach_func5 (GstBuffer ** buffer, guint group, guint idx, ForeachData * data) +{ + fail_unless (buffer != NULL); + + data->iter++; + + return GST_BUFFER_LIST_CONTINUE; +} + +GST_START_TEST (test_foreach) +{ + GstBufferListIterator *it; + ForeachData data; + + /* add buffers to the list */ + it = gst_buffer_list_iterate (list); + gst_buffer_list_iterator_add_group (it); + data.buf[0][0] = gst_buffer_new (); + gst_buffer_list_iterator_add (it, data.buf[0][0]); + gst_buffer_list_iterator_add_group (it); + data.buf[1][0] = gst_buffer_new (); + gst_buffer_list_iterator_add (it, data.buf[1][0]); + data.buf[1][1] = gst_buffer_new (); + gst_buffer_list_iterator_add (it, data.buf[1][1]); + gst_buffer_list_iterator_free (it); + gst_buffer_list_iterator_add_group (it); + + fail_unless (gst_buffer_list_get (list, 0, 0) == data.buf[0][0]); + fail_unless (gst_buffer_list_get (list, 0, 1) == NULL); + fail_unless (gst_buffer_list_get (list, 1, 0) == data.buf[1][0]); + fail_unless (gst_buffer_list_get (list, 1, 1) == data.buf[1][1]); + fail_unless (gst_buffer_list_get (list, 1, 2) == NULL); + fail_unless (gst_buffer_list_get (list, 2, 0) == NULL); + fail_unless (gst_buffer_list_get (list, 2, 1) == NULL); + fail_unless (gst_buffer_list_get (list, 3, 3) == NULL); + + /* iterate everything */ + data.iter = 0; + gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func1, &data); + fail_unless (data.iter == 3); + + /* iterate only the first buffer of groups */ + data.iter = 0; + gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func2, &data); + fail_unless (data.iter == 2); + + /* iterate only the first buffer */ + data.iter = 0; + gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func3, &data); + fail_unless (data.iter == 1); + + /* remove the first buffer of each group */ + data.iter = 0; + gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func4, &data); + fail_unless (data.iter == 2); + + fail_unless (gst_buffer_list_get (list, 0, 0) == NULL); + fail_unless (gst_buffer_list_get (list, 0, 1) == NULL); + fail_unless (gst_buffer_list_get (list, 1, 0) == data.buf[1][1]); + fail_unless (gst_buffer_list_get (list, 1, 1) == NULL); + fail_unless (gst_buffer_list_get (list, 1, 2) == NULL); + fail_unless (gst_buffer_list_get (list, 2, 0) == NULL); + + /* iterate everything, just one more buffer now */ + data.iter = 0; + gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func5, &data); + fail_unless (data.iter == 1); +} + +GST_END_TEST; + + static Suite * gst_buffer_list_suite (void) { @@ -660,6 +782,7 @@ gst_buffer_list_suite (void) tcase_add_test (tc_chain, test_take); tcase_add_test (tc_chain, test_do); tcase_add_test (tc_chain, test_merge); + tcase_add_test (tc_chain, test_foreach); return s; } diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index fec9c77f2a..ecfd130cab 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -87,12 +87,14 @@ EXPORTS gst_buffer_is_metadata_writable gst_buffer_is_span_fast gst_buffer_join + gst_buffer_list_foreach + gst_buffer_list_get gst_buffer_list_get_type + gst_buffer_list_item_get_type gst_buffer_list_iterate gst_buffer_list_iterator_add gst_buffer_list_iterator_add_group gst_buffer_list_iterator_do - gst_buffer_list_iterator_do_data gst_buffer_list_iterator_free gst_buffer_list_iterator_merge_group gst_buffer_list_iterator_n_buffers