Initialize Tizen 2.3
[framework/multimedia/gstreamer0.10.git] / mobile / gst / gstbufferlist.c
1 /* GStreamer
2  * Copyright (C) 2009 Axis Communications <dev-gstreamer at axis dot com>
3  * @author Jonas Holmberg <jonas dot holmberg at axis dot com>
4  *
5  * gstbufferlist.c: Buffer list
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /**
24  * SECTION:gstbufferlist
25  * @short_description: Grouped scatter data buffer type for data-passing
26  * @see_also: #GstPad, #GstMiniObject
27  *
28  * Buffer lists are units of grouped scatter/gather data transfer in
29  * GStreamer.
30  *
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.
41  *
42  * <informalfigure>
43  *   <programlisting>
44  *      +--- group0 ----------------------+--- group1 ------------+
45  *      |   buffer0   buffer1   buffer2   |   buffer3   buffer4   |
46  *    ^   ^         ^         ^         ^   ^         ^         ^
47  *    Iterator positions between buffers
48  *   </programlisting>
49  * </informalfigure>
50  *
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().
55  *
56  * The basic use pattern of creating a buffer list with an iterator is as
57  * follows:
58  *
59  * <example>
60  * <title>Creating a buffer list</title>
61  *   <programlisting>
62  *    GstBufferList *list;
63  *    GstBufferListIterator *it;
64  *
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);
76  *    ...
77  *    gst_buffer_list_iterator_free (it);
78  *   </programlisting>
79  * </example>
80  *
81  * The basic use pattern of iterating over a buffer list is as follows:
82  *
83  * <example>
84  * <title>Iterating a buffer list</title>
85  *   <programlisting>
86  *    GstBufferListIterator *it;
87  *
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);
92  *      }
93  *    }
94  *    gst_buffer_list_iterator_free (it);
95  *   </programlisting>
96  * </example>
97  *
98  * The basic use pattern of modifying a buffer in a list is as follows:
99  *
100  * <example>
101  * <title>Modifying the data of the first buffer in a list</title>
102  *   <programlisting>
103  *    GstBufferListIterator *it;
104  *
105  *    list = gst_buffer_list_make_writable (list);
106  *    it = gst_buffer_list_iterate (list);
107  *    if (gst_buffer_list_iterator_next_group (it)) {
108  *      GstBuffer *buf
109  *
110  *      buf = gst_buffer_list_iterator_next (it);
111  *      if (buf != NULL) {
112  *        buf = gst_buffer_list_iterator_do (it,
113  *            (GstBufferListDoFunction) gst_mini_object_make_writable, NULL);
114  *        modify_data (GST_BUFFER_DATA (buf));
115  *      }
116  *    }
117  *    gst_buffer_list_iterator_free (it);
118  *   </programlisting>
119  * </example>
120  *
121  * Since: 0.10.24
122  */
123 #include "gst_private.h"
124
125 #include "gstbuffer.h"
126 #include "gstbufferlist.h"
127
128 #define GST_CAT_DEFAULT GST_CAT_BUFFER_LIST
129
130 #define GROUP_START NULL
131 static gconstpointer STOLEN = "";
132
133 /**
134  * GstBufferList:
135  *
136  * Opaque list of grouped buffers.
137  *
138  * Since: 0.10.24
139  */
140 struct _GstBufferList
141 {
142   GstMiniObject mini_object;
143
144   GQueue *buffers;
145 };
146
147 struct _GstBufferListClass
148 {
149   GstMiniObjectClass mini_object_class;
150 };
151
152 /**
153  * GstBufferListIterator:
154  *
155  * Opaque iterator for a #GstBufferList.
156  *
157  * Since: 0.10.24
158  */
159 struct _GstBufferListIterator
160 {
161   GstBufferList *list;
162   GList *next;
163   GList *last_returned;
164 };
165
166 static GType _gst_buffer_list_type = 0;
167
168 G_DEFINE_TYPE (GstBufferList, gst_buffer_list, GST_TYPE_MINI_OBJECT);
169
170 void
171 _gst_buffer_list_initialize (void)
172 {
173   GType type = gst_buffer_list_get_type ();
174
175   g_type_class_ref (type);
176   _gst_buffer_list_type = type;
177 }
178
179 static void
180 gst_buffer_list_init (GstBufferList * list)
181 {
182   list->buffers = g_queue_new ();
183
184   GST_LOG ("init %p", list);
185 }
186
187 static void
188 gst_buffer_list_finalize (GstBufferList * list)
189 {
190   GList *tmp;
191
192   g_return_if_fail (list != NULL);
193
194   GST_LOG ("finalize %p", list);
195
196   tmp = list->buffers->head;
197   while (tmp) {
198     if (tmp->data != GROUP_START && tmp->data != STOLEN) {
199       gst_buffer_unref (GST_BUFFER_CAST (tmp->data));
200     }
201     tmp = tmp->next;
202   }
203   g_queue_free (list->buffers);
204
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));*/
208 }
209
210 static GstBufferList *
211 _gst_buffer_list_copy (GstBufferList * list)
212 {
213   GstBufferList *list_copy;
214   GQueue *buffers_copy;
215   GList *tmp;
216
217   g_return_val_if_fail (list != NULL, NULL);
218
219   /* shallow copy of list and pointers */
220   buffers_copy = g_queue_copy (list->buffers);
221
222   /* ref all buffers in the list */
223   tmp = list->buffers->head;
224   while (tmp) {
225     if (tmp->data != GROUP_START && tmp->data != STOLEN) {
226       tmp->data = gst_buffer_ref (GST_BUFFER_CAST (tmp->data));
227     }
228     tmp = g_list_next (tmp);
229   }
230
231   list_copy = gst_buffer_list_new ();
232   g_queue_free (list_copy->buffers);
233   list_copy->buffers = buffers_copy;
234
235   return list_copy;
236 }
237
238 static void
239 gst_buffer_list_class_init (GstBufferListClass * list_class)
240 {
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;
245 }
246
247 /**
248  * gst_buffer_list_new:
249  *
250  * Creates a new, empty #GstBufferList. The caller is responsible for unreffing
251  * the returned #GstBufferList.
252  *
253  * Free-function: gst_buffer_list_unref
254  *
255  * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref()
256  *     after usage.
257  *
258  * Since: 0.10.24
259  */
260 GstBufferList *
261 gst_buffer_list_new (void)
262 {
263   GstBufferList *list;
264
265   list = (GstBufferList *) gst_mini_object_new (_gst_buffer_list_type);
266
267   GST_LOG ("new %p", list);
268
269   return list;
270 }
271
272 /**
273  * gst_buffer_list_n_groups:
274  * @list: a #GstBufferList
275  *
276  * Returns the number of groups in @list.
277  *
278  * Returns: the number of groups in the buffer list
279  *
280  * Since: 0.10.24
281  */
282 guint
283 gst_buffer_list_n_groups (GstBufferList * list)
284 {
285   GList *tmp;
286   guint n;
287
288   g_return_val_if_fail (list != NULL, 0);
289
290   tmp = list->buffers->head;
291   n = 0;
292   while (tmp) {
293     if (tmp->data == GROUP_START) {
294       n++;
295     }
296     tmp = g_list_next (tmp);
297   }
298
299   return n;
300 }
301
302 /**
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
307  *
308  * Call @func with @data for each buffer in @list.
309  *
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.
313  *
314  * Since: 0.10.24
315  */
316 void
317 gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
318     gpointer user_data)
319 {
320   GList *tmp, *next;
321   guint group, idx;
322   GstBufferListItem res;
323
324   g_return_if_fail (list != NULL);
325   g_return_if_fail (func != NULL);
326
327   next = list->buffers->head;
328   group = idx = 0;
329   while (next) {
330     GstBuffer *buffer;
331
332     tmp = next;
333     next = g_list_next (tmp);
334
335     buffer = tmp->data;
336
337     if (buffer == GROUP_START) {
338       group++;
339       idx = 0;
340       continue;
341     } else if (buffer == STOLEN)
342       continue;
343     else
344       idx++;
345
346     /* need to decrement the indices */
347     res = func (&buffer, group - 1, idx - 1, user_data);
348
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);
354         idx--;
355       } else {
356         /* change the buffer */
357         tmp->data = buffer;
358       }
359     }
360
361     switch (res) {
362       case GST_BUFFER_LIST_CONTINUE:
363         break;
364       case GST_BUFFER_LIST_SKIP_GROUP:
365         while (next && next->data != GROUP_START)
366           next = g_list_next (next);
367         break;
368       case GST_BUFFER_LIST_END:
369         return;
370     }
371   }
372 }
373
374 /**
375  * gst_buffer_list_get:
376  * @list: a #GstBufferList
377  * @group: the group
378  * @idx: the index in @group
379  *
380  * Get the buffer at @idx in @group.
381  *
382  * Note that this function is not efficient for iterating over the entire list.
383  * Use an iterator or gst_buffer_list_foreach() instead.
384  *
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.
387  *
388  * Since: 0.10.24
389  */
390 GstBuffer *
391 gst_buffer_list_get (GstBufferList * list, guint group, guint idx)
392 {
393   GList *tmp;
394   guint cgroup, cidx;
395
396   g_return_val_if_fail (list != NULL, NULL);
397
398   tmp = list->buffers->head;
399   cgroup = 0;
400   while (tmp) {
401     if (tmp->data == GROUP_START) {
402       if (cgroup == group) {
403         /* we found the group */
404         tmp = g_list_next (tmp);
405         cidx = 0;
406         while (tmp && tmp->data != GROUP_START) {
407           if (tmp->data != STOLEN) {
408             if (cidx == idx)
409               return GST_BUFFER_CAST (tmp->data);
410             else
411               cidx++;
412           }
413           tmp = g_list_next (tmp);
414         }
415         break;
416       } else {
417         cgroup++;
418         if (cgroup > group)
419           break;
420       }
421     }
422     tmp = g_list_next (tmp);
423   }
424   return NULL;
425 }
426
427 static GstBufferListIterator *
428 gst_buffer_list_iterator_copy (const GstBufferListIterator * it)
429 {
430   GstBufferListIterator *ret;
431
432   ret = g_slice_new (GstBufferListIterator);
433   ret->list = it->list;
434   ret->next = it->next;
435   ret->last_returned = it->last_returned;
436
437   return ret;
438 }
439
440 GType
441 gst_buffer_list_iterator_get_type (void)
442 {
443   static GType type = 0;
444
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);
449   }
450
451   return type;
452 }
453
454 /**
455  * gst_buffer_list_iterate:
456  * @list: a #GstBufferList
457  *
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.
460  *
461  * Free-function: gst_buffer_list_iterator_free
462  *
463  * Returns: (transfer full): a new #GstBufferListIterator of the buffers in
464  *     @list. gst_buffer_list_iterator_free() after usage
465  *
466  * Since: 0.10.24
467  */
468 GstBufferListIterator *
469 gst_buffer_list_iterate (GstBufferList * list)
470 {
471   GstBufferListIterator *it;
472
473   g_return_val_if_fail (list != NULL, NULL);
474
475   it = g_slice_new (GstBufferListIterator);
476   it->list = list;
477   it->next = list->buffers->head;
478   it->last_returned = NULL;
479
480   return it;
481 }
482
483 /**
484  * gst_buffer_list_iterator_free:
485  * @it: (transfer full): the #GstBufferListIterator to free
486  *
487  * Free the iterator.
488  *
489  * Since: 0.10.24
490  */
491 void
492 gst_buffer_list_iterator_free (GstBufferListIterator * it)
493 {
494   g_return_if_fail (it != NULL);
495
496   g_slice_free (GstBufferListIterator, it);
497 }
498
499 /**
500  * gst_buffer_list_iterator_n_buffers:
501  * @it: a #GstBufferListIterator
502  *
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
505  * it returns NULL.
506  *
507  * This function will not move the implicit cursor or in any other way affect
508  * the state of the iterator @it.
509  *
510  * Returns: the number of buffers left to iterate in the current group
511  *
512  * Since: 0.10.24
513  */
514 guint
515 gst_buffer_list_iterator_n_buffers (const GstBufferListIterator * it)
516 {
517   GList *tmp;
518   guint n;
519
520   g_return_val_if_fail (it != NULL, 0);
521
522   tmp = it->next;
523   n = 0;
524   while (tmp && tmp->data != GROUP_START) {
525     if (tmp->data != STOLEN) {
526       n++;
527     }
528     tmp = g_list_next (tmp);
529   }
530
531   return n;
532 }
533
534 /**
535  * gst_buffer_list_iterator_add:
536  * @it: a #GstBufferListIterator
537  * @buffer: (transfer full): a #GstBuffer
538  *
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.
544  *
545  * This function takes ownership of @buffer.
546  *
547  * Since: 0.10.24
548  */
549 void
550 gst_buffer_list_iterator_add (GstBufferListIterator * it, GstBuffer * buffer)
551 {
552   g_return_if_fail (it != NULL);
553   g_return_if_fail (buffer != NULL);
554
555   /* adding before the first group start is not allowed */
556   g_return_if_fail (it->next != it->list->buffers->head);
557
558   /* cheap insert into the GQueue */
559   if (it->next != NULL) {
560     g_queue_insert_before (it->list->buffers, it->next, buffer);
561   } else {
562     g_queue_push_tail (it->list->buffers, buffer);
563   }
564 }
565
566 /**
567  * gst_buffer_list_iterator_add_list:
568  * @it: a #GstBufferListIterator
569  * @list: (transfer full) (element-type Gst.Buffer): a #GList of buffers
570  *
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.
576  *
577  * This function takes ownership of @list and all its buffers.
578  *
579  * Since: 0.10.31
580  */
581 void
582 gst_buffer_list_iterator_add_list (GstBufferListIterator * it, GList * list)
583 {
584   GList *last;
585   guint len;
586
587   g_return_if_fail (it != NULL);
588   g_return_if_fail (it->next != it->list->buffers->head);
589
590   if (list == NULL)
591     return;
592
593   last = list;
594   len = 1;
595   while (last->next) {
596     last = last->next;
597     len++;
598   }
599
600   if (it->next) {
601     last->next = it->next;
602     list->prev = it->next->prev;
603     it->next->prev = last;
604     if (list->prev)
605       list->prev->next = list;
606   } else {
607     it->list->buffers->tail->next = list;
608     list->prev = it->list->buffers->tail;
609     it->list->buffers->tail = last;
610   }
611   it->list->buffers->length += len;
612 }
613
614 /**
615  * gst_buffer_list_iterator_add_group:
616  * @it: a #GstBufferListIterator
617  *
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.
623  *
624  * Since: 0.10.24
625  */
626 void
627 gst_buffer_list_iterator_add_group (GstBufferListIterator * it)
628 {
629   g_return_if_fail (it != NULL);
630
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);
634   }
635
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);
639   } else {
640     g_queue_push_tail (it->list->buffers, GROUP_START);
641   }
642 }
643
644 /**
645  * gst_buffer_list_iterator_next:
646  * @it: a #GstBufferListIterator
647  *
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.
651  *
652  * The caller will not get a new ref to the returned #GstBuffer and must not
653  * unref it.
654  *
655  * Returns: (transfer none): the next buffer in the current group of the
656  *     buffer list, or NULL
657  *
658  * Since: 0.10.24
659  */
660 GstBuffer *
661 gst_buffer_list_iterator_next (GstBufferListIterator * it)
662 {
663   GstBuffer *buffer;
664
665   g_return_val_if_fail (it != NULL, NULL);
666
667   while (it->next != NULL && it->next->data != GROUP_START &&
668       it->next->data == STOLEN) {
669     it->next = g_list_next (it->next);
670   }
671
672   if (it->next == NULL || it->next->data == GROUP_START) {
673     goto no_buffer;
674   }
675
676   buffer = GST_BUFFER_CAST (it->next->data);
677
678   it->last_returned = it->next;
679   it->next = g_list_next (it->next);
680
681   return buffer;
682
683 no_buffer:
684   {
685     it->last_returned = NULL;
686     return NULL;
687   }
688 }
689
690 /**
691  * gst_buffer_list_iterator_next_group:
692  * @it: a #GstBufferListIterator
693  *
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.
697  *
698  * Returns: TRUE if the iterator could be advanced to the next group, FALSE if
699  * the iterator was already at the last group
700  *
701  * Since: 0.10.24
702  */
703 gboolean
704 gst_buffer_list_iterator_next_group (GstBufferListIterator * it)
705 {
706   g_return_val_if_fail (it != NULL, FALSE);
707
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);
711   }
712
713   if (it->next) {
714     /* move one step beyond the group start */
715     it->next = g_list_next (it->next);
716   }
717
718   it->last_returned = NULL;
719
720   return (it->next != NULL);
721 }
722
723 /**
724  * gst_buffer_list_iterator_remove:
725  * @it: a #GstBufferListIterator
726  *
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().
731  *
732  * The removed buffer is unreffed.
733  *
734  * Since: 0.10.24
735  */
736 void
737 gst_buffer_list_iterator_remove (GstBufferListIterator * it)
738 {
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);
742
743   if (it->last_returned->data != STOLEN) {
744     gst_buffer_unref (it->last_returned->data);
745   }
746   g_queue_delete_link (it->list->buffers, it->last_returned);
747   it->last_returned = NULL;
748 }
749
750 /**
751  * gst_buffer_list_iterator_take:
752  * @it: a #GstBufferListIterator
753  * @buffer: (transfer full): a #GstBuffer
754  *
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().
760  *
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).
764  *
765  * FIXME 0.11: this conditional taking-ownership is not good for bindings
766  *
767  * Since: 0.10.24
768  */
769 void
770 gst_buffer_list_iterator_take (GstBufferListIterator * it, GstBuffer * buffer)
771 {
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);
776
777   if (it->last_returned->data != STOLEN) {
778     gst_buffer_unref (it->last_returned->data);
779   }
780   it->last_returned->data = buffer;
781 }
782
783 /**
784  * gst_buffer_list_iterator_steal:
785  * @it: a #GstBufferListIterator
786  *
787  * Returns the last buffer returned by gst_buffer_list_iterator_next() without
788  * modifying the refcount of the buffer.
789  *
790  * Returns: (transfer none): the last buffer returned by
791  *     gst_buffer_list_iterator_next()
792  *
793  * Since: 0.10.24
794  */
795 GstBuffer *
796 gst_buffer_list_iterator_steal (GstBufferListIterator * it)
797 {
798   GstBuffer *buffer;
799
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);
804
805   buffer = it->last_returned->data;
806   it->last_returned->data = (gpointer) STOLEN;
807
808   return buffer;
809 }
810
811 /**
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.
816  *
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().
822  *
823  * See #GstBufferListDoFunction for more details.
824  *
825  * Returns: (transfer none): the return value from @do_func
826  *
827  * Since: 0.10.24
828  */
829 GstBuffer *
830 gst_buffer_list_iterator_do (GstBufferListIterator * it,
831     GstBufferListDoFunction do_func, gpointer user_data)
832 {
833   GstBuffer *buffer;
834
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);
841
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);
846   } else {
847     gst_buffer_list_iterator_take (it, buffer);
848   }
849
850   return buffer;
851 }
852
853 /**
854  * gst_buffer_list_iterator_merge_group:
855  * @it: a #GstBufferListIterator
856  *
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.
863  *
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.
866  *
867  * Returns: (transfer full): a new #GstBuffer, gst_buffer_unref() after usage,
868  *     or NULL
869  *
870  * Since: 0.10.24
871  */
872 GstBuffer *
873 gst_buffer_list_iterator_merge_group (const GstBufferListIterator * it)
874 {
875   GList *tmp;
876   guint size;
877   GstBuffer *buf;
878   guint8 *ptr;
879
880   g_return_val_if_fail (it != NULL, NULL);
881
882   /* calculate size of merged buffer */
883   size = 0;
884   tmp = it->next;
885   while (tmp && tmp->data != GROUP_START) {
886     if (tmp->data != STOLEN) {
887       size += GST_BUFFER_SIZE (tmp->data);
888     }
889     tmp = g_list_next (tmp);
890   }
891
892   if (size == 0) {
893     return NULL;
894   }
895
896   /* allocate a new buffer */
897   buf = gst_buffer_new_and_alloc (size);
898
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);
902
903   /* copy data of all buffers before the next group start into the new buffer */
904   ptr = GST_BUFFER_DATA (buf);
905   tmp = it->next;
906   do {
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);
910     }
911     tmp = g_list_next (tmp);
912   } while (tmp && tmp->data != GROUP_START);
913
914   return buf;
915 }