X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstbuffer.c;h=25c521360d1389cdf54c8486afed65526522c036;hb=94ba1cea88788fd3ad72fadcc2ceb7884ed6df50;hp=07d1e29999e48c8765d48d656e698150dd09f164;hpb=a5ab02733a614a258e0effcdeb91968f4795cb7b;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 07d1e29..25c5213 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -22,6 +22,7 @@ /** * SECTION:gstbuffer + * @title: GstBuffer * @short_description: Data-passing buffer type * @see_also: #GstPad, #GstMiniObject, #GstMemory, #GstMeta, #GstBufferPool * @@ -33,7 +34,7 @@ * created one will typically allocate memory for it and add it to the buffer. * The following example creates a buffer that can hold a given video frame * with a given width, height and bits per plane. - * |[ + * |[ * GstBuffer *buffer; * GstMemory *memory; * gint size, width, height, bpp; @@ -104,6 +105,16 @@ * the refcount drops to 0, any memory and metadata pointed to by the buffer is * unreffed as well. Buffers allocated from a #GstBufferPool will be returned to * the pool when the refcount drops to 0. + * + * The #GstParentBufferMeta is a meta which can be attached to a #GstBuffer + * to hold a reference to another buffer that is only released when the child + * #GstBuffer is released. + * + * Typically, #GstParentBufferMeta is used when the child buffer is directly + * using the #GstMemory of the parent buffer, and wants to prevent the parent + * buffer from being returned to a buffer pool until the #GstMemory is available + * for re-use. (Since: 1.6) + * */ #include "gst_private.h" @@ -122,14 +133,9 @@ GType _gst_buffer_type = 0; -typedef struct _GstMetaItem GstMetaItem; - -struct _GstMetaItem -{ - GstMetaItem *next; - GstMeta meta; -}; -#define ITEM_SIZE(info) ((info)->size + sizeof (GstMetaItem)) +/* info->size will be sizeof(FooMeta) which contains a GstMeta at the beginning + * too, and then there is again a GstMeta in GstMetaItem, so subtract one. */ +#define ITEM_SIZE(info) ((info)->size + sizeof (GstMetaItem) - sizeof (GstMeta)) #define GST_BUFFER_MEM_MAX 16 @@ -139,6 +145,7 @@ struct _GstMetaItem #define GST_BUFFER_MEM_PTR(b,i) (((GstBufferImpl *)(b))->mem[i]) #define GST_BUFFER_BUFMEM(b) (((GstBufferImpl *)(b))->bufmem) #define GST_BUFFER_META(b) (((GstBufferImpl *)(b))->item) +#define GST_BUFFER_TAIL_META(b) (((GstBufferImpl *)(b))->tail_item) typedef struct { @@ -156,8 +163,41 @@ typedef struct /* FIXME, make metadata allocation more efficient by using part of the * GstBufferImpl */ GstMetaItem *item; + GstMetaItem *tail_item; } GstBufferImpl; +static gint64 meta_seq; /* 0 *//* ATOMIC */ + +/* TODO: use GLib's once https://gitlab.gnome.org/GNOME/glib/issues/1076 lands */ +#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) +static inline gint64 +gst_atomic_int64_inc (volatile gint64 * atomic) +{ + return __sync_fetch_and_add (atomic, 1); +} +#elif defined (G_PLATFORM_WIN32) +#include +static inline gint64 +gst_atomic_int64_inc (volatile gint64 * atomic) +{ + return InterlockedExchangeAdd64 (atomic, 1); +} +#else +#warning No 64-bit atomic int defined for this platform/toolchain! +#define NO_64BIT_ATOMIC_INT_FOR_PLATFORM +G_LOCK_DEFINE_STATIC (meta_seq); +static inline gint64 +gst_atomic_int64_inc (volatile gint64 * atomic) +{ + gint64 ret; + + G_LOCK (meta_seq); + ret = *atomic++; + G_UNLOCK (meta_seq); + + return ret; +} +#endif static gboolean _is_span (GstMemory ** mem, gsize len, gsize * poffset, GstMemory ** parent) @@ -226,13 +266,25 @@ _get_merged_memory (GstBuffer * buffer, guint idx, guint length) guint8 *ptr; result = gst_allocator_alloc (NULL, size, NULL); - gst_memory_map (result, &dinfo, GST_MAP_WRITE); + if (result == NULL || !gst_memory_map (result, &dinfo, GST_MAP_WRITE)) { + GST_CAT_ERROR (GST_CAT_BUFFER, "Failed to map memory writable"); + if (result) + gst_memory_unref (result); + return NULL; + } ptr = dinfo.data; left = size; for (i = idx; i < (idx + length) && left > 0; i++) { - gst_memory_map (mem[i], &sinfo, GST_MAP_READ); + if (!gst_memory_map (mem[i], &sinfo, GST_MAP_READ)) { + GST_CAT_ERROR (GST_CAT_BUFFER, + "buffer %p, idx %u, length %u failed to map readable", buffer, + idx, length); + gst_memory_unmap (result, &dinfo); + gst_memory_unref (result); + return NULL; + } tocopy = MIN (sinfo.size, left); GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "memcpy %" G_GSIZE_FORMAT " bytes for merge %p from memory %p", @@ -265,11 +317,15 @@ _replace_memory (GstBuffer * buffer, guint len, guint idx, guint length, GstMemory *old = GST_BUFFER_MEM_PTR (buffer, i); gst_memory_unlock (old, GST_LOCK_FLAG_EXCLUSIVE); + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (old), + GST_MINI_OBJECT_CAST (buffer)); gst_memory_unref (old); } if (mem != NULL) { /* replace with single memory */ + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (mem), + GST_MINI_OBJECT_CAST (buffer)); gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE); GST_BUFFER_MEM_PTR (buffer, idx) = mem; idx++; @@ -284,13 +340,111 @@ _replace_memory (GstBuffer * buffer, guint len, guint idx, guint length, GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); } +/** + * gst_buffer_get_flags: + * @buffer: a #GstBuffer + * + * Get the #GstBufferFlags flags set on this buffer. + * + * Returns: the flags set on this buffer. + * + * Since: 1.10 + */ +GstBufferFlags +gst_buffer_get_flags (GstBuffer * buffer) +{ + return (GstBufferFlags) GST_BUFFER_FLAGS (buffer); +} + +/** + * gst_buffer_flag_is_set: + * @buffer: a #GstBuffer + * @flags: the #GstBufferFlags flag to check. + * + * Gives the status of a specific flag on a buffer. + * + * Returns: %TRUE if all flags in @flags are found on @buffer. + * + * Since: 1.10 + */ +gboolean +gst_buffer_has_flags (GstBuffer * buffer, GstBufferFlags flags) +{ + return GST_BUFFER_FLAG_IS_SET (buffer, flags); +} + +/** + * gst_buffer_set_flags: + * @buffer: a #GstBuffer + * @flags: the #GstBufferFlags to set. + * + * Sets one or more buffer flags on a buffer. + * + * Returns: %TRUE if @flags were successfully set on buffer. + * + * Since: 1.10 + */ +gboolean +gst_buffer_set_flags (GstBuffer * buffer, GstBufferFlags flags) +{ + GST_BUFFER_FLAG_SET (buffer, flags); + return TRUE; +} + +/** + * gst_buffer_unset_flags: + * @buffer: a #GstBuffer + * @flags: the #GstBufferFlags to clear + * + * Clears one or more buffer flags. + * + * Returns: true if @flags is successfully cleared from buffer. + * + * Since: 1.10 + */ +gboolean +gst_buffer_unset_flags (GstBuffer * buffer, GstBufferFlags flags) +{ + GST_BUFFER_FLAG_UNSET (buffer, flags); + return TRUE; +} + + + +/* transfer full for return and transfer none for @mem */ +static inline GstMemory * +_memory_get_exclusive_reference (GstMemory * mem) +{ + GstMemory *ret = NULL; + + if (gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE)) { + ret = gst_memory_ref (mem); + } else { + /* we cannot take another exclusive lock as the memory is already + * locked WRITE + EXCLUSIVE according to part-miniobject.txt */ + ret = gst_memory_copy (mem, 0, -1); + + if (ret) { + if (!gst_memory_lock (ret, GST_LOCK_FLAG_EXCLUSIVE)) { + gst_memory_unref (ret); + ret = NULL; + } + } + } + + if (!ret) + GST_CAT_WARNING (GST_CAT_BUFFER, "Failed to acquire an exclusive lock for " + "memory %p", mem); + + return ret; +} + static inline void -_memory_add (GstBuffer * buffer, gint idx, GstMemory * mem, gboolean lock) +_memory_add (GstBuffer * buffer, gint idx, GstMemory * mem) { guint i, len = GST_BUFFER_MEM_LEN (buffer); - GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, idx %d, mem %p, lock %d", buffer, - idx, mem, lock); + GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, idx %d, mem %p", buffer, idx, mem); if (G_UNLIKELY (len >= GST_BUFFER_MEM_MAX)) { /* too many buffer, span them. */ @@ -312,10 +466,10 @@ _memory_add (GstBuffer * buffer, gint idx, GstMemory * mem, gboolean lock) GST_BUFFER_MEM_PTR (buffer, i) = GST_BUFFER_MEM_PTR (buffer, i - 1); } /* and insert the new buffer */ - if (lock) - gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE); GST_BUFFER_MEM_PTR (buffer, idx) = mem; GST_BUFFER_MEM_LEN (buffer) = len + 1; + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (mem), + GST_MINI_OBJECT_CAST (buffer)); GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); } @@ -326,6 +480,11 @@ void _priv_gst_buffer_initialize (void) { _gst_buffer_type = gst_buffer_get_type (); + +#ifdef NO_64BIT_ATOMIC_INT_FOR_PLATFORM + GST_CAT_WARNING (GST_CAT_PERFORMANCE, + "No 64-bit atomic int defined for this platform/toolchain!"); +#endif } /** @@ -451,17 +610,22 @@ gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src, if (tocopy < bsize && !deep && !GST_MEMORY_IS_NO_SHARE (mem)) { /* we need to clip something */ newmem = gst_memory_share (mem, skip, tocopy); - if (newmem) + if (newmem) { + gst_memory_lock (newmem, GST_LOCK_FLAG_EXCLUSIVE); skip = 0; + } } if (deep || GST_MEMORY_IS_NO_SHARE (mem) || (!newmem && tocopy < bsize)) { /* deep copy or we're not allowed to share this memory * between buffers, always copy then */ newmem = gst_memory_copy (mem, skip, tocopy); - skip = 0; + if (newmem) { + gst_memory_lock (newmem, GST_LOCK_FLAG_EXCLUSIVE); + skip = 0; + } } else if (!newmem) { - newmem = gst_memory_ref (mem); + newmem = _memory_get_exclusive_reference (mem); } if (!newmem) { @@ -469,7 +633,7 @@ gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src, return FALSE; } - _memory_add (dest, -1, newmem, TRUE); + _memory_add (dest, -1, newmem); left -= tocopy; } } @@ -487,11 +651,24 @@ gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src, } if (flags & GST_BUFFER_COPY_META) { + /* NOTE: GstGLSyncMeta copying relies on the meta + * being copied now, after the buffer data, + * so this has to happen last */ for (walk = GST_BUFFER_META (src); walk; walk = walk->next) { GstMeta *meta = &walk->meta; const GstMetaInfo *info = meta->info; - if (info->transform_func) { + /* Don't copy memory metas if we only copied part of the buffer, didn't + * copy memories or merged memories. In all these cases the memory + * structure has changed and the memory meta becomes meaningless. + */ + if ((region || !(flags & GST_BUFFER_COPY_MEMORY) + || (flags & GST_BUFFER_COPY_MERGE)) + && gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) { + GST_CAT_DEBUG (GST_CAT_BUFFER, + "don't copy memory meta %p of API type %s", meta, + g_type_name (info->api)); + } else if (info->transform_func) { GstMetaTransformCopy copy_data; copy_data.region = region; @@ -546,6 +723,8 @@ _gst_buffer_copy (const GstBuffer * buffer) * copy of the data the source buffer contains. * * Returns: (transfer full): a new copy of @buf. + * + * Since: 1.6 */ GstBuffer * gst_buffer_copy_deep (const GstBuffer * buffer) @@ -607,6 +786,8 @@ _gst_buffer_free (GstBuffer * buffer) len = GST_BUFFER_MEM_LEN (buffer); for (i = 0; i < len; i++) { gst_memory_unlock (GST_BUFFER_MEM_PTR (buffer, i), GST_LOCK_FLAG_EXCLUSIVE); + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (GST_BUFFER_MEM_PTR + (buffer, i)), GST_MINI_OBJECT_CAST (buffer)); gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i)); } @@ -706,8 +887,10 @@ gst_buffer_new_allocate (GstAllocator * allocator, gsize size, newbuf = gst_buffer_new (); - if (mem != NULL) - _memory_add (newbuf, -1, mem, TRUE); + if (mem != NULL) { + gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE); + _memory_add (newbuf, -1, mem); + } GST_CAT_LOG (GST_CAT_BUFFER, "new buffer %p of size %" G_GSIZE_FORMAT " from allocator %p", newbuf, @@ -801,7 +984,8 @@ gst_buffer_new_wrapped_full (GstMemoryFlags flags, gpointer data, mem = gst_memory_new_wrapped (flags, data, maxsize, offset, size, user_data, notify); - _memory_add (newbuf, -1, mem, TRUE); + gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE); + _memory_add (newbuf, -1, mem); GST_BUFFER_FLAG_UNSET (newbuf, GST_BUFFER_FLAG_TAG_MEMORY); return newbuf; @@ -826,13 +1010,40 @@ gst_buffer_new_wrapped (gpointer data, gsize size) } /** + * gst_buffer_new_wrapped_bytes: + * @bytes: (transfer none): a #GBytes to wrap + * + * Creates a new #GstBuffer that wraps the given @bytes. The data inside + * @bytes cannot be %NULL and the resulting buffer will be marked as read only. + * + * MT safe. + * + * Returns: (transfer full): a new #GstBuffer wrapping @bytes + * + * Since: 1.16 + */ +GstBuffer * +gst_buffer_new_wrapped_bytes (GBytes * bytes) +{ + guint8 *bytes_data; + gsize size; + + g_return_val_if_fail (bytes != NULL, NULL); + bytes_data = (guint8 *) g_bytes_get_data (bytes, &size); + g_return_val_if_fail (bytes_data != NULL, NULL); + + return gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, bytes_data, + size, 0, size, g_bytes_ref (bytes), (GDestroyNotify) g_bytes_unref); +} + +/** * gst_buffer_n_memory: * @buffer: a #GstBuffer. * * Get the amount of memory blocks that this buffer has. This amount is never * larger than what gst_buffer_get_max_memory() returns. * - * Returns: (transfer full): the amount of memory block in this buffer. + * Returns: the number of memory blocks this buffer is made of. */ guint gst_buffer_n_memory (GstBuffer * buffer) @@ -892,13 +1103,18 @@ gst_buffer_append_memory (GstBuffer * buffer, GstMemory * mem) void gst_buffer_insert_memory (GstBuffer * buffer, gint idx, GstMemory * mem) { + GstMemory *tmp; + g_return_if_fail (GST_IS_BUFFER (buffer)); g_return_if_fail (gst_buffer_is_writable (buffer)); g_return_if_fail (mem != NULL); g_return_if_fail (idx == -1 || (idx >= 0 && idx <= GST_BUFFER_MEM_LEN (buffer))); - _memory_add (buffer, idx, mem, TRUE); + tmp = _memory_get_exclusive_reference (mem); + g_return_if_fail (tmp != NULL); + gst_memory_unref (mem); + _memory_add (buffer, idx, tmp); } static GstMemory * @@ -913,10 +1129,14 @@ _get_mapped (GstBuffer * buffer, guint idx, GstMapInfo * info, if (mapped != mem) { /* memory changed, lock new memory */ + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (mapped), + GST_MINI_OBJECT_CAST (buffer)); gst_memory_lock (mapped, GST_LOCK_FLAG_EXCLUSIVE); GST_BUFFER_MEM_PTR (buffer, idx) = mapped; /* unlock old memory */ gst_memory_unlock (mem, GST_LOCK_FLAG_EXCLUSIVE); + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (mem), + GST_MINI_OBJECT_CAST (buffer)); GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); } gst_memory_unref (mem); @@ -933,7 +1153,7 @@ _get_mapped (GstBuffer * buffer, guint idx, GstMapInfo * info, * the memory block in @buffer is removed, replaced or merged, typically with * any call that modifies the memory in @buffer. * - * Returns: (transfer none): the #GstMemory at @idx. + * Returns: (transfer none) (nullable): the #GstMemory at @idx. */ GstMemory * gst_buffer_peek_memory (GstBuffer * buffer, guint idx) @@ -954,7 +1174,7 @@ gst_buffer_peek_memory (GstBuffer * buffer, guint idx) * * Get the memory block at index @idx in @buffer. * - * Returns: (transfer full): a #GstMemory that contains the data of the + * Returns: (transfer full) (nullable): a #GstMemory that contains the data of the * memory block at @idx. Use gst_memory_unref () after usage. */ GstMemory * @@ -970,7 +1190,7 @@ gst_buffer_get_memory (GstBuffer * buffer, guint idx) * Get all the memory block in @buffer. The memory blocks will be merged * into one large #GstMemory. * - * Returns: (transfer full): a #GstMemory that contains the merged memory. + * Returns: (transfer full) (nullable): a #GstMemory that contains the merged memory. * Use gst_memory_unref () after usage. */ GstMemory * @@ -990,7 +1210,7 @@ gst_buffer_get_all_memory (GstBuffer * buffer) * * If @length is -1, all memory starting from @idx is merged. * - * Returns: (transfer full): a #GstMemory that contains the merged data of @length + * Returns: (transfer full) (nullable): a #GstMemory that contains the merged data of @length * blocks starting at @idx. Use gst_memory_unref () after usage. */ GstMemory * @@ -1486,9 +1706,13 @@ gst_buffer_resize_range (GstBuffer * buffer, guint idx, gint length, if (newmem == NULL) return FALSE; + gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (newmem), + GST_MINI_OBJECT_CAST (buffer)); gst_memory_lock (newmem, GST_LOCK_FLAG_EXCLUSIVE); GST_BUFFER_MEM_PTR (buffer, i) = newmem; gst_memory_unlock (mem, GST_LOCK_FLAG_EXCLUSIVE); + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (mem), + GST_MINI_OBJECT_CAST (buffer)); gst_memory_unref (mem); GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); @@ -1707,7 +1931,8 @@ gst_buffer_fill (GstBuffer * buffer, gsize offset, gconstpointer src, * gst_buffer_extract: * @buffer: a #GstBuffer. * @offset: the offset to extract - * @dest: the destination address + * @dest: (out caller-allocates) (array length=size) (element-type guint8): + * the destination address * @size: the size to extract * * Copy @size bytes starting from @offset in @buffer to @dest. @@ -1858,7 +2083,7 @@ gst_buffer_memset (GstBuffer * buffer, gsize offset, guint8 val, gsize size) * gst_buffer_copy_region: * @parent: a #GstBuffer. * @flags: the #GstBufferCopyFlags - * @offset: the offset into parent #GstBuffer at which the new sub-buffer + * @offset: the offset into parent #GstBuffer at which the new sub-buffer * begins. * @size: the size of the new #GstBuffer sub-buffer, in bytes. If -1, all * data is copied. @@ -1866,7 +2091,7 @@ gst_buffer_memset (GstBuffer * buffer, gsize offset, guint8 val, gsize size) * Creates a sub-buffer from @parent at @offset and @size. * This sub-buffer uses the actual memory space of the parent buffer. * This function will copy the offset and timestamp fields when the - * offset is 0. If not, they will be set to #GST_CLOCK_TIME_NONE and + * offset is 0. If not, they will be set to #GST_CLOCK_TIME_NONE and * #GST_BUFFER_OFFSET_NONE. * If @offset equals 0 and @size equals the total size of @buffer, the * duration and offset end fields are also copied. If not they will be set @@ -1947,8 +2172,10 @@ gst_buffer_append_region (GstBuffer * buf1, GstBuffer * buf2, gssize offset, GstMemory *mem; mem = GST_BUFFER_MEM_PTR (buf2, i); + gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (mem), + GST_MINI_OBJECT_CAST (buf2)); GST_BUFFER_MEM_PTR (buf2, i) = NULL; - _memory_add (buf1, -1, mem, FALSE); + _memory_add (buf1, -1, mem); } GST_BUFFER_MEM_LEN (buf2) = 0; @@ -1993,6 +2220,28 @@ gst_buffer_get_meta (GstBuffer * buffer, GType api) } /** + * gst_buffer_get_n_meta: + * @buffer: a #GstBuffer + * @api_type: the #GType of an API + * + * Returns: number of metas of type @api_type on @buffer. + * + * Since: 1.14 + */ +guint +gst_buffer_get_n_meta (GstBuffer * buffer, GType api_type) +{ + gpointer state = NULL; + GstMeta *meta; + guint n = 0; + + while ((meta = gst_buffer_iterate_meta_filtered (buffer, &state, api_type))) + ++n; + + return n; +} + +/** * gst_buffer_add_meta: * @buffer: a #GstBuffer * @info: a #GstMetaInfo @@ -2000,7 +2249,7 @@ gst_buffer_get_meta (GstBuffer * buffer, GType api) * * Add metadata for @info to @buffer using the parameters in @params. * - * Returns: (transfer none): the metadata for the api in @info on @buffer. + * Returns: (transfer none) (nullable): the metadata for the api in @info on @buffer. */ GstMeta * gst_buffer_add_meta (GstBuffer * buffer, const GstMetaInfo * info, @@ -2016,11 +2265,17 @@ gst_buffer_add_meta (GstBuffer * buffer, const GstMetaInfo * info, /* create a new slice */ size = ITEM_SIZE (info); - item = g_slice_alloc (size); + /* We warn in gst_meta_register() about metas without + * init function but let's play safe here and prevent + * uninitialized memory + */ + if (!info->init_func) + item = g_slice_alloc0 (size); + else + item = g_slice_alloc (size); result = &item->meta; result->info = info; result->flags = GST_META_FLAG_NONE; - GST_CAT_DEBUG (GST_CAT_BUFFER, "alloc metadata %p (%s) of size %" G_GSIZE_FORMAT, result, g_type_name (info->type), info->size); @@ -2030,9 +2285,16 @@ gst_buffer_add_meta (GstBuffer * buffer, const GstMetaInfo * info, if (!info->init_func (result, params, buffer)) goto init_failed; - /* and add to the list of metadata */ - item->next = GST_BUFFER_META (buffer); - GST_BUFFER_META (buffer) = item; + item->seq_num = gst_atomic_int64_inc (&meta_seq); + item->next = NULL; + + if (!GST_BUFFER_META (buffer)) { + GST_BUFFER_META (buffer) = item; + GST_BUFFER_TAIL_META (buffer) = item; + } else { + GST_BUFFER_TAIL_META (buffer)->next = item; + GST_BUFFER_TAIL_META (buffer) = item; + } return result; @@ -2072,10 +2334,18 @@ gst_buffer_remove_meta (GstBuffer * buffer, GstMeta * meta) const GstMetaInfo *info = meta->info; /* remove from list */ + if (GST_BUFFER_TAIL_META (buffer) == walk) { + if (prev != walk) + GST_BUFFER_TAIL_META (buffer) = prev; + else + GST_BUFFER_TAIL_META (buffer) = NULL; + } + if (GST_BUFFER_META (buffer) == walk) GST_BUFFER_META (buffer) = walk->next; else prev->next = walk->next; + /* call free_func if any */ if (info->free_func) info->free_func (m, buffer); @@ -2090,9 +2360,9 @@ gst_buffer_remove_meta (GstBuffer * buffer, GstMeta * meta) } /** - * gst_buffer_iterate_meta: + * gst_buffer_iterate_meta: (skip) * @buffer: a #GstBuffer - * @state: an opaque state pointer + * @state: (out caller-allocates): an opaque state pointer * * Retrieve the next #GstMeta after @current. If @state points * to %NULL, the first metadata is returned. @@ -2125,6 +2395,49 @@ gst_buffer_iterate_meta (GstBuffer * buffer, gpointer * state) } /** + * gst_buffer_iterate_meta_filtered: (skip) + * @buffer: a #GstBuffer + * @state: (out caller-allocates): an opaque state pointer + * @meta_api_type: only return #GstMeta of this type + * + * Retrieve the next #GstMeta of type @meta_api_type after the current one + * according to @state. If @state points to %NULL, the first metadata of + * type @meta_api_type is returned. + * + * @state will be updated with an opaque state pointer + * + * Returns: (transfer none) (nullable): The next #GstMeta of type + * @meta_api_type or %NULL when there are no more items. + * + * Since: 1.12 + */ +GstMeta * +gst_buffer_iterate_meta_filtered (GstBuffer * buffer, gpointer * state, + GType meta_api_type) +{ + GstMetaItem **meta; + + g_return_val_if_fail (buffer != NULL, NULL); + g_return_val_if_fail (state != NULL, NULL); + + meta = (GstMetaItem **) state; + if (*meta == NULL) + /* state NULL, move to first item */ + *meta = GST_BUFFER_META (buffer); + else + /* state !NULL, move to next item in list */ + *meta = (*meta)->next; + + while (*meta != NULL && (*meta)->meta.info->api != meta_api_type) + *meta = (*meta)->next; + + if (*meta) + return &(*meta)->meta; + else + return NULL; +} + +/** * gst_buffer_foreach_meta: * @buffer: a #GstBuffer * @func: (scope call): a #GstBufferForeachMetaFunc to call @@ -2168,9 +2481,16 @@ gst_buffer_foreach_meta (GstBuffer * buffer, GstBufferForeachMetaFunc func, g_return_val_if_fail (!GST_META_FLAG_IS_SET (m, GST_META_FLAG_LOCKED), FALSE); + if (GST_BUFFER_TAIL_META (buffer) == walk) { + if (prev != walk) + GST_BUFFER_TAIL_META (buffer) = prev; + else + GST_BUFFER_TAIL_META (buffer) = NULL; + } + /* remove from list */ if (GST_BUFFER_META (buffer) == walk) - GST_BUFFER_META (buffer) = next; + prev = GST_BUFFER_META (buffer) = next; else prev->next = next; @@ -2180,6 +2500,8 @@ gst_buffer_foreach_meta (GstBuffer * buffer, GstBufferForeachMetaFunc func, /* and free the slice */ g_slice_free1 (ITEM_SIZE (info), walk); + } else { + prev = walk; } if (!res) break; @@ -2193,11 +2515,11 @@ gst_buffer_foreach_meta (GstBuffer * buffer, GstBufferForeachMetaFunc func, * @offset: the offset to extract * @size: the size to extract * @dest: (array length=dest_size) (element-type guint8) (out): A pointer where - * the destination array will be written. + * the destination array will be written. Might be %NULL if the size is 0. * @dest_size: (out): A location where the size of @dest can be written * - * Extracts a copy of at most @size bytes the data at @offset into a #GBytes. - * @dest must be freed using g_free() when done. + * Extracts a copy of at most @size bytes the data at @offset into + * newly-allocated memory. @dest must be freed using g_free() when done. * * Since: 1.0.10 */ @@ -2206,11 +2528,313 @@ void gst_buffer_extract_dup (GstBuffer * buffer, gsize offset, gsize size, gpointer * dest, gsize * dest_size) { - gsize real_size; + gsize real_size, alloc_size; real_size = gst_buffer_get_size (buffer); - *dest = g_malloc (MIN (real_size - offset, size)); + alloc_size = MIN (real_size - offset, size); + if (alloc_size == 0) { + *dest = NULL; + *dest_size = 0; + } else { + *dest = g_malloc (alloc_size); + *dest_size = gst_buffer_extract (buffer, offset, *dest, size); + } +} + +GST_DEBUG_CATEGORY_STATIC (gst_parent_buffer_meta_debug); + +/** + * gst_buffer_add_parent_buffer_meta: + * @buffer: (transfer none): a #GstBuffer + * @ref: (transfer none): a #GstBuffer to ref + * + * Add a #GstParentBufferMeta to @buffer that holds a reference on + * @ref until the buffer is freed. + * + * Returns: (transfer none) (nullable): The #GstParentBufferMeta that was added to the buffer + * + * Since: 1.6 + */ +GstParentBufferMeta * +gst_buffer_add_parent_buffer_meta (GstBuffer * buffer, GstBuffer * ref) +{ + GstParentBufferMeta *meta; + + g_return_val_if_fail (GST_IS_BUFFER (ref), NULL); + + meta = + (GstParentBufferMeta *) gst_buffer_add_meta (buffer, + GST_PARENT_BUFFER_META_INFO, NULL); + + if (!meta) + return NULL; + + meta->buffer = gst_buffer_ref (ref); + + return meta; +} + +static gboolean +_gst_parent_buffer_meta_transform (GstBuffer * dest, GstMeta * meta, + GstBuffer * buffer, GQuark type, gpointer data) +{ + GstParentBufferMeta *dmeta, *smeta; + + smeta = (GstParentBufferMeta *) meta; + + if (GST_META_TRANSFORM_IS_COPY (type)) { + /* copy over the reference to the parent buffer. + * Usually, this meta means we need to keep the parent buffer + * alive because one of the child memories is in use, which + * might not be the case if memory is deep copied or sub-regioned, + * but we can't tell, so keep the meta */ + dmeta = gst_buffer_add_parent_buffer_meta (dest, smeta->buffer); + if (!dmeta) + return FALSE; + + GST_CAT_DEBUG (gst_parent_buffer_meta_debug, + "copy buffer reference metadata"); + } else { + /* return FALSE, if transform type is not supported */ + return FALSE; + } + return TRUE; +} + +static void +_gst_parent_buffer_meta_free (GstParentBufferMeta * parent_meta, + GstBuffer * buffer) +{ + GST_CAT_DEBUG (gst_parent_buffer_meta_debug, + "Dropping reference on buffer %p", parent_meta->buffer); + gst_buffer_unref (parent_meta->buffer); +} + +static gboolean +_gst_parent_buffer_meta_init (GstParentBufferMeta * parent_meta, + gpointer params, GstBuffer * buffer) +{ + static volatile gsize _init; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (gst_parent_buffer_meta_debug, "parentbuffermeta", + 0, "parentbuffermeta"); + g_once_init_leave (&_init, 1); + } + + parent_meta->buffer = NULL; + + return TRUE; +} + +GType +gst_parent_buffer_meta_api_get_type (void) +{ + static volatile GType type = 0; + static const gchar *tags[] = { NULL }; + + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstParentBufferMetaAPI", tags); + g_once_init_leave (&type, _type); + } + + return type; +} + +/** + * gst_parent_buffer_meta_get_info: + * + * Get the global #GstMetaInfo describing the #GstParentBufferMeta meta. + * + * Returns: (transfer none): The #GstMetaInfo + * + * Since: 1.6 + */ +const GstMetaInfo * +gst_parent_buffer_meta_get_info (void) +{ + static const GstMetaInfo *meta_info = NULL; + + if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { + const GstMetaInfo *meta = + gst_meta_register (gst_parent_buffer_meta_api_get_type (), + "GstParentBufferMeta", + sizeof (GstParentBufferMeta), + (GstMetaInitFunction) _gst_parent_buffer_meta_init, + (GstMetaFreeFunction) _gst_parent_buffer_meta_free, + _gst_parent_buffer_meta_transform); + g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta); + } + + return meta_info; +} + +GST_DEBUG_CATEGORY_STATIC (gst_reference_timestamp_meta_debug); + +/** + * gst_buffer_add_reference_timestamp_meta: + * @buffer: (transfer none): a #GstBuffer + * @reference: (transfer none): identifier for the timestamp reference. + * @timestamp: timestamp + * @duration: duration, or %GST_CLOCK_TIME_NONE + * + * Add a #GstReferenceTimestampMeta to @buffer that holds a @timestamp and + * optionally @duration based on a specific timestamp @reference. See the + * documentation of #GstReferenceTimestampMeta for details. + * + * Returns: (transfer none) (nullable): The #GstReferenceTimestampMeta that was added to the buffer + * + * Since: 1.14 + */ +GstReferenceTimestampMeta * +gst_buffer_add_reference_timestamp_meta (GstBuffer * buffer, + GstCaps * reference, GstClockTime timestamp, GstClockTime duration) +{ + GstReferenceTimestampMeta *meta; + + g_return_val_if_fail (GST_IS_CAPS (reference), NULL); + g_return_val_if_fail (timestamp != GST_CLOCK_TIME_NONE, NULL); + + meta = + (GstReferenceTimestampMeta *) gst_buffer_add_meta (buffer, + GST_REFERENCE_TIMESTAMP_META_INFO, NULL); + + if (!meta) + return NULL; + + meta->reference = gst_caps_ref (reference); + meta->timestamp = timestamp; + meta->duration = duration; + + return meta; +} + +/** + * gst_buffer_get_reference_timestamp_meta: + * @buffer: a #GstBuffer + * @reference: (allow-none): a reference #GstCaps + * + * Find the first #GstReferenceTimestampMeta on @buffer that conforms to + * @reference. Conformance is tested by checking if the meta's reference is a + * subset of @reference. + * + * Buffers can contain multiple #GstReferenceTimestampMeta metadata items. + * + * Returns: (transfer none) (nullable): the #GstReferenceTimestampMeta or %NULL when there + * is no such metadata on @buffer. + * + * Since: 1.14 + */ +GstReferenceTimestampMeta * +gst_buffer_get_reference_timestamp_meta (GstBuffer * buffer, + GstCaps * reference) +{ + gpointer state = NULL; + GstMeta *meta; + const GstMetaInfo *info = GST_REFERENCE_TIMESTAMP_META_INFO; + + while ((meta = gst_buffer_iterate_meta (buffer, &state))) { + if (meta->info->api == info->api) { + GstReferenceTimestampMeta *rmeta = (GstReferenceTimestampMeta *) meta; + + if (!reference) + return rmeta; + if (gst_caps_is_subset (rmeta->reference, reference)) + return rmeta; + } + } + return NULL; +} + +static gboolean +_gst_reference_timestamp_meta_transform (GstBuffer * dest, GstMeta * meta, + GstBuffer * buffer, GQuark type, gpointer data) +{ + GstReferenceTimestampMeta *dmeta, *smeta; + + /* we copy over the reference timestamp meta, independent of transformation + * that happens. If it applied to the original buffer, it still applies to + * the new buffer as it refers to the time when the media was captured */ + smeta = (GstReferenceTimestampMeta *) meta; + dmeta = + gst_buffer_add_reference_timestamp_meta (dest, smeta->reference, + smeta->timestamp, smeta->duration); + if (!dmeta) + return FALSE; + + GST_CAT_DEBUG (gst_reference_timestamp_meta_debug, + "copy reference timestamp metadata from buffer %p to %p", buffer, dest); + + return TRUE; +} + +static void +_gst_reference_timestamp_meta_free (GstReferenceTimestampMeta * meta, + GstBuffer * buffer) +{ + if (meta->reference) + gst_caps_unref (meta->reference); +} + +static gboolean +_gst_reference_timestamp_meta_init (GstReferenceTimestampMeta * meta, + gpointer params, GstBuffer * buffer) +{ + static volatile gsize _init; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (gst_reference_timestamp_meta_debug, + "referencetimestampmeta", 0, "referencetimestampmeta"); + g_once_init_leave (&_init, 1); + } + + meta->reference = NULL; + meta->timestamp = GST_CLOCK_TIME_NONE; + meta->duration = GST_CLOCK_TIME_NONE; + + return TRUE; +} + +GType +gst_reference_timestamp_meta_api_get_type (void) +{ + static volatile GType type = 0; + static const gchar *tags[] = { NULL }; + + if (g_once_init_enter (&type)) { + GType _type = + gst_meta_api_type_register ("GstReferenceTimestampMetaAPI", tags); + g_once_init_leave (&type, _type); + } + + return type; +} + +/** + * gst_reference_timestamp_meta_get_info: + * + * Get the global #GstMetaInfo describing the #GstReferenceTimestampMeta meta. + * + * Returns: (transfer none): The #GstMetaInfo + * + * Since: 1.14 + */ +const GstMetaInfo * +gst_reference_timestamp_meta_get_info (void) +{ + static const GstMetaInfo *meta_info = NULL; + + if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { + const GstMetaInfo *meta = + gst_meta_register (gst_reference_timestamp_meta_api_get_type (), + "GstReferenceTimestampMeta", + sizeof (GstReferenceTimestampMeta), + (GstMetaInitFunction) _gst_reference_timestamp_meta_init, + (GstMetaFreeFunction) _gst_reference_timestamp_meta_free, + _gst_reference_timestamp_meta_transform); + g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta); + } - *dest_size = gst_buffer_extract (buffer, offset, *dest, size); + return meta_info; }