2 * Copyright (C) 2009 Axis Communications <dev-gstreamer at axis dot com>
3 * @author Jonas Holmberg <jonas dot holmberg at axis dot com>
5 * gstbufferlist.c: Buffer list
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 * SECTION:gstbufferlist
25 * @short_description: Grouped scatter data buffer type for data-passing
26 * @see_also: #GstPad, #GstMiniObject
28 * Buffer lists are units of grouped scatter/gather data transfer in
31 * Buffer lists are created with gst_buffer_list_new() and filled with data
32 * using a #GstBufferListIterator. The iterator has no current buffer; its
33 * cursor position lies between buffers, immediately before the buffer that
34 * would be returned by gst_buffer_list_iterator_next(). After iterating to the
35 * end of a group the iterator must be advanced to the next group by a call to
36 * gst_buffer_list_iterator_next_group() before any further calls to
37 * gst_buffer_list_iterator_next() can return buffers again. The cursor position
38 * of a newly created iterator lies before the first group; a call to
39 * gst_buffer_list_iterator_next_group() is necessary before calls to
40 * gst_buffer_list_iterator_next() can return buffers.
44 * +--- group0 ----------------------+--- group1 ------------+
45 * | buffer0 buffer1 buffer2 | buffer3 buffer4 |
47 * Iterator positions between buffers
51 * The gst_buffer_list_iterator_remove(), gst_buffer_list_iterator_steal(),
52 * gst_buffer_list_iterator_take() and gst_buffer_list_iterator_do() functions
53 * are not defined in terms of the cursor position; they operate on the last
54 * element returned from gst_buffer_list_iterator_next().
56 * The basic use pattern of creating a buffer list with an iterator is as
60 * <title>Creating a buffer list</title>
62 * GstBufferList *list;
63 * GstBufferListIterator *it;
65 * list = gst_buffer_list_new ();
66 * it = gst_buffer_list_iterate (list);
67 * gst_buffer_list_iterator_add_group (it);
68 * gst_buffer_list_iterator_add (it, header1);
69 * gst_buffer_list_iterator_add (it, data1);
70 * gst_buffer_list_iterator_add_group (it);
71 * gst_buffer_list_iterator_add (it, header2);
72 * gst_buffer_list_iterator_add (it, data2);
73 * gst_buffer_list_iterator_add_group (it);
74 * gst_buffer_list_iterator_add (it, header3);
75 * gst_buffer_list_iterator_add (it, data3);
77 * gst_buffer_list_iterator_free (it);
81 * The basic use pattern of iterating over a buffer list is as follows:
84 * <title>Iterating a buffer list</title>
86 * GstBufferListIterator *it;
88 * it = gst_buffer_list_iterate (list);
89 * while (gst_buffer_list_iterator_next_group (it)) {
90 * while ((buffer = gst_buffer_list_iterator_next (it)) != NULL) {
91 * do_something_with_buffer (buffer);
94 * gst_buffer_list_iterator_free (it);
98 * The basic use pattern of modifying a buffer in a list is as follows:
101 * <title>Modifying the data of the first buffer in a list</title>
103 * GstBufferListIterator *it;
105 * list = gst_buffer_list_make_writable (list);
106 * it = gst_buffer_list_iterate (list);
107 * if (gst_buffer_list_iterator_next_group (it)) {
110 * buf = gst_buffer_list_iterator_next (it);
112 * buf = gst_buffer_list_iterator_do (it,
113 * (GstBufferListDoFunction) gst_mini_object_make_writable, NULL);
114 * modify_data (GST_BUFFER_DATA (buf));
117 * gst_buffer_list_iterator_free (it);
123 #include "gst_private.h"
125 #include "gstbuffer.h"
126 #include "gstbufferlist.h"
128 #define GST_CAT_DEFAULT GST_CAT_BUFFER_LIST
130 #define GROUP_START NULL
131 static gconstpointer STOLEN = "";
136 * Opaque list of grouped buffers.
140 struct _GstBufferList
142 GstMiniObject mini_object;
147 struct _GstBufferListClass
149 GstMiniObjectClass mini_object_class;
153 * GstBufferListIterator:
155 * Opaque iterator for a #GstBufferList.
159 struct _GstBufferListIterator
163 GList *last_returned;
166 static GType _gst_buffer_list_type = 0;
168 G_DEFINE_TYPE (GstBufferList, gst_buffer_list, GST_TYPE_MINI_OBJECT);
171 _gst_buffer_list_initialize (void)
173 GType type = gst_buffer_list_get_type ();
175 g_type_class_ref (type);
176 _gst_buffer_list_type = type;
180 gst_buffer_list_init (GstBufferList * list)
182 list->buffers = g_queue_new ();
184 GST_LOG ("init %p", list);
188 gst_buffer_list_finalize (GstBufferList * list)
192 g_return_if_fail (list != NULL);
194 GST_LOG ("finalize %p", list);
196 tmp = list->buffers->head;
198 if (tmp->data != GROUP_START && tmp->data != STOLEN) {
199 gst_buffer_unref (GST_BUFFER_CAST (tmp->data));
203 g_queue_free (list->buffers);
205 /* Not chaining up because GstMiniObject::finalize() does nothing
206 GST_MINI_OBJECT_CLASS (gst_buffer_list_parent_class)->finalize
207 (GST_MINI_OBJECT_CAST (list));*/
210 static GstBufferList *
211 _gst_buffer_list_copy (GstBufferList * list)
213 GstBufferList *list_copy;
214 GQueue *buffers_copy;
217 g_return_val_if_fail (list != NULL, NULL);
219 /* shallow copy of list and pointers */
220 buffers_copy = g_queue_copy (list->buffers);
222 /* ref all buffers in the list */
223 tmp = list->buffers->head;
225 if (tmp->data != GROUP_START && tmp->data != STOLEN) {
226 tmp->data = gst_buffer_ref (GST_BUFFER_CAST (tmp->data));
228 tmp = g_list_next (tmp);
231 list_copy = gst_buffer_list_new ();
232 g_queue_free (list_copy->buffers);
233 list_copy->buffers = buffers_copy;
239 gst_buffer_list_class_init (GstBufferListClass * list_class)
241 list_class->mini_object_class.copy =
242 (GstMiniObjectCopyFunction) _gst_buffer_list_copy;
243 list_class->mini_object_class.finalize =
244 (GstMiniObjectFinalizeFunction) gst_buffer_list_finalize;
248 * gst_buffer_list_new:
250 * Creates a new, empty #GstBufferList. The caller is responsible for unreffing
251 * the returned #GstBufferList.
253 * Free-function: gst_buffer_list_unref
255 * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref()
261 gst_buffer_list_new (void)
265 list = (GstBufferList *) gst_mini_object_new (_gst_buffer_list_type);
267 GST_LOG ("new %p", list);
273 * gst_buffer_list_n_groups:
274 * @list: a #GstBufferList
276 * Returns the number of groups in @list.
278 * Returns: the number of groups in the buffer list
283 gst_buffer_list_n_groups (GstBufferList * list)
288 g_return_val_if_fail (list != NULL, 0);
290 tmp = list->buffers->head;
293 if (tmp->data == GROUP_START) {
296 tmp = g_list_next (tmp);
303 * gst_buffer_list_foreach:
304 * @list: a #GstBufferList
305 * @func: (scope call): a #GstBufferListFunc to call
306 * @user_data: (closure): user data passed to @func
308 * Call @func with @data for each buffer in @list.
310 * @func can modify the passed buffer pointer or its contents. The return value
311 * of @func define if this function returns or if the remaining buffers in a
312 * group should be skipped.
317 gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
322 GstBufferListItem res;
324 g_return_if_fail (list != NULL);
325 g_return_if_fail (func != NULL);
327 next = list->buffers->head;
333 next = g_list_next (tmp);
337 if (buffer == GROUP_START) {
341 } else if (buffer == STOLEN)
346 /* need to decrement the indices */
347 res = func (&buffer, group - 1, idx - 1, user_data);
349 if (G_UNLIKELY (buffer != tmp->data)) {
350 /* the function changed the buffer */
351 if (buffer == NULL) {
352 /* we were asked to remove the item */
353 g_queue_delete_link (list->buffers, tmp);
356 /* change the buffer */
362 case GST_BUFFER_LIST_CONTINUE:
364 case GST_BUFFER_LIST_SKIP_GROUP:
365 while (next && next->data != GROUP_START)
366 next = g_list_next (next);
368 case GST_BUFFER_LIST_END:
375 * gst_buffer_list_get:
376 * @list: a #GstBufferList
378 * @idx: the index in @group
380 * Get the buffer at @idx in @group.
382 * Note that this function is not efficient for iterating over the entire list.
383 * Use an iterator or gst_buffer_list_foreach() instead.
385 * Returns: (transfer none): the buffer at @idx in @group or NULL when there
386 * is no buffer. The buffer remains valid as long as @list is valid.
391 gst_buffer_list_get (GstBufferList * list, guint group, guint idx)
396 g_return_val_if_fail (list != NULL, NULL);
398 tmp = list->buffers->head;
401 if (tmp->data == GROUP_START) {
402 if (cgroup == group) {
403 /* we found the group */
404 tmp = g_list_next (tmp);
406 while (tmp && tmp->data != GROUP_START) {
407 if (tmp->data != STOLEN) {
409 return GST_BUFFER_CAST (tmp->data);
413 tmp = g_list_next (tmp);
422 tmp = g_list_next (tmp);
427 static GstBufferListIterator *
428 gst_buffer_list_iterator_copy (const GstBufferListIterator * it)
430 GstBufferListIterator *ret;
432 ret = g_slice_new (GstBufferListIterator);
433 ret->list = it->list;
434 ret->next = it->next;
435 ret->last_returned = it->last_returned;
441 gst_buffer_list_iterator_get_type (void)
443 static GType type = 0;
445 if (G_UNLIKELY (type == 0)) {
446 type = g_boxed_type_register_static ("GstBufferListIterator",
447 (GBoxedCopyFunc) gst_buffer_list_iterator_copy,
448 (GBoxedFreeFunc) gst_buffer_list_iterator_free);
455 * gst_buffer_list_iterate:
456 * @list: a #GstBufferList
458 * Iterate the buffers in @list. The owner of the iterator must also be the
459 * owner of a reference to @list while the returned iterator is in use.
461 * Free-function: gst_buffer_list_iterator_free
463 * Returns: (transfer full): a new #GstBufferListIterator of the buffers in
464 * @list. gst_buffer_list_iterator_free() after usage
468 GstBufferListIterator *
469 gst_buffer_list_iterate (GstBufferList * list)
471 GstBufferListIterator *it;
473 g_return_val_if_fail (list != NULL, NULL);
475 it = g_slice_new (GstBufferListIterator);
477 it->next = list->buffers->head;
478 it->last_returned = NULL;
484 * gst_buffer_list_iterator_free:
485 * @it: (transfer full): the #GstBufferListIterator to free
492 gst_buffer_list_iterator_free (GstBufferListIterator * it)
494 g_return_if_fail (it != NULL);
496 g_slice_free (GstBufferListIterator, it);
500 * gst_buffer_list_iterator_n_buffers:
501 * @it: a #GstBufferListIterator
503 * Returns the number of buffers left to iterate in the current group. I.e. the
504 * number of calls that can be made to gst_buffer_list_iterator_next() before
507 * This function will not move the implicit cursor or in any other way affect
508 * the state of the iterator @it.
510 * Returns: the number of buffers left to iterate in the current group
515 gst_buffer_list_iterator_n_buffers (const GstBufferListIterator * it)
520 g_return_val_if_fail (it != NULL, 0);
524 while (tmp && tmp->data != GROUP_START) {
525 if (tmp->data != STOLEN) {
528 tmp = g_list_next (tmp);
535 * gst_buffer_list_iterator_add:
536 * @it: a #GstBufferListIterator
537 * @buffer: (transfer full): a #GstBuffer
539 * Inserts @buffer into the #GstBufferList iterated with @it. The buffer is
540 * inserted into the current group, immediately before the buffer that would be
541 * returned by gst_buffer_list_iterator_next(). The buffer is inserted before
542 * the implicit cursor, a subsequent call to gst_buffer_list_iterator_next()
543 * will return the buffer after the inserted buffer, if any.
545 * This function takes ownership of @buffer.
550 gst_buffer_list_iterator_add (GstBufferListIterator * it, GstBuffer * buffer)
552 g_return_if_fail (it != NULL);
553 g_return_if_fail (buffer != NULL);
555 /* adding before the first group start is not allowed */
556 g_return_if_fail (it->next != it->list->buffers->head);
558 /* cheap insert into the GQueue */
559 if (it->next != NULL) {
560 g_queue_insert_before (it->list->buffers, it->next, buffer);
562 g_queue_push_tail (it->list->buffers, buffer);
567 * gst_buffer_list_iterator_add_list:
568 * @it: a #GstBufferListIterator
569 * @list: (transfer full) (element-type Gst.Buffer): a #GList of buffers
571 * Inserts @list of buffers into the #GstBufferList iterated with @it. The list is
572 * inserted into the current group, immediately before the buffer that would be
573 * returned by gst_buffer_list_iterator_next(). The list is inserted before
574 * the implicit cursor, a subsequent call to gst_buffer_list_iterator_next()
575 * will return the buffer after the last buffer of the inserted list, if any.
577 * This function takes ownership of @list and all its buffers.
582 gst_buffer_list_iterator_add_list (GstBufferListIterator * it, GList * list)
587 g_return_if_fail (it != NULL);
588 g_return_if_fail (it->next != it->list->buffers->head);
601 last->next = it->next;
602 list->prev = it->next->prev;
603 it->next->prev = last;
605 list->prev->next = list;
607 it->list->buffers->tail->next = list;
608 list->prev = it->list->buffers->tail;
609 it->list->buffers->tail = last;
611 it->list->buffers->length += len;
615 * gst_buffer_list_iterator_add_group:
616 * @it: a #GstBufferListIterator
618 * Inserts a new, empty group into the #GstBufferList iterated with @it. The
619 * group is inserted immediately before the group that would be returned by
620 * gst_buffer_list_iterator_next_group(). A subsequent call to
621 * gst_buffer_list_iterator_next_group() will advance the iterator to the group
622 * after the inserted group, if any.
627 gst_buffer_list_iterator_add_group (GstBufferListIterator * it)
629 g_return_if_fail (it != NULL);
631 /* advance iterator to next group start */
632 while (it->next != NULL && it->next->data != GROUP_START) {
633 it->next = g_list_next (it->next);
636 /* cheap insert of a group start into the GQueue */
637 if (it->next != NULL) {
638 g_queue_insert_before (it->list->buffers, it->next, GROUP_START);
640 g_queue_push_tail (it->list->buffers, GROUP_START);
645 * gst_buffer_list_iterator_next:
646 * @it: a #GstBufferListIterator
648 * Returns the next buffer in the list iterated with @it. If the iterator is at
649 * the end of a group, NULL will be returned. This function may be called
650 * repeatedly to iterate through the current group.
652 * The caller will not get a new ref to the returned #GstBuffer and must not
655 * Returns: (transfer none): the next buffer in the current group of the
656 * buffer list, or NULL
661 gst_buffer_list_iterator_next (GstBufferListIterator * it)
665 g_return_val_if_fail (it != NULL, NULL);
667 while (it->next != NULL && it->next->data != GROUP_START &&
668 it->next->data == STOLEN) {
669 it->next = g_list_next (it->next);
672 if (it->next == NULL || it->next->data == GROUP_START) {
676 buffer = GST_BUFFER_CAST (it->next->data);
678 it->last_returned = it->next;
679 it->next = g_list_next (it->next);
685 it->last_returned = NULL;
691 * gst_buffer_list_iterator_next_group:
692 * @it: a #GstBufferListIterator
694 * Advance the iterator @it to the first buffer in the next group. If the
695 * iterator is at the last group, FALSE will be returned. This function may be
696 * called repeatedly to iterate through the groups in a buffer list.
698 * Returns: TRUE if the iterator could be advanced to the next group, FALSE if
699 * the iterator was already at the last group
704 gst_buffer_list_iterator_next_group (GstBufferListIterator * it)
706 g_return_val_if_fail (it != NULL, FALSE);
708 /* advance iterator to next group start */
709 while (it->next != NULL && it->next->data != GROUP_START) {
710 it->next = g_list_next (it->next);
714 /* move one step beyond the group start */
715 it->next = g_list_next (it->next);
718 it->last_returned = NULL;
720 return (it->next != NULL);
724 * gst_buffer_list_iterator_remove:
725 * @it: a #GstBufferListIterator
727 * Removes the last buffer returned by gst_buffer_list_iterator_next() from
728 * the #GstBufferList iterated with @it. gst_buffer_list_iterator_next() must
729 * have been called on @it before this function is called. This function can
730 * only be called once per call to gst_buffer_list_iterator_next().
732 * The removed buffer is unreffed.
737 gst_buffer_list_iterator_remove (GstBufferListIterator * it)
739 g_return_if_fail (it != NULL);
740 g_return_if_fail (it->last_returned != NULL);
741 g_assert (it->last_returned->data != GROUP_START);
743 if (it->last_returned->data != STOLEN) {
744 gst_buffer_unref (it->last_returned->data);
746 g_queue_delete_link (it->list->buffers, it->last_returned);
747 it->last_returned = NULL;
751 * gst_buffer_list_iterator_take:
752 * @it: a #GstBufferListIterator
753 * @buffer: (transfer full): a #GstBuffer
755 * Replaces the last buffer returned by gst_buffer_list_iterator_next() with
756 * @buffer in the #GstBufferList iterated with @it and takes ownership of
757 * @buffer. gst_buffer_list_iterator_next() must have been called on @it before
758 * this function is called. gst_buffer_list_iterator_remove() must not have been
759 * called since the last call to gst_buffer_list_iterator_next().
761 * This function unrefs the replaced buffer if it has not been stolen with
762 * gst_buffer_list_iterator_steal() and takes ownership of @buffer (i.e. the
763 * refcount of @buffer is not increased).
765 * FIXME 0.11: this conditional taking-ownership is not good for bindings
770 gst_buffer_list_iterator_take (GstBufferListIterator * it, GstBuffer * buffer)
772 g_return_if_fail (it != NULL);
773 g_return_if_fail (it->last_returned != NULL);
774 g_return_if_fail (buffer != NULL);
775 g_assert (it->last_returned->data != GROUP_START);
777 if (it->last_returned->data != STOLEN) {
778 gst_buffer_unref (it->last_returned->data);
780 it->last_returned->data = buffer;
784 * gst_buffer_list_iterator_steal:
785 * @it: a #GstBufferListIterator
787 * Returns the last buffer returned by gst_buffer_list_iterator_next() without
788 * modifying the refcount of the buffer.
790 * Returns: (transfer none): the last buffer returned by
791 * gst_buffer_list_iterator_next()
796 gst_buffer_list_iterator_steal (GstBufferListIterator * it)
800 g_return_val_if_fail (it != NULL, NULL);
801 g_return_val_if_fail (it->last_returned != NULL, NULL);
802 g_return_val_if_fail (it->last_returned->data != STOLEN, NULL);
803 g_assert (it->last_returned->data != GROUP_START);
805 buffer = it->last_returned->data;
806 it->last_returned->data = (gpointer) STOLEN;
812 * gst_buffer_list_iterator_do:
813 * @it: a #GstBufferListIterator
814 * @do_func: (scope call): the function to be called
815 * @user_data: (closure): the gpointer to optional user data.
817 * Calls the given function for the last buffer returned by
818 * gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have
819 * been called on @it before this function is called.
820 * gst_buffer_list_iterator_remove() and gst_buffer_list_iterator_steal() must
821 * not have been called since the last call to gst_buffer_list_iterator_next().
823 * See #GstBufferListDoFunction for more details.
825 * Returns: (transfer none): the return value from @do_func
830 gst_buffer_list_iterator_do (GstBufferListIterator * it,
831 GstBufferListDoFunction do_func, gpointer user_data)
835 g_return_val_if_fail (it != NULL, NULL);
836 g_return_val_if_fail (it->last_returned != NULL, NULL);
837 g_return_val_if_fail (it->last_returned->data != STOLEN, NULL);
838 g_return_val_if_fail (do_func != NULL, NULL);
839 g_return_val_if_fail (gst_buffer_list_is_writable (it->list), NULL);
840 g_assert (it->last_returned->data != GROUP_START);
842 buffer = gst_buffer_list_iterator_steal (it);
843 buffer = do_func (buffer, user_data);
844 if (buffer == NULL) {
845 gst_buffer_list_iterator_remove (it);
847 gst_buffer_list_iterator_take (it, buffer);
854 * gst_buffer_list_iterator_merge_group:
855 * @it: a #GstBufferListIterator
857 * Merge a buffer list group into a normal #GstBuffer by copying its metadata
858 * and memcpying its data into consecutive memory. All buffers in the current
859 * group after the implicit cursor will be merged into one new buffer. The
860 * metadata of the new buffer will be a copy of the metadata of the buffer that
861 * would be returned by gst_buffer_list_iterator_next(). If there is no buffer
862 * in the current group after the implicit cursor, NULL will be returned.
864 * This function will not move the implicit cursor or in any other way affect
865 * the state of the iterator @it or the list.
867 * Returns: (transfer full): a new #GstBuffer, gst_buffer_unref() after usage,
873 gst_buffer_list_iterator_merge_group (const GstBufferListIterator * it)
880 g_return_val_if_fail (it != NULL, NULL);
882 /* calculate size of merged buffer */
885 while (tmp && tmp->data != GROUP_START) {
886 if (tmp->data != STOLEN) {
887 size += GST_BUFFER_SIZE (tmp->data);
889 tmp = g_list_next (tmp);
896 /* allocate a new buffer */
897 buf = gst_buffer_new_and_alloc (size);
899 /* copy metadata from the next buffer after the implicit cursor */
900 gst_buffer_copy_metadata (buf, GST_BUFFER_CAST (it->next->data),
901 GST_BUFFER_COPY_ALL);
903 /* copy data of all buffers before the next group start into the new buffer */
904 ptr = GST_BUFFER_DATA (buf);
907 if (tmp->data != STOLEN) {
908 memcpy (ptr, GST_BUFFER_DATA (tmp->data), GST_BUFFER_SIZE (tmp->data));
909 ptr += GST_BUFFER_SIZE (tmp->data);
911 tmp = g_list_next (tmp);
912 } while (tmp && tmp->data != GROUP_START);