X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstbuffer.c;h=25c521360d1389cdf54c8486afed65526522c036;hb=94ba1cea88788fd3ad72fadcc2ceb7884ed6df50;hp=008ce6a9f192bb4aa7cb94a9139c4f667ed9af2a;hpb=313f01ab79c9377c05bc1ca4f9c24a5cd522c2be;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 008ce6a..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; @@ -105,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" @@ -124,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 @@ -141,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 { @@ -158,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) @@ -228,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", @@ -267,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++; @@ -286,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. */ @@ -314,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); } @@ -328,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 } /** @@ -399,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) { @@ -449,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) { @@ -467,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; } } @@ -485,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)); + } } } } @@ -506,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; @@ -515,15 +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); - GST_BUFFER_FLAG_UNSET (copy, GST_BUFFER_FLAG_TAG_MEMORY); + 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 @@ -577,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)); } @@ -636,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, @@ -676,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, @@ -771,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; @@ -796,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) @@ -862,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 * @@ -883,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); @@ -903,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) @@ -924,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 * @@ -940,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 * @@ -960,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 * @@ -1111,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. @@ -1203,10 +1453,12 @@ gst_buffer_is_memory_range_writable (GstBuffer * buffer, guint idx, gint length) FALSE); if (length == -1) - length = len - idx; + len -= idx; + else + len = length; for (i = 0; i < len; i++) { - if (!gst_memory_is_writable (GST_BUFFER_MEM_PTR (buffer, i))) + if (!gst_memory_is_writable (GST_BUFFER_MEM_PTR (buffer, i + idx))) return FALSE; } return TRUE; @@ -1234,8 +1486,8 @@ gst_buffer_is_all_memory_writable (GstBuffer * buffer) /** * 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. * @@ -1272,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. * @@ -1454,13 +1706,17 @@ 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); } - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); } offset = noffs; @@ -1580,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; } } @@ -1676,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. @@ -1827,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 @@ -1842,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 * @@ -1915,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; @@ -1931,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) @@ -1957,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 @@ -1964,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, @@ -1980,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); @@ -1994,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; @@ -2036,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); @@ -2054,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 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) @@ -2089,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 @@ -2132,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; @@ -2144,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; @@ -2157,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 */ @@ -2170,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; }