X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=gst%2Fgstbufferlist.c;h=2c1359769e2ee5e529a6bb5e13ca8ea16c524c4e;hb=5470f6df00595f4ab44871e0e633bf15006abc5c;hp=06f817ff795d53b1dfe8b6aa1c8393bca1d21bfa;hpb=4f8bb4085fe1653462e1d3523cd8ed8082040301;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbufferlist.c b/gst/gstbufferlist.c index 06f817f..2c13597 100644 --- a/gst/gstbufferlist.c +++ b/gst/gstbufferlist.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 2009 Axis Communications * @author Jonas Holmberg + * Copyright (C) 2014 Tim-Philipp Müller * * gstbufferlist.c: Buffer list * @@ -16,12 +17,13 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstbufferlist + * @title: GstBufferList * @short_description: Lists of buffers for data-passing * @see_also: #GstPad, #GstMiniObject * @@ -30,26 +32,40 @@ * Buffer lists are created with gst_buffer_list_new() and filled with data * using a gst_buffer_list_insert(). * + * Buffer lists can be pushed on a srcpad with gst_pad_push_list(). This is + * interesting when multiple buffers need to be pushed in one go because it + * can reduce the amount of overhead for pushing each buffer individually. */ #include "gst_private.h" #include "gstbuffer.h" #include "gstbufferlist.h" +#include "gstutils.h" #define GST_CAT_DEFAULT GST_CAT_BUFFER_LIST +#define GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY(list) \ + ((list)->buffers != &(list)->arr[0]) + /** * GstBufferList: * * Opaque list of grouped buffers. - * - * Since: 0.10.24 */ struct _GstBufferList { GstMiniObject mini_object; - GArray *array; + GstBuffer **buffers; + guint n_buffers; + guint n_allocated; + + gsize slice_size; + + /* one-item array, in reality more items are pre-allocated + * as part of the GstBufferList structure, and that + * pre-allocated array extends beyond the declared struct */ + GstBuffer *arr[1]; }; GType _gst_buffer_list_type = 0; @@ -68,15 +84,18 @@ _gst_buffer_list_copy (GstBufferList * list) GstBufferList *copy; guint i, len; - len = list->array->len; - copy = gst_buffer_list_sized_new (len); + len = list->n_buffers; + copy = gst_buffer_list_new_sized (list->n_allocated); /* add and ref all buffers in the array */ for (i = 0; i < len; i++) { - GstBuffer *buf = g_array_index (list->array, GstBuffer *, i); - buf = gst_buffer_ref (buf); - g_array_append_val (copy->array, buf); + copy->buffers[i] = gst_buffer_ref (list->buffers[i]); + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (copy->buffers[i]), + GST_MINI_OBJECT_CAST (copy)); } + + copy->n_buffers = len; + return copy; } @@ -84,33 +103,47 @@ static void _gst_buffer_list_free (GstBufferList * list) { guint i, len; + gsize slice_size; + GST_LOG ("free %p", list); /* unrefs all buffers too */ - len = list->array->len; - for (i = 0; i < len; i++) - gst_buffer_unref (g_array_index (list->array, GstBuffer *, i)); - g_array_free (list->array, TRUE); + len = list->n_buffers; + for (i = 0; i < len; i++) { + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[i]), + GST_MINI_OBJECT_CAST (list)); + gst_buffer_unref (list->buffers[i]); + } + + if (GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY (list)) + g_free (list->buffers); - g_slice_free1 (GST_MINI_OBJECT_SIZE (list), list); + slice_size = list->slice_size; + +#ifdef USE_POISONING + memset (list, 0xff, slice_size); +#endif + + g_slice_free1 (slice_size, list); } static void -gst_buffer_list_init (GstBufferList * list, gsize size, guint asize) +gst_buffer_list_init (GstBufferList * list, guint n_allocated, gsize slice_size) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (list), _gst_buffer_list_type, - size); - - list->mini_object.copy = (GstMiniObjectCopyFunction) _gst_buffer_list_copy; - list->mini_object.free = (GstMiniObjectFreeFunction) _gst_buffer_list_free; + gst_mini_object_init (GST_MINI_OBJECT_CAST (list), 0, _gst_buffer_list_type, + (GstMiniObjectCopyFunction) _gst_buffer_list_copy, NULL, + (GstMiniObjectFreeFunction) _gst_buffer_list_free); - list->array = g_array_sized_new (FALSE, FALSE, sizeof (GstBuffer *), asize); + list->buffers = &list->arr[0]; + list->n_buffers = 0; + list->n_allocated = n_allocated; + list->slice_size = slice_size; GST_LOG ("init %p", list); } /** - * gst_buffer_list_sized_new: + * gst_buffer_list_new_sized: * @size: an initial reserved size * * Creates a new, empty #GstBufferList. The caller is responsible for unreffing @@ -121,19 +154,26 @@ gst_buffer_list_init (GstBufferList * list, gsize size, guint asize) * * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref() * after usage. - * - * Since: 0.10.24 */ GstBufferList * -gst_buffer_list_sized_new (guint size) +gst_buffer_list_new_sized (guint size) { GstBufferList *list; + gsize slice_size; + guint n_allocated; + + if (size == 0) + size = 1; + + n_allocated = GST_ROUND_UP_16 (size); - list = g_slice_new0 (GstBufferList); + slice_size = sizeof (GstBufferList) + (n_allocated - 1) * sizeof (gpointer); + + list = g_slice_alloc0 (slice_size); GST_LOG ("new %p", list); - gst_buffer_list_init (list, sizeof (GstBufferList), size); + gst_buffer_list_init (list, n_allocated, slice_size); return list; } @@ -148,31 +188,49 @@ gst_buffer_list_sized_new (guint size) * * Returns: (transfer full): the new #GstBufferList. gst_buffer_list_unref() * after usage. - * - * Since: 0.10.24 */ GstBufferList * gst_buffer_list_new (void) { - return gst_buffer_list_sized_new (8); + return gst_buffer_list_new_sized (8); } /** - * gst_buffer_list_len: + * gst_buffer_list_length: * @list: a #GstBufferList * * Returns the number of buffers in @list. * * Returns: the number of buffers in the buffer list - * - * Since: 0.10.24 */ guint -gst_buffer_list_len (GstBufferList * list) +gst_buffer_list_length (GstBufferList * list) { g_return_val_if_fail (GST_IS_BUFFER_LIST (list), 0); - return list->array->len; + return list->n_buffers; +} + +static inline void +gst_buffer_list_remove_range_internal (GstBufferList * list, guint idx, + guint length, gboolean unref_old) +{ + guint i; + + if (unref_old) { + for (i = idx; i < idx + length; ++i) { + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[i]), + GST_MINI_OBJECT_CAST (list)); + gst_buffer_unref (list->buffers[i]); + } + } + + if (idx + length != list->n_buffers) { + memmove (&list->buffers[idx], &list->buffers[idx + length], + (list->n_buffers - (idx + length)) * sizeof (void *)); + } + + list->n_buffers -= length; } /** @@ -184,35 +242,84 @@ gst_buffer_list_len (GstBufferList * list) * Call @func with @data for each buffer in @list. * * @func can modify the passed buffer pointer or its contents. The return value - * of @func define if this function returns or if the remaining buffers in a - * group should be skipped. + * of @func define if this function returns or if the remaining buffers in + * the list should be skipped. * - * Since: 0.10.24 + * Returns: %TRUE when @func returned %TRUE for each buffer in @list or when + * @list is empty. */ -void +gboolean gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func, gpointer user_data) { guint i, len; + gboolean ret = TRUE; + gboolean list_was_writable, first_warning = TRUE; - g_return_if_fail (GST_IS_BUFFER_LIST (list)); - g_return_if_fail (func != NULL); + g_return_val_if_fail (GST_IS_BUFFER_LIST (list), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + list_was_writable = gst_buffer_list_is_writable (list); - len = list->array->len; + len = list->n_buffers; for (i = 0; i < len;) { GstBuffer *buf, *buf_ret; - gboolean ret; + gboolean was_writable; + + buf = buf_ret = list->buffers[i]; + + /* If the buffer is writable, we remove us as parent for now to + * allow the callback to destroy the buffer. If we get the buffer + * back, we add ourselves as parent again. + * + * Non-writable buffers just get another reference as they were not + * writable to begin with, and they would possibly become writable + * by removing ourselves as parent + */ + was_writable = list_was_writable && gst_buffer_is_writable (buf); + + if (was_writable) + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (buf), + GST_MINI_OBJECT_CAST (list)); + else + gst_buffer_ref (buf); - buf = buf_ret = g_array_index (list->array, GstBuffer *, i); ret = func (&buf_ret, i, user_data); /* Check if the function changed the buffer */ if (buf != buf_ret) { - if (buf_ret == NULL) { - g_array_remove_index (list->array, i); + /* If the list was not writable but the callback was actually changing + * our buffer, then it wouldn't have been allowed to do so. + * + * Fortunately we still have a reference to the old buffer in that case + * and just not modify the list, unref the new buffer (if any) and warn + * about this */ + if (!list_was_writable) { + if (first_warning) { + g_critical + ("gst_buffer_list_foreach: non-writable list %p was changed from callback", + list); + first_warning = FALSE; + } + if (buf_ret) + gst_buffer_unref (buf_ret); + } else if (buf_ret == NULL) { + gst_buffer_list_remove_range_internal (list, i, 1, !was_writable); + --len; } else { - g_array_index (list->array, GstBuffer *, i) = buf_ret; + if (!was_writable) + gst_buffer_unref (buf); + + list->buffers[i] = buf_ret; + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buf_ret), + GST_MINI_OBJECT_CAST (list)); } + } else { + if (was_writable) + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buf), + GST_MINI_OBJECT_CAST (list)); + else + gst_buffer_unref (buf); } if (!ret) @@ -222,6 +329,7 @@ gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func, if (buf_ret != NULL) i++; } + return ret; } /** @@ -231,29 +339,75 @@ gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func, * * Get the buffer at @idx. * - * Returns: (transfer none): the buffer at @idx in @group or NULL when there - * is no buffer. The buffer remains valid as long as @list is valid. + * You must make sure that @idx does not exceed the number of + * buffers available. * - * Since: 0.10.24 + * Returns: (transfer none) (nullable): the buffer at @idx in @group + * or %NULL when there is no buffer. The buffer remains valid as + * long as @list is valid and buffer is not removed from the list. */ GstBuffer * gst_buffer_list_get (GstBufferList * list, guint idx) { - GstBuffer *buf; - g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL); - g_return_val_if_fail (idx < list->array->len, NULL); + g_return_val_if_fail (idx < list->n_buffers, NULL); + + return list->buffers[idx]; +} - buf = g_array_index (list->array, GstBuffer *, idx); +/** + * gst_buffer_list_get_writable: + * @list: a (writable) #GstBufferList + * @idx: the index + * + * Gets the buffer at @idx, ensuring it is a writable buffer. + * + * You must make sure that @idx does not exceed the number of + * buffers available. + * + * Returns: (transfer none) (nullable): the buffer at @idx in @group. + * The returned buffer remains valid as long as @list is valid and + * the buffer is not removed from the list. + * + * Since: 1.14 + */ +GstBuffer * +gst_buffer_list_get_writable (GstBufferList * list, guint idx) +{ + GstBuffer *new_buf; - return buf; + g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL); + g_return_val_if_fail (gst_buffer_list_is_writable (list), NULL); + g_return_val_if_fail (idx < list->n_buffers, NULL); + + /* We have to implement this manually here to correctly add/remove the + * parent */ + if (gst_buffer_is_writable (list->buffers[idx])) + return list->buffers[idx]; + + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (list->buffers[idx]), + GST_MINI_OBJECT_CAST (list)); + new_buf = gst_buffer_copy (list->buffers[idx]); + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (new_buf), + GST_MINI_OBJECT_CAST (list)); + gst_buffer_unref (list->buffers[idx]); + list->buffers[idx] = new_buf; + + return new_buf; } /** + * gst_buffer_list_add: + * @l: a #GstBufferList + * @b: a #GstBuffer + * + * Append @b at the end of @l. + */ +/** * gst_buffer_list_insert: * @list: a #GstBufferList * @idx: the index - * @buffer: a #GstBuffer + * @buffer: (transfer full): a #GstBuffer * * Insert @buffer at @idx in @list. Other buffers are moved to make room for * this new buffer. @@ -261,24 +415,135 @@ gst_buffer_list_get (GstBufferList * list, guint idx) * A -1 value for @idx will append the buffer at the end. */ void -gst_buffer_list_insert (GstBufferList * list, guint idx, GstBuffer * buffer) +gst_buffer_list_insert (GstBufferList * list, gint idx, GstBuffer * buffer) { + guint want_alloc; + g_return_if_fail (GST_IS_BUFFER_LIST (list)); g_return_if_fail (buffer != NULL); + g_return_if_fail (gst_buffer_list_is_writable (list)); - if (idx == -1) - g_array_append_val (list->array, buffer); - else { - g_return_if_fail (idx < list->array->len); - g_array_insert_val (list->array, idx, buffer); + if (idx == -1 && list->n_buffers < list->n_allocated) { + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buffer), + GST_MINI_OBJECT_CAST (list)); + list->buffers[list->n_buffers++] = buffer; + return; } + + if (idx == -1 || idx > list->n_buffers) + idx = list->n_buffers; + + want_alloc = list->n_buffers + 1; + + if (want_alloc > list->n_allocated) { + want_alloc = MAX (GST_ROUND_UP_16 (want_alloc), list->n_allocated * 2); + + if (GST_BUFFER_LIST_IS_USING_DYNAMIC_ARRAY (list)) { + list->buffers = g_renew (GstBuffer *, list->buffers, want_alloc); + } else { + list->buffers = g_new0 (GstBuffer *, want_alloc); + memcpy (list->buffers, &list->arr[0], list->n_buffers * sizeof (void *)); + GST_CAT_LOG (GST_CAT_PERFORMANCE, "exceeding pre-alloced array"); + } + + list->n_allocated = want_alloc; + } + + if (idx < list->n_buffers) { + memmove (&list->buffers[idx + 1], &list->buffers[idx], + (list->n_buffers - idx) * sizeof (void *)); + } + + ++list->n_buffers; + list->buffers[idx] = buffer; + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (buffer), + GST_MINI_OBJECT_CAST (list)); } +/** + * gst_buffer_list_remove: + * @list: a #GstBufferList + * @idx: the index + * @length: the amount to remove + * + * Remove @length buffers starting from @idx in @list. The following buffers + * are moved to close the gap. + */ void gst_buffer_list_remove (GstBufferList * list, guint idx, guint length) { g_return_if_fail (GST_IS_BUFFER_LIST (list)); - g_return_if_fail (idx < list->array->len); + g_return_if_fail (idx < list->n_buffers); + g_return_if_fail (idx + length <= list->n_buffers); + g_return_if_fail (gst_buffer_list_is_writable (list)); + + gst_buffer_list_remove_range_internal (list, idx, length, TRUE); +} + +/** + * gst_buffer_list_copy_deep: + * @list: a #GstBufferList + * + * Create a copy of the given buffer list. This will make a newly allocated + * copy of the buffer that the source buffer list contains. + * + * Returns: (transfer full): a new copy of @list. + * + * Since: 1.6 + */ +GstBufferList * +gst_buffer_list_copy_deep (const GstBufferList * list) +{ + guint i, len; + GstBufferList *result = NULL; + + g_return_val_if_fail (GST_IS_BUFFER_LIST (list), NULL); + + result = gst_buffer_list_new (); + + len = list->n_buffers; + for (i = 0; i < len; i++) { + GstBuffer *old = list->buffers[i]; + GstBuffer *new = gst_buffer_copy_deep (old); + + if (G_LIKELY (new)) { + gst_buffer_list_insert (result, i, new); + } else { + g_warning + ("Failed to deep copy buffer %p while deep " + "copying buffer list %p. Buffer list copy " + "will be incomplete", old, list); + } + } + + return result; +} + +/** + * gst_buffer_list_calculate_size: + * @list: a #GstBufferList + * + * Calculates the size of the data contained in buffer list by adding the + * size of all buffers. + * + * Returns: the size of the data contained in buffer list in bytes. + * + * Since: 1.14 + */ +gsize +gst_buffer_list_calculate_size (GstBufferList * list) +{ + GstBuffer **buffers; + gsize size = 0; + guint i, n; + + g_return_val_if_fail (GST_IS_BUFFER_LIST (list), 0); + + n = list->n_buffers; + buffers = list->buffers; + + for (i = 0; i < n; ++i) + size += gst_buffer_get_size (buffers[i]); - g_array_remove_range (list->array, idx, length); + return size; }