• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Copyright (C) 2014 Tim-Philipp Müller <tim at centricular dot com>
5  *
6  * gstbufferlist.c: Buffer list
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /**
25  * SECTION:gstbufferlist
26  * @title: GstBufferList
27  * @short_description: Lists of buffers for data-passing
28  * @see_also: #GstPad, #GstMiniObject
29  *
30  * Buffer lists are an object containing a list of buffers.
31  *
32  * Buffer lists are created with gst_buffer_list_new() and filled with data
33  * using gst_buffer_list_insert().
34  *
35  * Buffer lists can be pushed on a srcpad with gst_pad_push_list(). This is
36  * interesting when multiple buffers need to be pushed in one go because it
37  * can reduce the amount of overhead for pushing each buffer individually.
38  */
39 #define GST_DISABLE_MINIOBJECT_INLINE_FUNCTIONS
40 #include "gst_private.h"
41 
42 #include "gstbuffer.h"
43 #include "gstbufferlist.h"
44 #include "gstutils.h"
45 
46 #define GST_CAT_DEFAULT GST_CAT_BUFFER_LIST
47 
48 #define GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY(list) \
49     ((list)->buffers != &(list)->arr[0])
50 
51 /**
52  * GstBufferList:
53  *
54  * Opaque list of grouped buffers.
55  */
56 struct _GstBufferList
57 {
58   GstMiniObject mini_object;
59 
60   GstBuffer **buffers;
61   guint n_buffers;
62   guint n_allocated;
63 
64   gsize slice_size;
65 
66   /* one-item array, in reality more items are pre-allocated
67    * as part of the GstBufferList structure, and that
68    * pre-allocated array extends beyond the declared struct */
69   GstBuffer *arr[1];
70 };
71 
72 GType _gst_buffer_list_type = 0;
73 
74 GST_DEFINE_MINI_OBJECT_TYPE (GstBufferList, gst_buffer_list);
75 
76 void
_priv_gst_buffer_list_initialize(void)77 _priv_gst_buffer_list_initialize (void)
78 {
79   _gst_buffer_list_type = gst_buffer_list_get_type ();
80 }
81 
82 static GstBufferList *
_gst_buffer_list_copy(GstBufferList * list)83 _gst_buffer_list_copy (GstBufferList * list)
84 {
85   GstBufferList *copy;
86   guint i, len;
87 
88   len = list->n_buffers;
89   copy = gst_buffer_list_new_sized (list->n_allocated);
90 
91   /* add and ref all buffers in the array */
92   for (i = 0; i < len; i++) {
93     copy->buffers[i] = gst_buffer_ref (list->buffers[i]);
94     gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (copy->buffers[i]),
95         GST_MINI_OBJECT_CAST (copy));
96   }
97 
98   copy->n_buffers = len;
99 
100   return copy;
101 }
102 
103 static void
_gst_buffer_list_free(GstBufferList * list)104 _gst_buffer_list_free (GstBufferList * list)
105 {
106   guint i, len;
107   gsize slice_size;
108 
109   GST_LOG ("free %p", list);
110 
111   /* unrefs all buffers too */
112   len = list->n_buffers;
113   for (i = 0; i < len; i++) {
114     gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[i]),
115         GST_MINI_OBJECT_CAST (list));
116     gst_buffer_unref (list->buffers[i]);
117   }
118 
119   if (GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY (list))
120     g_free (list->buffers);
121 
122   slice_size = list->slice_size;
123 
124 #ifdef USE_POISONING
125   memset (list, 0xff, slice_size);
126 #endif
127 
128   g_slice_free1 (slice_size, list);
129 }
130 
131 static void
gst_buffer_list_init(GstBufferList * list,guint n_allocated,gsize slice_size)132 gst_buffer_list_init (GstBufferList * list, guint n_allocated, gsize slice_size)
133 {
134   gst_mini_object_init (GST_MINI_OBJECT_CAST (list), 0, _gst_buffer_list_type,
135       (GstMiniObjectCopyFunction) _gst_buffer_list_copy, NULL,
136       (GstMiniObjectFreeFunction) _gst_buffer_list_free);
137 
138   list->buffers = &list->arr[0];
139   list->n_buffers = 0;
140   list->n_allocated = n_allocated;
141   list->slice_size = slice_size;
142 
143   GST_LOG ("init %p", list);
144 }
145 
146 /**
147  * gst_buffer_list_new_sized:
148  * @size: an initial reserved size
149  *
150  * Creates a new, empty #GstBufferList. The list will have @size space
151  * preallocated so that memory reallocations can be avoided.
152  *
153  * Returns: (transfer full): the new #GstBufferList.
154  */
155 GstBufferList *
gst_buffer_list_new_sized(guint size)156 gst_buffer_list_new_sized (guint size)
157 {
158   GstBufferList *list;
159   gsize slice_size;
160   guint n_allocated;
161 
162   if (size == 0)
163     size = 1;
164 
165   n_allocated = GST_ROUND_UP_16 (size);
166 
167   slice_size = sizeof (GstBufferList) + (n_allocated - 1) * sizeof (gpointer);
168 
169   list = g_slice_alloc0 (slice_size);
170 
171   GST_LOG ("new %p", list);
172 
173   gst_buffer_list_init (list, n_allocated, slice_size);
174 
175   return list;
176 }
177 
178 /**
179  * gst_buffer_list_new:
180  *
181  * Creates a new, empty #GstBufferList.
182  *
183  * Returns: (transfer full): the new #GstBufferList.
184  */
185 GstBufferList *
gst_buffer_list_new(void)186 gst_buffer_list_new (void)
187 {
188   return gst_buffer_list_new_sized (8);
189 }
190 
191 /**
192  * gst_buffer_list_length:
193  * @list: a #GstBufferList
194  *
195  * Returns the number of buffers in @list.
196  *
197  * Returns: the number of buffers in the buffer list
198  */
199 guint
gst_buffer_list_length(GstBufferList * list)200 gst_buffer_list_length (GstBufferList * list)
201 {
202   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), 0);
203 
204   return list->n_buffers;
205 }
206 
207 static inline void
gst_buffer_list_remove_range_internal(GstBufferList * list,guint idx,guint length,gboolean unref_old)208 gst_buffer_list_remove_range_internal (GstBufferList * list, guint idx,
209     guint length, gboolean unref_old)
210 {
211   guint i;
212 
213   if (unref_old) {
214     for (i = idx; i < idx + length; ++i) {
215       gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[i]),
216           GST_MINI_OBJECT_CAST (list));
217       gst_buffer_unref (list->buffers[i]);
218     }
219   }
220 
221   if (idx + length != list->n_buffers) {
222     memmove (&list->buffers[idx], &list->buffers[idx + length],
223         (list->n_buffers - (idx + length)) * sizeof (void *));
224   }
225 
226   list->n_buffers -= length;
227 }
228 
229 /**
230  * gst_buffer_list_foreach:
231  * @list: a #GstBufferList
232  * @func: (scope call): a #GstBufferListFunc to call
233  * @user_data: (closure): user data passed to @func
234  *
235  * Calls @func with @data for each buffer in @list.
236  *
237  * @func can modify the passed buffer pointer or its contents. The return value
238  * of @func defines if this function returns or if the remaining buffers in
239  * the list should be skipped.
240  *
241  * Returns: %TRUE when @func returned %TRUE for each buffer in @list or when
242  * @list is empty.
243  */
244 gboolean
gst_buffer_list_foreach(GstBufferList * list,GstBufferListFunc func,gpointer user_data)245 gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
246     gpointer user_data)
247 {
248   guint i, len;
249   gboolean ret = TRUE;
250   gboolean list_was_writable, first_warning = TRUE;
251 
252   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), FALSE);
253   g_return_val_if_fail (func != NULL, FALSE);
254 
255   list_was_writable = gst_buffer_list_is_writable (list);
256 
257   len = list->n_buffers;
258   for (i = 0; i < len;) {
259     GstBuffer *buf, *buf_ret;
260     gboolean was_writable;
261 
262     buf = buf_ret = list->buffers[i];
263 
264     /* If the buffer is writable, we remove us as parent for now to
265      * allow the callback to destroy the buffer. If we get the buffer
266      * back, we add ourselves as parent again.
267      *
268      * Non-writable buffers just get another reference as they were not
269      * writable to begin with, and they would possibly become writable
270      * by removing ourselves as parent
271      */
272     was_writable = list_was_writable && gst_buffer_is_writable (buf);
273 
274     if (was_writable)
275       gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (buf),
276           GST_MINI_OBJECT_CAST (list));
277     else
278       gst_buffer_ref (buf);
279 
280     ret = func (&buf_ret, i, user_data);
281 
282     /* Check if the function changed the buffer */
283     if (buf != buf_ret) {
284       /* If the list was not writable but the callback was actually changing
285        * our buffer, then it wouldn't have been allowed to do so.
286        *
287        * Fortunately we still have a reference to the old buffer in that case
288        * and just not modify the list, unref the new buffer (if any) and warn
289        * about this */
290       if (!list_was_writable) {
291         if (first_warning) {
292           g_critical
293               ("gst_buffer_list_foreach: non-writable list %p was changed from callback",
294               list);
295           first_warning = FALSE;
296         }
297         if (buf_ret)
298           gst_buffer_unref (buf_ret);
299       } else if (buf_ret == NULL) {
300         gst_buffer_list_remove_range_internal (list, i, 1, !was_writable);
301         --len;
302       } else {
303         if (!was_writable) {
304           gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (buf),
305               GST_MINI_OBJECT_CAST (list));
306           gst_buffer_unref (buf);
307         }
308 
309         list->buffers[i] = buf_ret;
310         gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buf_ret),
311             GST_MINI_OBJECT_CAST (list));
312       }
313     } else {
314       if (was_writable)
315         gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buf),
316             GST_MINI_OBJECT_CAST (list));
317       else
318         gst_buffer_unref (buf);
319     }
320 
321     if (!ret)
322       break;
323 
324     /* If the buffer was not removed by func go to the next buffer */
325     if (buf_ret != NULL)
326       i++;
327   }
328   return ret;
329 }
330 
331 /**
332  * gst_buffer_list_get:
333  * @list: a #GstBufferList
334  * @idx: the index
335  *
336  * Gets the buffer at @idx.
337  *
338  * You must make sure that @idx does not exceed the number of
339  * buffers available.
340  *
341  * Returns: (transfer none) (nullable): the buffer at @idx in @group
342  *     or %NULL when there is no buffer. The buffer remains valid as
343  *     long as @list is valid and buffer is not removed from the list.
344  */
345 GstBuffer *
gst_buffer_list_get(GstBufferList * list,guint idx)346 gst_buffer_list_get (GstBufferList * list, guint idx)
347 {
348   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL);
349   g_return_val_if_fail (idx < list->n_buffers, NULL);
350 
351   return list->buffers[idx];
352 }
353 
354 /**
355  * gst_buffer_list_get_writable:
356  * @list: a (writable) #GstBufferList
357  * @idx: the index
358  *
359  * Gets the buffer at @idx, ensuring it is a writable buffer.
360  *
361  * You must make sure that @idx does not exceed the number of
362  * buffers available.
363  *
364  * Returns: (transfer none) (nullable): the buffer at @idx in @group.
365  *     The returned  buffer remains valid as long as @list is valid and
366  *     the buffer is not removed from the list.
367  *
368  * Since: 1.14
369  */
370 GstBuffer *
gst_buffer_list_get_writable(GstBufferList * list,guint idx)371 gst_buffer_list_get_writable (GstBufferList * list, guint idx)
372 {
373   GstBuffer *new_buf;
374 
375   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL);
376   g_return_val_if_fail (gst_buffer_list_is_writable (list), NULL);
377   g_return_val_if_fail (idx < list->n_buffers, NULL);
378 
379   /* We have to implement this manually here to correctly add/remove the
380    * parent */
381   if (gst_buffer_is_writable (list->buffers[idx]))
382     return list->buffers[idx];
383 
384   gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[idx]),
385       GST_MINI_OBJECT_CAST (list));
386   new_buf = gst_buffer_copy (list->buffers[idx]);
387   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (new_buf),
388       GST_MINI_OBJECT_CAST (list));
389   gst_buffer_unref (list->buffers[idx]);
390   list->buffers[idx] = new_buf;
391 
392   return new_buf;
393 }
394 
395 /**
396  * gst_buffer_list_add:
397  * @l: a #GstBufferList
398  * @b: a #GstBuffer
399  *
400  * Append @b at the end of @l.
401  */
402 /**
403  * gst_buffer_list_insert:
404  * @list: a #GstBufferList
405  * @idx: the index
406  * @buffer: (transfer full): a #GstBuffer
407  *
408  * Inserts @buffer at @idx in @list. Other buffers are moved to make room for
409  * this new buffer.
410  *
411  * A -1 value for @idx will append the buffer at the end.
412  */
413 void
gst_buffer_list_insert(GstBufferList * list,gint idx,GstBuffer * buffer)414 gst_buffer_list_insert (GstBufferList * list, gint idx, GstBuffer * buffer)
415 {
416   guint want_alloc;
417 
418   g_return_if_fail (GST_IS_BUFFER_LIST (list));
419   g_return_if_fail (buffer != NULL);
420   g_return_if_fail (gst_buffer_list_is_writable (list));
421 
422   if (idx == -1 && list->n_buffers < list->n_allocated) {
423     gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buffer),
424         GST_MINI_OBJECT_CAST (list));
425     list->buffers[list->n_buffers++] = buffer;
426     return;
427   }
428 
429   if (idx == -1 || idx > list->n_buffers)
430     idx = list->n_buffers;
431 
432   want_alloc = list->n_buffers + 1;
433 
434   if (want_alloc > list->n_allocated) {
435     if (G_UNLIKELY (list->n_allocated > (G_MAXUINT / 2)))
436       g_error ("Growing GstBufferList would result in overflow");
437 
438     want_alloc = MAX (GST_ROUND_UP_16 (want_alloc), list->n_allocated * 2);
439 
440     if (GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY (list)) {
441       list->buffers = g_renew (GstBuffer *, list->buffers, want_alloc);
442     } else {
443       list->buffers = g_new0 (GstBuffer *, want_alloc);
444       memcpy (list->buffers, &list->arr[0], list->n_buffers * sizeof (void *));
445       GST_CAT_LOG (GST_CAT_PERFORMANCE, "exceeding pre-alloced array");
446     }
447 
448     list->n_allocated = want_alloc;
449   }
450 
451   if (idx < list->n_buffers) {
452     memmove (&list->buffers[idx + 1], &list->buffers[idx],
453         (list->n_buffers - idx) * sizeof (void *));
454   }
455 
456   ++list->n_buffers;
457   list->buffers[idx] = buffer;
458   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buffer),
459       GST_MINI_OBJECT_CAST (list));
460 }
461 
462 /**
463  * gst_buffer_list_remove:
464  * @list: a #GstBufferList
465  * @idx: the index
466  * @length: the amount to remove
467  *
468  * Removes @length buffers starting from @idx in @list. The following buffers
469  * are moved to close the gap.
470  */
471 void
gst_buffer_list_remove(GstBufferList * list,guint idx,guint length)472 gst_buffer_list_remove (GstBufferList * list, guint idx, guint length)
473 {
474   g_return_if_fail (GST_IS_BUFFER_LIST (list));
475   g_return_if_fail (idx < list->n_buffers);
476   g_return_if_fail (idx + length <= list->n_buffers);
477   g_return_if_fail (gst_buffer_list_is_writable (list));
478 
479   gst_buffer_list_remove_range_internal (list, idx, length, TRUE);
480 }
481 
482 /**
483  * gst_buffer_list_copy_deep:
484  * @list: a #GstBufferList
485  *
486  * Creates a copy of the given buffer list. This will make a newly allocated
487  * copy of the buffers that the source buffer list contains.
488  *
489  * Returns: (transfer full): a new copy of @list.
490  *
491  * Since: 1.6
492  */
493 GstBufferList *
gst_buffer_list_copy_deep(const GstBufferList * list)494 gst_buffer_list_copy_deep (const GstBufferList * list)
495 {
496   guint i, len;
497   GstBufferList *result = NULL;
498 
499   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL);
500 
501   result = gst_buffer_list_new ();
502 
503   len = list->n_buffers;
504   for (i = 0; i < len; i++) {
505     GstBuffer *old = list->buffers[i];
506     GstBuffer *new = gst_buffer_copy_deep (old);
507 
508     if (G_LIKELY (new)) {
509       gst_buffer_list_insert (result, i, new);
510     } else {
511       g_warning
512           ("Failed to deep copy buffer %p while deep "
513           "copying buffer list %p. Buffer list copy "
514           "will be incomplete", old, list);
515     }
516   }
517 
518   return result;
519 }
520 
521 /**
522  * gst_buffer_list_calculate_size:
523  * @list: a #GstBufferList
524  *
525  * Calculates the size of the data contained in @list by adding the
526  * size of all buffers.
527  *
528  * Returns: the size of the data contained in @list in bytes.
529  *
530  * Since: 1.14
531  */
532 gsize
gst_buffer_list_calculate_size(GstBufferList * list)533 gst_buffer_list_calculate_size (GstBufferList * list)
534 {
535   GstBuffer **buffers;
536   gsize size = 0;
537   guint i, n;
538 
539   g_return_val_if_fail (GST_IS_BUFFER_LIST (list), 0);
540 
541   n = list->n_buffers;
542   buffers = list->buffers;
543 
544   for (i = 0; i < n; ++i)
545     size += gst_buffer_get_size (buffers[i]);
546 
547   return size;
548 }
549 
550 /**
551  * gst_buffer_list_ref: (skip)
552  * @list: a #GstBufferList
553  *
554  * Increases the refcount of the given buffer list by one.
555  *
556  * Note that the refcount affects the writability of @list and its data, see
557  * gst_buffer_list_make_writable(). It is important to note that keeping
558  * additional references to GstBufferList instances can potentially increase
559  * the number of memcpy operations in a pipeline.
560  *
561  * Returns: (transfer full): @list
562  */
563 GstBufferList *
gst_buffer_list_ref(GstBufferList * list)564 gst_buffer_list_ref (GstBufferList * list)
565 {
566   return
567       GST_BUFFER_LIST_CAST (gst_mini_object_ref (GST_MINI_OBJECT_CAST (list)));
568 }
569 
570 /**
571  * gst_buffer_list_unref: (skip)
572  * @list: (transfer full): a #GstBufferList
573  *
574  * Decreases the refcount of the buffer list. If the refcount reaches 0, the
575  * buffer list will be freed.
576  */
577 void
gst_buffer_list_unref(GstBufferList * list)578 gst_buffer_list_unref (GstBufferList * list)
579 {
580   gst_mini_object_unref (GST_MINI_OBJECT_CAST (list));
581 }
582 
583 /**
584  * gst_clear_buffer_list: (skip)
585  * @list_ptr: a pointer to a #GstBufferList reference
586  *
587  * Clears a reference to a #GstBufferList.
588  *
589  * @list_ptr must not be %NULL.
590  *
591  * If the reference is %NULL then this function does nothing. Otherwise, the
592  * reference count of the list is decreased and the pointer is set to %NULL.
593  *
594  * Since: 1.16
595  */
596 void
gst_clear_buffer_list(GstBufferList ** list_ptr)597 gst_clear_buffer_list (GstBufferList ** list_ptr)
598 {
599   gst_clear_mini_object ((GstMiniObject **) list_ptr);
600 }
601 
602 /**
603  * gst_buffer_list_copy: (skip)
604  * @list: a #GstBufferList
605  *
606  * Creates a shallow copy of the given buffer list. This will make a newly
607  * allocated copy of the source list with copies of buffer pointers. The
608  * refcount of buffers pointed to will be increased by one.
609  *
610  * Returns: (transfer full): a new copy of @list.
611  */
612 GstBufferList *
gst_buffer_list_copy(const GstBufferList * list)613 gst_buffer_list_copy (const GstBufferList * list)
614 {
615   return
616       GST_BUFFER_LIST_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST
617           (list)));
618 }
619 
620 /**
621  * gst_buffer_list_replace:
622  * @old_list: (inout) (transfer full) (nullable): pointer to a pointer to a
623  *     #GstBufferList to be replaced.
624  * @new_list: (transfer none) (allow-none): pointer to a #GstBufferList that
625  *     will replace the buffer list pointed to by @old_list.
626  *
627  * Modifies a pointer to a #GstBufferList to point to a different
628  * #GstBufferList. The modification is done atomically (so this is useful for
629  * ensuring thread safety in some cases), and the reference counts are updated
630  * appropriately (the old buffer list is unreffed, the new is reffed).
631  *
632  * Either @new_list or the #GstBufferList pointed to by @old_list may be %NULL.
633  *
634  * Returns: %TRUE if @new_list was different from @old_list
635  *
636  * Since: 1.16
637  */
638 gboolean
gst_buffer_list_replace(GstBufferList ** old_list,GstBufferList * new_list)639 gst_buffer_list_replace (GstBufferList ** old_list, GstBufferList * new_list)
640 {
641   return gst_mini_object_replace ((GstMiniObject **) old_list,
642       (GstMiniObject *) new_list);
643 }
644 
645 /**
646  * gst_buffer_list_take:
647  * @old_list: (inout) (transfer full): pointer to a pointer to a #GstBufferList
648  *     to be replaced.
649  * @new_list: (transfer full) (allow-none): pointer to a #GstBufferList
650  *     that will replace the bufferlist pointed to by @old_list.
651  *
652  * Modifies a pointer to a #GstBufferList to point to a different
653  * #GstBufferList. This function is similar to gst_buffer_list_replace() except
654  * that it takes ownership of @new_list.
655  *
656  * Returns: %TRUE if @new_list was different from @old_list
657  *
658  * Since: 1.16
659  */
660 gboolean
gst_buffer_list_take(GstBufferList ** old_list,GstBufferList * new_list)661 gst_buffer_list_take (GstBufferList ** old_list, GstBufferList * new_list)
662 {
663   return gst_mini_object_take ((GstMiniObject **) old_list,
664       (GstMiniObject *) new_list);
665 }
666