X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstbuffer.c;h=25c521360d1389cdf54c8486afed65526522c036;hb=53bf06c08814ff9ac3968d47daf11a395cf26a01;hp=8ba2e6f37cdad75cefefebdba4206a75fbe4593a;hpb=64affd3e61de7c92e417a45c2dc5dc1db825eceb;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 8ba2e6f..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,9 +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. - * - * Creating a buffer for a video frame - * + * |[ * GstBuffer *buffer; * GstMemory *memory; * gint size, width, height, bpp; @@ -45,11 +44,10 @@ * memory = gst_allocator_alloc (NULL, size, NULL); * gst_buffer_insert_memory (buffer, -1, memory); * ... - * - * + * ]| * - * Alternatively, use gst_buffer_new_allocate() - * to create a buffer with preallocated data of a given size. + * Alternatively, use gst_buffer_new_allocate() to create a buffer with + * preallocated data of a given size. * * Buffers can contain a list of #GstMemory objects. You can retrieve how many * memory objects with gst_buffer_n_memory() and you can get a pointer @@ -71,7 +69,7 @@ * produced so far. For compressed data, it could be the byte offset in a * source or destination file. Likewise, the end offset will be the offset of * the end of the buffer. These can only be meaningfully interpreted if you - * know the media type of the buffer (the preceeding CAPS event). Either or both + * know the media type of the buffer (the preceding CAPS event). Either or both * can be set to #GST_BUFFER_OFFSET_NONE. * * gst_buffer_ref() is used to increase the refcount of a buffer. This must be @@ -91,7 +89,7 @@ * * Several flags of the buffer can be set and unset with the * GST_BUFFER_FLAG_SET() and GST_BUFFER_FLAG_UNSET() macros. Use - * GST_BUFFER_FLAG_IS_SET() to test if a certain #GstBufferFlag is set. + * GST_BUFFER_FLAG_IS_SET() to test if a certain #GstBufferFlags flag is set. * * Buffers can be efficiently merged into a larger buffer with * gst_buffer_append(). Copying of memory will only be done when absolutely @@ -108,7 +106,15 @@ * unreffed as well. Buffers allocated from a #GstBufferPool will be returned to * the pool when the refcount drops to 0. * - * Last reviewed on 2012-03-28 (0.11.3) + * 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" @@ -127,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 @@ -144,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 { @@ -161,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) @@ -216,7 +251,7 @@ _get_merged_memory (GstBuffer * buffer, guint idx, guint length) GstMemory *parent = NULL; gsize size, poffset = 0; - size = gst_buffer_get_size (buffer); + size = gst_buffer_get_sizes_range (buffer, idx, length, NULL, NULL); if (G_UNLIKELY (_is_span (mem + idx, length, &poffset, &parent))) { if (!GST_MEMORY_IS_NO_SHARE (parent)) @@ -231,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 < length && left > 0; i++) { - gst_memory_map (mem[i], &sinfo, GST_MAP_READ); + for (i = idx; i < (idx + length) && left > 0; i++) { + 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", @@ -270,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++; @@ -282,19 +333,118 @@ _replace_memory (GstBuffer * buffer, guint len, guint idx, guint length, } if (end < len) { - g_memmove (&GST_BUFFER_MEM_PTR (buffer, idx), + memmove (&GST_BUFFER_MEM_PTR (buffer, idx), &GST_BUFFER_MEM_PTR (buffer, end), (len - end) * sizeof (gpointer)); } GST_BUFFER_MEM_LEN (buffer) = len - 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. */ @@ -316,10 +466,12 @@ _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); } GST_DEFINE_MINI_OBJECT_TYPE (GstBuffer, gst_buffer); @@ -328,6 +480,30 @@ 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 +} + +/** + * gst_buffer_get_max_memory: + * + * Get the maximum amount of memory blocks that a buffer can hold. This is a + * compile time constant that can be queried with the function. + * + * When more memory blocks are added, existing memory blocks will be merged + * together to make room for the new block. + * + * Returns: the maximum amount of memory blocks that a buffer can hold. + * + * Since: 1.2 + */ +guint +gst_buffer_get_max_memory (void) +{ + return GST_BUFFER_MEM_MAX; } /** @@ -380,7 +556,11 @@ gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src, if (flags & GST_BUFFER_COPY_FLAGS) { /* copy flags */ - GST_MINI_OBJECT_FLAGS (dest) = GST_MINI_OBJECT_FLAGS (src); + guint flags_mask = ~GST_BUFFER_FLAG_TAG_MEMORY; + + GST_MINI_OBJECT_FLAGS (dest) = + (GST_MINI_OBJECT_FLAGS (src) & flags_mask) | + (GST_MINI_OBJECT_FLAGS (dest) & ~flags_mask); } if (flags & GST_BUFFER_COPY_TIMESTAMPS) { @@ -430,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) { @@ -448,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; } } @@ -466,19 +651,36 @@ 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; copy_data.offset = offset; copy_data.size = size; - info->transform_func (dest, meta, src, - _gst_meta_transform_copy, ©_data); + if (!info->transform_func (dest, meta, src, + _gst_meta_transform_copy, ©_data)) { + GST_CAT_ERROR (GST_CAT_BUFFER, + "failed to copy meta %p of API type %s", meta, + g_type_name (info->api)); + } } } } @@ -487,7 +689,7 @@ gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src, } static GstBuffer * -_gst_buffer_copy (GstBuffer * buffer) +gst_buffer_copy_with_flags (const GstBuffer * buffer, GstBufferCopyFlags flags) { GstBuffer *copy; @@ -496,13 +698,41 @@ _gst_buffer_copy (GstBuffer * buffer) /* create a fresh new buffer */ copy = gst_buffer_new (); - /* we simply copy everything from our parent */ - if (!gst_buffer_copy_into (copy, buffer, GST_BUFFER_COPY_ALL, 0, -1)) + /* copy what the 'flags' want from our parent */ + /* FIXME why we can't pass const to gst_buffer_copy_into() ? */ + if (!gst_buffer_copy_into (copy, (GstBuffer *) buffer, flags, 0, -1)) gst_buffer_replace (©, NULL); + if (copy) + GST_BUFFER_FLAG_UNSET (copy, GST_BUFFER_FLAG_TAG_MEMORY); + return copy; } +static GstBuffer * +_gst_buffer_copy (const GstBuffer * buffer) +{ + return gst_buffer_copy_with_flags (buffer, GST_BUFFER_COPY_ALL); +} + +/** + * gst_buffer_copy_deep: + * @buf: a #GstBuffer. + * + * Create a copy of the given buffer. This will make a newly allocated + * 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) +{ + return gst_buffer_copy_with_flags (buffer, + GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP); +} + /* the default dispose function revives the buffer and returns it to the * pool when there is a pool */ static gboolean @@ -556,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)); } @@ -615,23 +847,23 @@ gst_buffer_new (void) /** * gst_buffer_new_allocate: - * @allocator: (transfer none) (allow-none): the #GstAllocator to use, or NULL to use the + * @allocator: (transfer none) (allow-none): the #GstAllocator to use, or %NULL to use the * default allocator * @size: the size in bytes of the new buffer's data. * @params: (transfer none) (allow-none): optional parameters * * Tries to create a newly allocated buffer with data of the given size and * extra parameters from @allocator. If the requested amount of memory can't be - * allocated, NULL will be returned. The allocated buffer memory is not cleared. + * allocated, %NULL will be returned. The allocated buffer memory is not cleared. * - * When @allocator is NULL, the default memory allocator will be used. + * When @allocator is %NULL, the default memory allocator will be used. * * Note that when @size == 0, the buffer will not have memory associated with it. * * MT safe. * - * Returns: (transfer full): a new #GstBuffer, or NULL if the memory couldn't - * be allocated. + * Returns: (transfer full) (nullable): a new #GstBuffer, or %NULL if + * the memory couldn't be allocated. */ GstBuffer * gst_buffer_new_allocate (GstAllocator * allocator, gsize size, @@ -655,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, @@ -704,6 +938,7 @@ gst_buffer_new_allocate (GstAllocator * allocator, gsize size, if (size > 0) _memory_add (newbuf, -1, gst_memory_ref (mem), TRUE); #endif + GST_BUFFER_FLAG_UNSET (newbuf, GST_BUFFER_FLAG_TAG_MEMORY); return newbuf; @@ -719,12 +954,12 @@ no_memory: /** * gst_buffer_new_wrapped_full: * @flags: #GstMemoryFlags - * @data: (array length=size) (element-type guint8): data to wrap + * @data: (array length=size) (element-type guint8) (transfer none): data to wrap * @maxsize: allocated size of @data * @offset: offset in @data * @size: size of valid data - * @user_data: user_data - * @notify: called with @user_data when the memory is freed + * @user_data: (allow-none): user_data + * @notify: (allow-none) (scope async) (closure user_data): called with @user_data when the memory is freed * * Allocate a new buffer that wraps the given memory. @data must point to * @maxsize of memory, the wrapped buffer will have the region from @offset and @@ -742,19 +977,23 @@ gst_buffer_new_wrapped_full (GstMemoryFlags flags, gpointer data, gsize maxsize, gsize offset, gsize size, gpointer user_data, GDestroyNotify notify) { + GstMemory *mem; GstBuffer *newbuf; newbuf = gst_buffer_new (); - gst_buffer_append_memory (newbuf, - gst_memory_new_wrapped (flags, data, maxsize, offset, size, - user_data, notify)); + mem = + gst_memory_new_wrapped (flags, data, maxsize, offset, size, user_data, + notify); + 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; } /** * gst_buffer_new_wrapped: - * @data: (array length=size) (element-type guint8): data to wrap + * @data: (array length=size) (element-type guint8) (transfer full): data to wrap * @size: allocated size of @data * * Creates a new buffer that wraps the given @data. The memory will be freed @@ -771,12 +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. + * 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) @@ -793,6 +1060,9 @@ gst_buffer_n_memory (GstBuffer * buffer) * * Prepend the memory block @mem to @buffer. This function takes * ownership of @mem and thus doesn't increase its refcount. + * + * This function is identical to gst_buffer_insert_memory() with an index of 0. + * See gst_buffer_insert_memory() for more details. */ void gst_buffer_prepend_memory (GstBuffer * buffer, GstMemory * mem) @@ -807,6 +1077,9 @@ gst_buffer_prepend_memory (GstBuffer * buffer, GstMemory * mem) * * Append the memory block @mem to @buffer. This function takes * ownership of @mem and thus doesn't increase its refcount. + * + * This function is identical to gst_buffer_insert_memory() with an index of -1. + * See gst_buffer_insert_memory() for more details. */ void gst_buffer_append_memory (GstBuffer * buffer, GstMemory * mem) @@ -822,17 +1095,26 @@ gst_buffer_append_memory (GstBuffer * buffer, GstMemory * mem) * * Insert the memory block @mem to @buffer at @idx. This function takes ownership * of @mem and thus doesn't increase its refcount. + * + * Only gst_buffer_get_max_memory() can be added to a buffer. If more memory is + * added, existing memory blocks will automatically be merged to make room for + * the new memory. */ 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 * @@ -847,10 +1129,15 @@ _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); @@ -866,11 +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. * - * Since this call does not influence the refcount of the memory, - * gst_memory_is_writable() can be used to check if @buffer is the sole owner - * of the returned memory. - * - * Returns: (transfer none): the #GstMemory at @idx. + * Returns: (transfer none) (nullable): the #GstMemory at @idx. */ GstMemory * gst_buffer_peek_memory (GstBuffer * buffer, guint idx) @@ -891,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 * @@ -907,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 * @@ -927,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 * @@ -1078,9 +1361,9 @@ gst_buffer_remove_memory_range (GstBuffer * buffer, guint idx, gint length) * in @buffer. * * When this function returns %TRUE, @idx will contain the index of the first - * memory bock where the byte for @offset can be found and @length contains the + * memory block where the byte for @offset can be found and @length contains the * number of memory blocks containing the @size remaining bytes. @skip contains - * the number of bytes to skip in the memory bock at @idx to get to the byte + * the number of bytes to skip in the memory block at @idx to get to the byte * for @offset. * * @size can be -1 to get all the memory blocks after @idx. @@ -1139,10 +1422,72 @@ gst_buffer_find_memory (GstBuffer * buffer, gsize offset, gsize size, } /** + * gst_buffer_is_memory_range_writable: + * @buffer: a #GstBuffer. + * @idx: an index + * @length: a length should not be 0 + * + * Check if @length memory blocks in @buffer starting from @idx are writable. + * + * @length can be -1 to check all the memory blocks after @idx. + * + * Note that this function does not check if @buffer is writable, use + * gst_buffer_is_writable() to check that if needed. + * + * Returns: %TRUE if the memory range is writable + * + * Since: 1.4 + */ +gboolean +gst_buffer_is_memory_range_writable (GstBuffer * buffer, guint idx, gint length) +{ + guint i, len; + + g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); + + GST_CAT_DEBUG (GST_CAT_BUFFER, "idx %u, length %d", idx, length); + + len = GST_BUFFER_MEM_LEN (buffer); + g_return_val_if_fail ((len == 0 && idx == 0 && length == -1) || + (length == -1 && idx < len) || (length > 0 && length + idx <= len), + FALSE); + + if (length == -1) + len -= idx; + else + len = length; + + for (i = 0; i < len; i++) { + if (!gst_memory_is_writable (GST_BUFFER_MEM_PTR (buffer, i + idx))) + return FALSE; + } + return TRUE; +} + +/** + * gst_buffer_is_all_memory_writable: + * @buffer: a #GstBuffer. + * + * Check if all memory blocks in @buffer are writable. + * + * Note that this function does not check if @buffer is writable, use + * gst_buffer_is_writable() to check that if needed. + * + * Returns: %TRUE if all memory blocks in @buffer are writable + * + * Since: 1.4 + */ +gboolean +gst_buffer_is_all_memory_writable (GstBuffer * buffer) +{ + return gst_buffer_is_memory_range_writable (buffer, 0, -1); +} + +/** * gst_buffer_get_sizes: * @buffer: a #GstBuffer. - * @offset: (out): a pointer to the offset - * @maxsize: (out): a pointer to the maxsize + * @offset: (out) (allow-none): a pointer to the offset + * @maxsize: (out) (allow-none): a pointer to the maxsize * * Get the total size of the memory blocks in @b. * @@ -1179,8 +1524,8 @@ gst_buffer_get_size (GstBuffer * buffer) * @buffer: a #GstBuffer. * @idx: an index * @length: a length - * @offset: (out): a pointer to the offset - * @maxsize: (out): a pointer to the maxsize + * @offset: (out) (allow-none): a pointer to the offset + * @maxsize: (out) (allow-none): a pointer to the maxsize * * Get the total size of @length memory blocks stating from @idx in @buffer. * @@ -1249,7 +1594,7 @@ gst_buffer_get_sizes_range (GstBuffer * buffer, guint idx, gint length, /** * gst_buffer_resize: * @buffer: a #GstBuffer. - * @offset: the offset adjustement + * @offset: the offset adjustment * @size: the new size or -1 to just adjust the offset * * Set the offset and total size of the memory blocks in @buffer. @@ -1278,7 +1623,7 @@ gst_buffer_set_size (GstBuffer * buffer, gssize size) * @buffer: a #GstBuffer. * @idx: an index * @length: a length - * @offset: the offset adjustement + * @offset: the offset adjustment * @size: the new size or -1 to just adjust the offset * * Set the total size of the @length memory blocks starting at @idx in @@ -1361,10 +1706,16 @@ 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); } } @@ -1485,21 +1836,20 @@ not_writable: { GST_WARNING_OBJECT (buffer, "write map requested on non-writable buffer"); g_critical ("write map requested on non-writable buffer"); + memset (info, 0, sizeof (GstMapInfo)); return FALSE; } no_memory: { /* empty buffer, we need to return NULL */ GST_DEBUG_OBJECT (buffer, "can't get buffer memory"); - info->memory = NULL; - info->data = NULL; - info->size = 0; - info->maxsize = 0; + memset (info, 0, sizeof (GstMapInfo)); return TRUE; } cannot_map: { GST_DEBUG_OBJECT (buffer, "cannot map memory"); + memset (info, 0, sizeof (GstMapInfo)); return FALSE; } } @@ -1529,7 +1879,7 @@ gst_buffer_unmap (GstBuffer * buffer, GstMapInfo * info) * gst_buffer_fill: * @buffer: a #GstBuffer. * @offset: the offset to fill - * @src: the source address + * @src: (array length=size) (element-type guint8): the source address * @size: the size to fill * * Copy @size bytes from @src to @buffer at @offset. @@ -1546,7 +1896,7 @@ gst_buffer_fill (GstBuffer * buffer, gsize offset, gconstpointer src, g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); g_return_val_if_fail (gst_buffer_is_writable (buffer), 0); - g_return_val_if_fail (src != NULL, 0); + g_return_val_if_fail (src != NULL || size == 0, 0); GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, offset %" G_GSIZE_FORMAT ", size %" G_GSIZE_FORMAT, buffer, @@ -1581,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. @@ -1631,7 +1982,7 @@ gst_buffer_extract (GstBuffer * buffer, gsize offset, gpointer dest, gsize size) * gst_buffer_memcmp: * @buffer: a #GstBuffer. * @offset: the offset in @buffer - * @mem: the memory to compare + * @mem: (array length=size) (element-type guint8): the memory to compare * @size: the size to compare * * Compare @size bytes starting from @offset in @buffer with the memory in @mem. @@ -1653,6 +2004,9 @@ gst_buffer_memcmp (GstBuffer * buffer, gsize offset, gconstpointer mem, "buffer %p, offset %" G_GSIZE_FORMAT ", size %" G_GSIZE_FORMAT, buffer, offset, size); + if (G_UNLIKELY (gst_buffer_get_size (buffer) < offset + size)) + return -1; + len = GST_BUFFER_MEM_LEN (buffer); for (i = 0; i < len && size > 0 && res == 0; i++) { @@ -1729,14 +2083,15 @@ 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. + * @size: the size of the new #GstBuffer sub-buffer, in bytes. If -1, all + * data is copied. * * 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 @@ -1744,7 +2099,7 @@ gst_buffer_memset (GstBuffer * buffer, gsize offset, guint8 val, gsize size) * * MT safe. * - * Returns: (transfer full): the new #GstBuffer or NULL if the arguments were + * Returns: (transfer full): the new #GstBuffer or %NULL if the arguments were * invalid. */ GstBuffer * @@ -1817,11 +2172,14 @@ 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; + GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_TAG_MEMORY); gst_buffer_unref (buf2); return buf1; @@ -1832,10 +2190,14 @@ gst_buffer_append_region (GstBuffer * buf1, GstBuffer * buf2, gssize offset, * @buffer: a #GstBuffer * @api: the #GType of an API * - * Get the metadata for @api on buffer. When there is no such - * metadata, NULL is returned. + * Get the metadata for @api on buffer. When there is no such metadata, %NULL is + * returned. If multiple metadata with the given @api are attached to this + * buffer only the first one is returned. To handle multiple metadata with a + * given API use gst_buffer_iterate_meta() or gst_buffer_foreach_meta() instead + * and check the meta->info.api member for the API type. * - * Returns: (transfer none): the metadata for @api on @buffer. + * Returns: (transfer none) (nullable): the metadata for @api on + * @buffer. */ GstMeta * gst_buffer_get_meta (GstBuffer * buffer, GType api) @@ -1858,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 @@ -1865,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, @@ -1881,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); @@ -1895,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; @@ -1937,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); @@ -1955,17 +2360,17 @@ 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. * - * @state will be updated with an opage state pointer + * @state will be updated with an opaque state pointer * - * Returns: (transfer none): The next #GstMeta or %NULL when there are - * no more items. + * Returns: (transfer none) (nullable): The next #GstMeta or %NULL + * when there are no more items. */ GstMeta * gst_buffer_iterate_meta (GstBuffer * buffer, gpointer * state) @@ -1990,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 @@ -2033,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; @@ -2045,9 +2500,341 @@ 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; } return res; } + +/** + * gst_buffer_extract_dup: + * @buffer: a #GstBuffer + * @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. 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 + * newly-allocated memory. @dest must be freed using g_free() when done. + * + * Since: 1.0.10 + */ + +void +gst_buffer_extract_dup (GstBuffer * buffer, gsize offset, gsize size, + gpointer * dest, gsize * dest_size) +{ + gsize real_size, alloc_size; + + real_size = gst_buffer_get_size (buffer); + + 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); + } + + return meta_info; +}