basesink: fix emergency rendering timestamp tracking
[platform/upstream/gstreamer.git] / 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  * <informalexample>
43  *   <programlisting>
44  *      +--- group0 ----------------------+--- group1 ------------+
45  *      |   buffer0   buffer1   buffer2   |   buffer3   buffer4   |
46  *    ^   ^         ^         ^         ^   ^         ^         ^
47  *    Iterator positions between buffers
48  *   </programlisting>
49  * </informalexample>
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 const gpointer STOLEN = "";
132
133 /**
134  * GstBufferList:
135  * @mini_object: the parent structure
136  *
137  * Opaque list of grouped buffers.
138  *
139  * Since: 0.10.24
140  */
141 struct _GstBufferList
142 {
143   GstMiniObject mini_object;
144
145   GList *buffers;
146 };
147
148 struct _GstBufferListClass
149 {
150   GstMiniObjectClass mini_object_class;
151 };
152
153 /**
154  * GstBufferListIterator:
155  *
156  * Opaque iterator for a #GstBufferList.
157  *
158  * Since: 0.10.24
159  */
160 struct _GstBufferListIterator
161 {
162   GstBufferList *list;
163   GList *next;
164   GList *last_returned;
165 };
166
167 static GType _gst_buffer_list_type = 0;
168 static GstMiniObjectClass *parent_class = NULL;
169
170 void
171 _gst_buffer_list_initialize (void)
172 {
173   g_type_class_ref (gst_buffer_list_get_type ());
174 }
175
176 static void
177 gst_buffer_list_init (GTypeInstance * instance, gpointer g_class)
178 {
179   GstBufferList *list;
180
181   list = (GstBufferList *) instance;
182   list->buffers = NULL;
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;
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_list_free (list->buffers);
204
205   parent_class->finalize (GST_MINI_OBJECT_CAST (list));
206 }
207
208 static GstBufferList *
209 _gst_buffer_list_copy (GstBufferList * list)
210 {
211   GstBufferList *list_copy;
212   GList *tmp;
213
214   g_return_val_if_fail (list != NULL, NULL);
215
216   list_copy = gst_buffer_list_new ();
217
218   /* shallow copy of list and pointers */
219   list_copy->buffers = g_list_copy (list->buffers);
220
221   /* ref all buffers in the list */
222   tmp = list_copy->buffers;
223   while (tmp) {
224     if (tmp->data != GROUP_START && tmp->data != STOLEN) {
225       tmp->data = gst_buffer_ref (GST_BUFFER_CAST (tmp->data));
226     }
227     tmp = g_list_next (tmp);
228   }
229
230   return list_copy;
231 }
232
233 static void
234 gst_buffer_list_class_init (gpointer g_class, gpointer class_data)
235 {
236   GstBufferListClass *list_class = GST_BUFFER_LIST_CLASS (g_class);
237
238   parent_class = g_type_class_peek_parent (g_class);
239
240   list_class->mini_object_class.copy =
241       (GstMiniObjectCopyFunction) _gst_buffer_list_copy;
242   list_class->mini_object_class.finalize =
243       (GstMiniObjectFinalizeFunction) gst_buffer_list_finalize;
244 }
245
246 /**
247  * gst_buffer_list_new:
248  *
249  * Creates a new, empty #GstBufferList. The caller is responsible for unreffing
250  * the returned #GstBufferList.
251  *
252  * Returns: the new #GstBufferList. gst_buffer_list_unref() after usage.
253  *
254  * Since: 0.10.24
255  */
256 GstBufferList *
257 gst_buffer_list_new (void)
258 {
259   GstBufferList *list;
260
261   list = (GstBufferList *) gst_mini_object_new (_gst_buffer_list_type);
262
263   GST_LOG ("new %p", list);
264
265   return list;
266 }
267
268 /**
269  * gst_buffer_list_n_groups:
270  * @list: a #GstBufferList
271  *
272  * Returns the number of groups in @list.
273  *
274  * Returns: the number of groups in the buffer list
275  *
276  * Since: 0.10.24
277  */
278 guint
279 gst_buffer_list_n_groups (GstBufferList * list)
280 {
281   GList *tmp;
282   guint n;
283
284   g_return_val_if_fail (list != NULL, 0);
285
286   tmp = list->buffers;
287   n = 0;
288   while (tmp) {
289     if (tmp->data == GROUP_START) {
290       n++;
291     }
292     tmp = g_list_next (tmp);
293   }
294
295   return n;
296 }
297
298 /**
299  * gst_buffer_list_foreach:
300  * @list: a #GstBufferList
301  * @func: a #GstBufferListFunc to call
302  * @user_data: user data passed to @func
303  *
304  * Call @func with @data for each buffer in @list.
305  *
306  * @func can modify the passed buffer pointer or its contents. The return value
307  * of @func define if this function returns or if the remaining buffers in a
308  * group should be skipped.
309  *
310  * Since: 0.10.24
311  */
312 void
313 gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
314     gpointer user_data)
315 {
316   GList *tmp, *next;
317   guint group, idx;
318   GstBufferListItem res;
319
320   g_return_if_fail (list != NULL);
321   g_return_if_fail (func != NULL);
322
323   next = list->buffers;
324   group = idx = 0;
325   while (next) {
326     GstBuffer *buffer;
327
328     tmp = next;
329     next = g_list_next (tmp);
330
331     buffer = tmp->data;
332
333     if (buffer == GROUP_START) {
334       group++;
335       idx = 0;
336       continue;
337     } else if (buffer == STOLEN)
338       continue;
339     else
340       idx++;
341
342     /* need to decrement the indices */
343     res = func (&buffer, group - 1, idx - 1, user_data);
344
345     if (G_UNLIKELY (buffer != tmp->data)) {
346       /* the function changed the buffer */
347       if (buffer == NULL) {
348         /* we were asked to remove the item */
349         list->buffers = g_list_delete_link (list->buffers, tmp);
350         idx--;
351       } else {
352         /* change the buffer */
353         tmp->data = buffer;
354       }
355     }
356
357     switch (res) {
358       case GST_BUFFER_LIST_CONTINUE:
359         break;
360       case GST_BUFFER_LIST_SKIP_GROUP:
361         while (next && next->data != GROUP_START)
362           next = g_list_next (next);
363         break;
364       case GST_BUFFER_LIST_END:
365         return;
366     }
367   }
368 }
369
370 /**
371  * gst_buffer_list_get:
372  * @list: a #GstBufferList
373  * @group: the group
374  * @idx: the index in @group
375  *
376  * Get the buffer at @idx in @group.
377  *
378  * Note that this function is not efficient for iterating over the entire list.
379  * Use an iterator or gst_buffer_list_foreach() instead.
380  *
381  * Returns: the buffer at @idx in @group or NULL when there is no buffer. The
382  * buffer remains valid as long as @list is valid.
383  *
384  * Since: 0.10.24
385  */
386 GstBuffer *
387 gst_buffer_list_get (GstBufferList * list, guint group, guint idx)
388 {
389   GList *tmp;
390   guint cgroup, cidx;
391
392   g_return_val_if_fail (list != NULL, NULL);
393
394   tmp = list->buffers;
395   cgroup = 0;
396   while (tmp) {
397     if (tmp->data == GROUP_START) {
398       if (cgroup == group) {
399         /* we found the group */
400         tmp = g_list_next (tmp);
401         cidx = 0;
402         while (tmp && tmp->data != GROUP_START) {
403           if (tmp->data != STOLEN) {
404             if (cidx == idx)
405               return GST_BUFFER_CAST (tmp->data);
406             else
407               cidx++;
408           }
409           tmp = g_list_next (tmp);
410         }
411         break;
412       } else {
413         cgroup++;
414         if (cgroup > group)
415           break;
416       }
417     }
418     tmp = g_list_next (tmp);
419   }
420   return NULL;
421 }
422
423 GType
424 gst_buffer_list_get_type (void)
425 {
426   if (G_UNLIKELY (_gst_buffer_list_type == 0)) {
427     static const GTypeInfo buffer_list_info = {
428       sizeof (GstBufferListClass),
429       NULL,
430       NULL,
431       gst_buffer_list_class_init,
432       NULL,
433       NULL,
434       sizeof (GstBufferList),
435       0,
436       gst_buffer_list_init,
437       NULL
438     };
439
440     _gst_buffer_list_type = g_type_register_static (GST_TYPE_MINI_OBJECT,
441         "GstBufferList", &buffer_list_info, 0);
442   }
443
444   return _gst_buffer_list_type;
445 }
446
447 /**
448  * gst_buffer_list_iterate:
449  * @list: a #GstBufferList
450  *
451  * Iterate the buffers in @list. The owner of the iterator must also be the
452  * owner of a reference to @list while the returned iterator is in use.
453  *
454  * Returns: a new #GstBufferListIterator of the buffers in @list.
455  * gst_buffer_list_iterator_free() after usage
456  *
457  * Since: 0.10.24
458  */
459 GstBufferListIterator *
460 gst_buffer_list_iterate (GstBufferList * list)
461 {
462   GstBufferListIterator *it;
463
464   g_return_val_if_fail (list != NULL, NULL);
465
466   it = g_slice_new (GstBufferListIterator);
467   it->list = list;
468   it->next = list->buffers;
469   it->last_returned = NULL;
470
471   return it;
472 }
473
474 /**
475  * gst_buffer_list_iterator_free:
476  * @it: the #GstBufferListIterator to free
477  *
478  * Free the iterator.
479  *
480  * Since: 0.10.24
481  */
482 void
483 gst_buffer_list_iterator_free (GstBufferListIterator * it)
484 {
485   g_return_if_fail (it != NULL);
486
487   g_slice_free (GstBufferListIterator, it);
488 }
489
490 /**
491  * gst_buffer_list_iterator_n_buffers:
492  * @it: a #GstBufferListIterator
493  *
494  * Returns the number of buffers left to iterate in the current group. I.e. the
495  * number of calls that can be made to gst_buffer_list_iterator_next() before
496  * it returns NULL.
497  *
498  * This function will not move the implicit cursor or in any other way affect
499  * the state of the iterator @it.
500  *
501  * Returns: the number of buffers left to iterate in the current group
502  *
503  * Since: 0.10.24
504  */
505 guint
506 gst_buffer_list_iterator_n_buffers (const GstBufferListIterator * it)
507 {
508   GList *tmp;
509   guint n;
510
511   g_return_val_if_fail (it != NULL, 0);
512
513   tmp = it->next;
514   n = 0;
515   while (tmp && tmp->data != GROUP_START) {
516     if (tmp->data != STOLEN) {
517       n++;
518     }
519     tmp = g_list_next (tmp);
520   }
521
522   return n;
523 }
524
525 /**
526  * gst_buffer_list_iterator_add:
527  * @it: a #GstBufferListIterator
528  * @buffer: a #GstBuffer
529  *
530  * Inserts @buffer into the #GstBufferList iterated with @it. The buffer is
531  * inserted into the current group, immediately before the buffer that would be
532  * returned by gst_buffer_list_iterator_next(). The buffer is inserted before
533  * the implicit cursor, a subsequent call to gst_buffer_list_iterator_next()
534  * will return the buffer after the inserted buffer, if any.
535  *
536  * This function takes ownership of @buffer.
537  *
538  * Since: 0.10.24
539  */
540 void
541 gst_buffer_list_iterator_add (GstBufferListIterator * it, GstBuffer * buffer)
542 {
543   g_return_if_fail (it != NULL);
544   g_return_if_fail (buffer != NULL);
545
546   /* adding before the first group start is not allowed */
547   g_return_if_fail (it->next != it->list->buffers);
548
549   /* cheap insert into the GList */
550   it->list->buffers = g_list_insert_before (it->list->buffers, it->next,
551       buffer);
552 }
553
554 /**
555  * gst_buffer_list_iterator_add_group:
556  * @it: a #GstBufferListIterator
557  *
558  * Inserts a new, empty group into the #GstBufferList iterated with @it. The
559  * group is inserted immediately before the group that would be returned by
560  * gst_buffer_list_iterator_next_group(). A subsequent call to
561  * gst_buffer_list_iterator_next_group() will advance the iterator to the group
562  * after the inserted group, if any.
563  *
564  * Since: 0.10.24
565  */
566 void
567 gst_buffer_list_iterator_add_group (GstBufferListIterator * it)
568 {
569   g_return_if_fail (it != NULL);
570
571   /* advance iterator to next group start */
572   while (it->next != NULL && it->next->data != GROUP_START) {
573     it->next = g_list_next (it->next);
574   }
575
576   /* cheap insert of a group start into the GList */
577   it->list->buffers = g_list_insert_before (it->list->buffers, it->next,
578       GROUP_START);
579 }
580
581 /**
582  * gst_buffer_list_iterator_next:
583  * @it: a #GstBufferListIterator
584  *
585  * Returns the next buffer in the list iterated with @it. If the iterator is at
586  * the end of a group, NULL will be returned. This function may be called
587  * repeatedly to iterate through the current group.
588  *
589  * The caller will not get a new ref to the returned #GstBuffer and must not
590  * unref it.
591  *
592  * Returns: the next buffer in the current group of the buffer list, or NULL
593  *
594  * Since: 0.10.24
595  */
596 GstBuffer *
597 gst_buffer_list_iterator_next (GstBufferListIterator * it)
598 {
599   GstBuffer *buffer;
600
601   g_return_val_if_fail (it != NULL, NULL);
602
603   while (it->next != NULL && it->next->data != GROUP_START &&
604       it->next->data == STOLEN) {
605     it->next = g_list_next (it->next);
606   }
607
608   if (it->next == NULL || it->next->data == GROUP_START) {
609     goto no_buffer;
610   }
611
612   buffer = GST_BUFFER_CAST (it->next->data);
613
614   it->last_returned = it->next;
615   it->next = g_list_next (it->next);
616
617   return buffer;
618
619 no_buffer:
620   {
621     it->last_returned = NULL;
622     return NULL;
623   }
624 }
625
626 /**
627  * gst_buffer_list_iterator_next_group:
628  * @it: a #GstBufferListIterator
629  *
630  * Advance the iterator @it to the first buffer in the next group. If the
631  * iterator is at the last group, FALSE will be returned. This function may be
632  * called repeatedly to iterate through the groups in a buffer list.
633  *
634  * Returns: TRUE if the iterator could be advanced to the next group, FALSE if
635  * the iterator was already at the last group
636  *
637  * Since: 0.10.24
638  */
639 gboolean
640 gst_buffer_list_iterator_next_group (GstBufferListIterator * it)
641 {
642   g_return_val_if_fail (it != NULL, FALSE);
643
644   /* advance iterator to next group start */
645   while (it->next != NULL && it->next->data != GROUP_START) {
646     it->next = g_list_next (it->next);
647   }
648
649   if (it->next) {
650     /* move one step beyond the group start */
651     it->next = g_list_next (it->next);
652   }
653
654   it->last_returned = NULL;
655
656   return (it->next != NULL);
657 }
658
659 /**
660  * gst_buffer_list_iterator_remove:
661  * @it: a #GstBufferListIterator
662  *
663  * Removes the last buffer returned by gst_buffer_list_iterator_next() from
664  * the #GstBufferList iterated with @it. gst_buffer_list_iterator_next() must
665  * have been called on @it before this function is called. This function can
666  * only be called once per call to gst_buffer_list_iterator_next().
667  *
668  * The removed buffer is unreffed.
669  *
670  * Since: 0.10.24
671  */
672 void
673 gst_buffer_list_iterator_remove (GstBufferListIterator * it)
674 {
675   g_return_if_fail (it != NULL);
676   g_return_if_fail (it->last_returned != NULL);
677   g_assert (it->last_returned->data != GROUP_START);
678
679   if (it->last_returned->data != STOLEN) {
680     gst_buffer_unref (it->last_returned->data);
681   }
682   it->list->buffers = g_list_delete_link (it->list->buffers, it->last_returned);
683   it->last_returned = NULL;
684 }
685
686 /**
687  * gst_buffer_list_iterator_take:
688  * @it: a #GstBufferListIterator
689  * @buffer: a #GstBuffer
690  *
691  * Replaces the last buffer returned by gst_buffer_list_iterator_next() with
692  * @buffer in the #GstBufferList iterated with @it and takes ownership of
693  * @buffer. gst_buffer_list_iterator_next() must have been called on @it before
694  * this function is called. gst_buffer_list_iterator_remove() must not have been
695  * called since the last call to gst_buffer_list_iterator_next().
696  *
697  * This function unrefs the replaced buffer if it has not been stolen with
698  * gst_buffer_list_iterator_steal() and takes ownership of @buffer (i.e. the
699  * refcount of @buffer is not increased).
700  *
701  * Since: 0.10.24
702  */
703 void
704 gst_buffer_list_iterator_take (GstBufferListIterator * it, GstBuffer * buffer)
705 {
706   g_return_if_fail (it != NULL);
707   g_return_if_fail (it->last_returned != NULL);
708   g_return_if_fail (buffer != NULL);
709   g_assert (it->last_returned->data != GROUP_START);
710
711   if (it->last_returned->data != STOLEN) {
712     gst_buffer_unref (it->last_returned->data);
713   }
714   it->last_returned->data = buffer;
715 }
716
717 /**
718  * gst_buffer_list_iterator_steal:
719  * @it: a #GstBufferListIterator
720  *
721  * Returns the last buffer returned by gst_buffer_list_iterator_next() without
722  * modifying the refcount of the buffer.
723  *
724  * Returns: the last buffer returned by gst_buffer_list_iterator_next()
725  *
726  * Since: 0.10.24
727  */
728 GstBuffer *
729 gst_buffer_list_iterator_steal (GstBufferListIterator * it)
730 {
731   GstBuffer *buffer;
732
733   g_return_val_if_fail (it != NULL, NULL);
734   g_return_val_if_fail (it->last_returned != NULL, NULL);
735   g_return_val_if_fail (it->last_returned->data != STOLEN, NULL);
736   g_assert (it->last_returned->data != GROUP_START);
737
738   buffer = it->last_returned->data;
739   it->last_returned->data = STOLEN;
740
741   return buffer;
742 }
743
744 /**
745  * gst_buffer_list_iterator_do:
746  * @it: a #GstBufferListIterator
747  * @do_func: the function to be called
748  * @user_data: the gpointer to optional user data.
749  *
750  * Calls the given function for the last buffer returned by
751  * gst_buffer_list_iterator_next(). gst_buffer_list_iterator_next() must have
752  * been called on @it before this function is called.
753  * gst_buffer_list_iterator_remove() and gst_buffer_list_iterator_steal() must
754  * not have been called since the last call to gst_buffer_list_iterator_next().
755  *
756  * See #GstBufferListDoFunction for more details.
757  *
758  * Returns: the return value from @do_func
759  *
760  * Since: 0.10.24
761  */
762 GstBuffer *
763 gst_buffer_list_iterator_do (GstBufferListIterator * it,
764     GstBufferListDoFunction do_func, gpointer user_data)
765 {
766   GstBuffer *buffer;
767
768   g_return_val_if_fail (it != NULL, NULL);
769   g_return_val_if_fail (it->last_returned != NULL, NULL);
770   g_return_val_if_fail (it->last_returned->data != STOLEN, NULL);
771   g_return_val_if_fail (do_func != NULL, NULL);
772   g_return_val_if_fail (gst_buffer_list_is_writable (it->list), NULL);
773   g_assert (it->last_returned->data != GROUP_START);
774
775   buffer = gst_buffer_list_iterator_steal (it);
776   buffer = do_func (buffer, user_data);
777   if (buffer == NULL) {
778     gst_buffer_list_iterator_remove (it);
779   } else {
780     gst_buffer_list_iterator_take (it, buffer);
781   }
782
783   return buffer;
784 }
785
786 /**
787  * gst_buffer_list_iterator_merge_group:
788  * @it: a #GstBufferListIterator
789  *
790  * Merge a buffer list group into a normal #GstBuffer by copying its metadata
791  * and memcpying its data into consecutive memory. All buffers in the current
792  * group after the implicit cursor will be merged into one new buffer. The
793  * metadata of the new buffer will be a copy of the metadata of the buffer that
794  * would be returned by gst_buffer_list_iterator_next(). If there is no buffer
795  * in the current group after the implicit cursor, NULL will be returned.
796  *
797  * This function will not move the implicit cursor or in any other way affect
798  * the state of the iterator @it or the list.
799  *
800  * Returns: a new #GstBuffer, gst_buffer_unref() after usage, or NULL
801  *
802  * Since: 0.10.24
803  */
804 GstBuffer *
805 gst_buffer_list_iterator_merge_group (const GstBufferListIterator * it)
806 {
807   GList *tmp;
808   guint size;
809   GstBuffer *buf;
810   guint8 *ptr;
811
812   g_return_val_if_fail (it != NULL, NULL);
813
814   /* calculate size of merged buffer */
815   size = 0;
816   tmp = it->next;
817   while (tmp && tmp->data != GROUP_START) {
818     if (tmp->data != STOLEN) {
819       size += GST_BUFFER_SIZE (tmp->data);
820     }
821     tmp = g_list_next (tmp);
822   }
823
824   if (size == 0) {
825     return NULL;
826   }
827
828   /* allocate a new buffer */
829   buf = gst_buffer_new_and_alloc (size);
830
831   /* copy metadata from the next buffer after the implicit cursor */
832   gst_buffer_copy_metadata (buf, GST_BUFFER_CAST (it->next->data),
833       GST_BUFFER_COPY_ALL);
834
835   /* copy data of all buffers before the next group start into the new buffer */
836   ptr = GST_BUFFER_DATA (buf);
837   tmp = it->next;
838   do {
839     if (tmp->data != STOLEN) {
840       memcpy (ptr, GST_BUFFER_DATA (tmp->data), GST_BUFFER_SIZE (tmp->data));
841       ptr += GST_BUFFER_SIZE (tmp->data);
842     }
843     tmp = g_list_next (tmp);
844   } while (tmp && tmp->data != GROUP_START);
845
846   return buf;
847 }