Commit 6438f6f9 authored by Wim Taymans's avatar Wim Taymans

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.
parent 17f794de
...@@ -215,14 +215,21 @@ gst_buffer_copy_flags_get_type ...@@ -215,14 +215,21 @@ gst_buffer_copy_flags_get_type
GstBufferList GstBufferList
GstBufferListIterator GstBufferListIterator
GstBufferListDoFunction GstBufferListDoFunction
GstBufferListDoDataFunction
gst_buffer_list_new gst_buffer_list_new
gst_buffer_list_ref gst_buffer_list_ref
gst_buffer_list_unref gst_buffer_list_unref
gst_buffer_list_copy gst_buffer_list_copy
gst_buffer_list_is_writable gst_buffer_list_is_writable
gst_buffer_list_make_writable gst_buffer_list_make_writable
gst_buffer_list_n_groups gst_buffer_list_n_groups
GstBufferListItem
GstBufferListFunc
gst_buffer_list_foreach
gst_buffer_list_get
gst_buffer_list_iterate gst_buffer_list_iterate
gst_buffer_list_iterator_free gst_buffer_list_iterator_free
gst_buffer_list_iterator_n_buffers gst_buffer_list_iterator_n_buffers
...@@ -234,7 +241,6 @@ gst_buffer_list_iterator_remove ...@@ -234,7 +241,6 @@ gst_buffer_list_iterator_remove
gst_buffer_list_iterator_steal gst_buffer_list_iterator_steal
gst_buffer_list_iterator_take gst_buffer_list_iterator_take
gst_buffer_list_iterator_do gst_buffer_list_iterator_do
gst_buffer_list_iterator_do_data
gst_buffer_list_iterator_merge_group gst_buffer_list_iterator_merge_group
<SUBSECTION Standard> <SUBSECTION Standard>
GstBufferListClass GstBufferListClass
...@@ -245,7 +251,9 @@ GST_IS_BUFFER_LIST ...@@ -245,7 +251,9 @@ GST_IS_BUFFER_LIST
GST_IS_BUFFER_LIST_CLASS GST_IS_BUFFER_LIST_CLASS
GST_TYPE_BUFFER_LIST GST_TYPE_BUFFER_LIST
GST_BUFFER_LIST_CAST GST_BUFFER_LIST_CAST
GST_TYPE_BUFFER_LIST_ITEM
<SUBSECTION Private> <SUBSECTION Private>
gst_buffer_list_item_get_type
gst_buffer_list_get_type gst_buffer_list_get_type
</SECTION> </SECTION>
......
...@@ -289,6 +289,127 @@ gst_buffer_list_n_groups (GstBufferList * list) ...@@ -289,6 +289,127 @@ gst_buffer_list_n_groups (GstBufferList * list)
return n; 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 GType
gst_buffer_list_get_type (void) gst_buffer_list_get_type (void)
{ {
...@@ -474,9 +595,10 @@ gst_buffer_list_iterator_next (GstBufferListIterator * it) ...@@ -474,9 +595,10 @@ gst_buffer_list_iterator_next (GstBufferListIterator * it)
return buffer; return buffer;
no_buffer: no_buffer:
it->last_returned = NULL; {
it->last_returned = NULL;
return NULL; return NULL;
}
} }
/** /**
...@@ -590,11 +712,10 @@ gst_buffer_list_iterator_steal (GstBufferListIterator * it) ...@@ -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 * @it: a #GstBufferListIterator
* @do_func: the function to be called * @do_func: the function to be called
* @data: the gpointer to optional user data. * @user_data: the gpointer to optional user data.
* @data_notify: function to be called when @data is no longer used
* *
* Calls the given function for the last buffer returned by * Calls the given function for the last buffer returned by
* gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have * gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have
...@@ -604,15 +725,11 @@ gst_buffer_list_iterator_steal (GstBufferListIterator * it) ...@@ -604,15 +725,11 @@ gst_buffer_list_iterator_steal (GstBufferListIterator * it)
* *
* See #GstBufferListDoFunction for more details. * 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 * Returns: the return value from @do_func
*/ */
GstBuffer * GstBuffer *
gst_buffer_list_iterator_do_data (GstBufferListIterator * it, gst_buffer_list_iterator_do (GstBufferListIterator * it,
GstBufferListDoDataFunction do_func, gpointer data, GstBufferListDoFunction do_func, gpointer user_data)
GDestroyNotify data_notify)
{ {
GstBuffer *buffer; GstBuffer *buffer;
...@@ -624,56 +741,16 @@ gst_buffer_list_iterator_do_data (GstBufferListIterator * it, ...@@ -624,56 +741,16 @@ gst_buffer_list_iterator_do_data (GstBufferListIterator * it,
g_assert (it->last_returned->data != GROUP_START); g_assert (it->last_returned->data != GROUP_START);
buffer = gst_buffer_list_iterator_steal (it); buffer = gst_buffer_list_iterator_steal (it);
buffer = do_func (buffer, data); buffer = do_func (buffer, user_data);
if (buffer == NULL) { if (buffer == NULL) {
gst_buffer_list_iterator_remove (it); gst_buffer_list_iterator_remove (it);
} else { } else {
gst_buffer_list_iterator_take (it, buffer); gst_buffer_list_iterator_take (it, buffer);
} }
if (data_notify != NULL) {
data_notify (data);
}
return buffer; 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: * gst_buffer_list_iterator_merge_group:
* @it: a #GstBufferListIterator * @it: a #GstBufferListIterator
......
...@@ -42,6 +42,7 @@ typedef struct _GstBufferListIterator GstBufferListIterator; ...@@ -42,6 +42,7 @@ typedef struct _GstBufferListIterator GstBufferListIterator;
/** /**
* GstBufferListDoFunction: * GstBufferListDoFunction:
* @buffer: the #GstBuffer * @buffer: the #GstBuffer
* @user_data: user data
* *
* A function for accessing the last buffer returned by * A function for accessing the last buffer returned by
* gst_buffer_list_iterator_next(). The function can leave @buffer in the list, * gst_buffer_list_iterator_next(). The function can leave @buffer in the list,
...@@ -58,29 +59,49 @@ typedef struct _GstBufferListIterator GstBufferListIterator; ...@@ -58,29 +59,49 @@ typedef struct _GstBufferListIterator GstBufferListIterator;
* Returns: the buffer to replace @buffer in the list, or NULL to remove @buffer * Returns: the buffer to replace @buffer in the list, or NULL to remove @buffer
* from the list * from the list
*/ */
typedef GstBuffer* (*GstBufferListDoFunction) (GstBuffer * buffer); typedef GstBuffer* (*GstBufferListDoFunction) (GstBuffer * buffer, gpointer user_data);
/** /**
* GstBufferListDoDataFunction: * GstBufferListItem:
* @buffer: the #GstBuffer * @GST_BUFFER_LIST_CONTINUE: Retrieve next buffer
* @data: the gpointer to optional user data. * @GST_BUFFER_LIST_SKIP_GROUP: Skip to next group
* * @GST_BUFFER_LIST_REMOVE: Remove the current buffer
* A function for accessing the last buffer returned by * @GST_BUFFER_LIST_END: End iteration
* 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 * The result of the #GstBufferListFunc.
* from the list
*/ */
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); GType gst_buffer_list_get_type (void);
...@@ -170,6 +191,11 @@ gst_buffer_list_copy (const GstBufferList * list) ...@@ -170,6 +191,11 @@ gst_buffer_list_copy (const GstBufferList * list)
guint gst_buffer_list_n_groups (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 */ /* iterator */
GstBufferListIterator * gst_buffer_list_iterate (GstBufferList *list); GstBufferListIterator * gst_buffer_list_iterate (GstBufferList *list);
void gst_buffer_list_iterator_free (GstBufferListIterator *it); void gst_buffer_list_iterator_free (GstBufferListIterator *it);
...@@ -184,9 +210,8 @@ void gst_buffer_list_iterator_remove (GstBufferListIte ...@@ -184,9 +210,8 @@ void gst_buffer_list_iterator_remove (GstBufferListIte
GstBuffer * gst_buffer_list_iterator_steal (GstBufferListIterator *it); GstBuffer * gst_buffer_list_iterator_steal (GstBufferListIterator *it);
void gst_buffer_list_iterator_take (GstBufferListIterator *it, GstBuffer *buffer); 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 (GstBufferListIterator *it, GstBufferListDoFunction do_func,
GstBuffer * gst_buffer_list_iterator_do_data (GstBufferListIterator *it, GstBufferListDoDataFunction do_func, gpointer user_data);
gpointer data, GDestroyNotify data_notify);
/* conversion */ /* conversion */
GstBuffer * gst_buffer_list_iterator_merge_group (const GstBufferListIterator *it); GstBuffer * gst_buffer_list_iterator_merge_group (const GstBufferListIterator *it);
......
...@@ -424,15 +424,6 @@ GST_END_TEST; ...@@ -424,15 +424,6 @@ GST_END_TEST;
static gpointer do_data_func_data; static gpointer do_data_func_data;
static gboolean notified; 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 * static GstBuffer *
do_data_func (GstBuffer * buffer, gpointer data) do_data_func (GstBuffer * buffer, gpointer data)
{ {
...@@ -458,16 +449,12 @@ GST_START_TEST (test_do) ...@@ -458,16 +449,12 @@ GST_START_TEST (test_do)
gchar *data; gchar *data;
/* error handling */ /* 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); fail_unless (buf == NULL);
ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (NULL, NULL, NULL,
NULL)));
fail_unless (buf == NULL); fail_unless (buf == NULL);
it = gst_buffer_list_iterate (list); 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); fail_unless (buf == NULL);
ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (it, NULL, NULL,
NULL)));
fail_unless (buf == NULL); fail_unless (buf == NULL);
/* add buffers to the list */ /* add buffers to the list */
...@@ -481,28 +468,32 @@ GST_START_TEST (test_do) ...@@ -481,28 +468,32 @@ GST_START_TEST (test_do)
/* call do-function */ /* call do-function */
it = gst_buffer_list_iterate (list); it = gst_buffer_list_iterate (list);
fail_unless (gst_buffer_list_iterator_next_group (it)); 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); fail_unless (buf == NULL);
data = "data"; data = "data";
ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do_data (it, do_data_func, ASSERT_CRITICAL ((buf = gst_buffer_list_iterator_do (it, do_data_func,
data, data_notify))); data)));
fail_unless (buf == NULL); fail_unless (buf == NULL);
fail_unless (do_data_func_data != data); fail_unless (do_data_func_data != data);
buf = gst_buffer_list_iterator_next (it); buf = gst_buffer_list_iterator_next (it);
fail_unless (buf == buf1); fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); 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); fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 3); ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 3);
gst_buffer_unref (buf); 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 (buf == buf1);
fail_unless (do_data_func_data == data); fail_unless (do_data_func_data == data);
/* do-function that return a new buffer replaces the buffer in the list */ /* do-function that return a new buffer replaces the buffer in the list */
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2);
buf = gst_buffer_list_iterator_do (it, buf = gst_buffer_list_iterator_do (it,
(GstBufferListDoFunction) gst_mini_object_make_writable); (GstBufferListDoFunction) gst_mini_object_make_writable, NULL);
fail_unless (buf != buf1); fail_unless (buf != buf1);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1); ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
ASSERT_BUFFER_REFCOUNT (buf, "buf1", 1); ASSERT_BUFFER_REFCOUNT (buf, "buf1", 1);
...@@ -510,9 +501,12 @@ GST_START_TEST (test_do) ...@@ -510,9 +501,12 @@ GST_START_TEST (test_do)
/* do-function that return NULL removes the buffer from the list */ /* do-function that return NULL removes the buffer from the list */
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2); 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_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 (buf == NULL);
fail_unless (gst_buffer_list_iterator_next (it) == NULL); fail_unless (gst_buffer_list_iterator_next (it) == NULL);
gst_buffer_list_iterator_free (it); gst_buffer_list_iterator_free (it);
...@@ -645,6 +639,134 @@ GST_START_TEST (test_merge) ...@@ -645,6 +639,134 @@ GST_START_TEST (test_merge)
GST_END_TEST; 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);