/**
* SECTION:gstbuffer
- * @short_description: Data-passing buffer type, supporting sub-buffers.
- * @see_also: #GstPad, #GstMiniObject, #GstBufferPool
+ * @short_description: Data-passing buffer type
+ * @see_also: #GstPad, #GstMiniObject, #GstMemory, #GstMeta, #GstBufferPool
*
- * Buffers are the basic unit of data transfer in GStreamer. The #GstBuffer
- * type provides all the state necessary to define the regions of memory as
- * part of a stream. Region copies are also supported, allowing a smaller
- * region of a buffer to become its own buffer, with mechanisms in place to
- * ensure that neither memory space goes away prematurely.
+ * Buffers are the basic unit of data transfer in GStreamer. They contain the
+ * timing and offset along with other arbitrary metadata that is associated
+ * with the #GstMemory blocks that the buffer contains.
*
* Buffers are usually created with gst_buffer_new(). After a buffer has been
* created one will typically allocate memory for it and add it to the buffer.
* ...
* size = width * height * bpp;
* buffer = gst_buffer_new ();
- * memory = gst_allocator_alloc (NULL, size, 0);
- * gst_buffer_take_memory (buffer, -1, memory);
+ * memory = gst_allocator_alloc (NULL, size, NULL);
+ * gst_buffer_insert_memory (buffer, -1, memory);
* ...
* </programlisting>
* </example>
* meaningful value can be given for these, they should be set. The timestamps
* and duration are measured in nanoseconds (they are #GstClockTime values).
*
+ * The buffer DTS refers to the timestamp when the buffer should be decoded and
+ * is usually monotonically increasing. The buffer PTS refers to the timestamp when
+ * the buffer content should be presented to the user and is not always
+ * monotonically increasing.
+ *
* A buffer can also have one or both of a start and an end offset. These are
* media-type specific. For video buffers, the start offset will generally be
* the frame number. For audio buffers, it will be the number of samples
* 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 #GstCaps set on it). Either or both
+ * know the media type of the buffer (the preceeding 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
* done when you want to keep a handle to the buffer after pushing it to the
- * next element.
+ * next element. The buffer refcount determines the writability of the buffer, a
+ * buffer is only writable when the refcount is exactly 1, i.e. when the caller
+ * has the only reference to the buffer.
*
* To efficiently create a smaller buffer out of an existing one, you can
- * use gst_buffer_copy_region().
+ * use gst_buffer_copy_region(). This method tries to share the memory objects
+ * between the two buffers.
*
* If a plug-in wants to modify the buffer data or metadata in-place, it should
* first obtain a buffer that is safe to modify by using
* GST_BUFFER_FLAG_IS_SET() to test if a certain #GstBufferFlag is set.
*
* Buffers can be efficiently merged into a larger buffer with
- * gst_buffer_span(), which avoids memory copies when the gst_buffer_is_span_fast()
- * function returns TRUE.
+ * gst_buffer_append(). Copying of memory will only be done when absolutely
+ * needed.
+ *
+ * Arbitrary extra metadata can be set on a buffer with gst_buffer_add_meta().
+ * Metadata can be retrieved with gst_buffer_get_meta(). See also #GstMeta
*
* An element should either unref the buffer or push it out on a src pad
* using gst_pad_push() (see #GstPad).
*
* Buffers are usually freed by unreffing them with gst_buffer_unref(). When
- * the refcount drops to 0, any data pointed to by the buffer is unreffed as
- * well.
+ * 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.
*
- * Last reviewed on November 8, 2011 (0.11.2)
+ * Last reviewed on 2012-03-28 (0.11.3)
*/
#include "gst_private.h"
#include "gstbufferpool.h"
#include "gstinfo.h"
#include "gstutils.h"
-#include "gstminiobject.h"
#include "gstversion.h"
GType _gst_buffer_type = 0;
-static GstMemory *_gst_buffer_arr_span (GstMemory ** mem[], gsize len[],
- guint n, gsize offset, gsize size, gboolean writable);
-
typedef struct _GstMetaItem GstMetaItem;
struct _GstMetaItem
GstMetaItem *item;
} GstBufferImpl;
+
+static gboolean
+_is_span (GstMemory ** mem, gsize len, gsize * poffset, GstMemory ** parent)
+{
+ GstMemory *mcur, *mprv;
+ gboolean have_offset = FALSE;
+ gsize i;
+
+ mcur = mprv = NULL;
+
+ for (i = 0; i < len; i++) {
+ if (mcur)
+ mprv = mcur;
+ mcur = mem[i];
+
+ if (mprv && mcur) {
+ gsize poffs;
+
+ /* check if memory is contiguous */
+ if (!gst_memory_is_span (mprv, mcur, &poffs))
+ return FALSE;
+
+ if (!have_offset) {
+ if (poffset)
+ *poffset = poffs;
+ if (parent)
+ *parent = mprv->parent;
+
+ have_offset = TRUE;
+ }
+ }
+ }
+ return have_offset;
+}
+
static GstMemory *
-_span_memory (GstBuffer * buffer, gsize offset, gsize size, gboolean writable)
+_get_merged_memory (GstBuffer * buffer, guint idx, guint length)
{
- GstMemory *span, **mem[1];
- gsize len[1];
+ GstMemory **mem, *result;
- /* not enough room, span buffers */
- mem[0] = GST_BUFFER_MEM_ARRAY (buffer);
- len[0] = GST_BUFFER_MEM_LEN (buffer);
+ mem = GST_BUFFER_MEM_ARRAY (buffer);
+
+ if (G_UNLIKELY (length == 0)) {
+ result = NULL;
+ } else if (G_LIKELY (length == 1)) {
+ result = gst_memory_ref (mem[idx]);
+ } else {
+ GstMemory *parent = NULL;
+ gsize size, poffset = 0;
- if (size == -1)
size = gst_buffer_get_size (buffer);
- span = _gst_buffer_arr_span (mem, len, 1, offset, size, writable);
+ if (G_UNLIKELY (_is_span (mem + idx, length, &poffset, &parent))) {
- return span;
+ if (parent->flags & GST_MEMORY_FLAG_NO_SHARE) {
+ GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy for merge %p", parent);
+ result = gst_memory_copy (parent, poffset, size);
+ } else {
+ result = gst_memory_share (parent, poffset, size);
+ }
+ } else {
+ gsize i, tocopy, left;
+ GstMapInfo sinfo, dinfo;
+ guint8 *ptr;
+
+ result = gst_allocator_alloc (NULL, size, NULL);
+ gst_memory_map (result, &dinfo, GST_MAP_WRITE);
+
+ ptr = dinfo.data;
+ left = size;
+
+ for (i = idx; i < length && left > 0; i++) {
+ gst_memory_map (mem[i], &sinfo, GST_MAP_READ);
+ tocopy = MIN (sinfo.size, left);
+ GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
+ "memcpy %" G_GSIZE_FORMAT " bytes for merge %p from memory %p",
+ tocopy, result, mem[i]);
+ memcpy (ptr, (guint8 *) sinfo.data, tocopy);
+ left -= tocopy;
+ ptr += tocopy;
+ gst_memory_unmap (mem[i], &sinfo);
+ }
+ gst_memory_unmap (result, &dinfo);
+ }
+ }
+ return result;
}
static void
-_replace_memory (GstBuffer * buffer, GstMemory * mem)
+_replace_memory (GstBuffer * buffer, guint len, guint idx, guint length,
+ GstMemory * mem)
{
- gsize len, i;
+ gsize end, i;
- len = GST_BUFFER_MEM_LEN (buffer);
- /* unref old buffers */
- for (i = 0; i < len; i++)
+ end = idx + length;
+ GST_LOG ("buffer %p replace %u-%" G_GSIZE_FORMAT " with memory %p", buffer,
+ idx, end, mem);
+
+ /* unref old memory */
+ for (i = idx; i < end; i++)
gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i));
- /* replace with single spanned buffer */
- GST_BUFFER_MEM_PTR (buffer, 0) = mem;
- GST_BUFFER_MEM_LEN (buffer) = 1;
+ if (mem != NULL) {
+ /* replace with single memory */
+ GST_BUFFER_MEM_PTR (buffer, idx) = mem;
+ idx++;
+ length--;
+ }
+
+ if (end < len) {
+ g_memmove (&GST_BUFFER_MEM_PTR (buffer, idx),
+ &GST_BUFFER_MEM_PTR (buffer, end), (len - end) * sizeof (gpointer));
+ }
+ GST_BUFFER_MEM_LEN (buffer) = len - length;
}
static inline void
/* FIXME, there is room for improvement here: We could only try to merge
* 2 buffers to make some room. If we can't efficiently merge 2 buffers we
* could try to only merge the two smallest buffers to avoid memcpy, etc. */
- _replace_memory (buffer, _span_memory (buffer, 0, -1, FALSE));
+ GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "memory array overflow in buffer %p",
+ buffer);
+ _replace_memory (buffer, len, 0, len, _get_merged_memory (buffer, 0, len));
/* we now have 1 single spanned buffer */
len = 1;
}
*
* Copies the information from @src into @dest.
*
+ * If @dest already contains memory and @flags contains GST_BUFFER_COPY_MEMORY,
+ * the memory from @src will be appended to @dest.
+ *
* @flags indicate which fields will be copied.
*/
void
{
GstMetaItem *walk;
gsize bufsize;
+ gboolean region = FALSE;
g_return_if_fail (dest != NULL);
g_return_if_fail (src != NULL);
bufsize = gst_buffer_get_size (src);
g_return_if_fail (bufsize >= offset);
+ if (offset > 0)
+ region = TRUE;
if (size == -1)
size = bufsize - offset;
+ if (size < bufsize)
+ region = TRUE;
g_return_if_fail (bufsize >= offset + size);
GST_CAT_LOG (GST_CAT_BUFFER, "copy %p to %p, offset %" G_GSIZE_FORMAT
}
}
if (flags & GST_BUFFER_COPY_MERGE) {
- _replace_memory (dest, _span_memory (dest, 0, size, FALSE));
+ len = GST_BUFFER_MEM_LEN (dest);
+ _replace_memory (dest, len, 0, len, _get_merged_memory (dest, 0, len));
}
}
GstMeta *meta = &walk->meta;
const GstMetaInfo *info = meta->info;
- if (info->copy_func)
- info->copy_func (dest, meta, src, offset, size);
+ 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);
+ }
}
}
}
/**
* gst_buffer_new_allocate:
- * @allocator: (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.
- * @align: the alignment of the buffer memory
+ * @params: (transfer none) (allow-none): optional parameters
*
* Tries to create a newly allocated buffer with data of the given size and
- * alignment from @allocator. If the requested amount of memory can't be
+ * 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.
*
* When @allocator is NULL, the default memory allocator will be used.
*
- * Allocator buffer memory will be aligned to multiples of (@align + 1) bytes.
- *
* Note that when @size == 0, the buffer will not have memory associated with it.
*
* MT safe.
* be allocated.
*/
GstBuffer *
-gst_buffer_new_allocate (const GstAllocator * allocator, gsize size,
- gsize align)
+gst_buffer_new_allocate (GstAllocator * allocator, gsize size,
+ GstAllocationParams * params)
{
GstBuffer *newbuf;
GstMemory *mem;
#if 1
if (size > 0) {
- mem = gst_allocator_alloc (allocator, size, align);
+ mem = gst_allocator_alloc (allocator, size, params);
if (G_UNLIKELY (mem == NULL))
goto no_memory;
} else {
/**
* gst_buffer_new_wrapped_full:
+ * @flags: #GstMemoryFlags
* @data: data to wrap
- * @free_func: function to free @data
- * @offset: offset in @data of valid data
- * @size: size of valid data in @data starting at @offset
+ * @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
*
- * Creates a new buffer that wraps the given @data. Valid data is set
- * to start at @offset and up to @size. If no @free_func is provided,
- * buffer memory is marked READONLY.
+ * 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
+ * @size visible.
*
- * MT safe.
+ * When the buffer is destroyed, @notify will be called with @user_data.
+ *
+ * The prefix/padding must be filled with 0 if @flags contains
+ * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
*
* Returns: (transfer full): a new #GstBuffer
*/
GstBuffer *
-gst_buffer_new_wrapped_full (gpointer data, GFreeFunc free_func, gsize offset,
- gsize size)
+gst_buffer_new_wrapped_full (GstMemoryFlags flags, gpointer data,
+ gsize maxsize, gsize offset, gsize size, gpointer user_data,
+ GDestroyNotify notify)
{
GstBuffer *newbuf;
newbuf = gst_buffer_new ();
- gst_buffer_take_memory (newbuf, -1,
- gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
- data, free_func, offset + size, offset, size));
+ gst_buffer_append_memory (newbuf,
+ gst_memory_new_wrapped (flags, data, maxsize, offset, size,
+ user_data, notify));
return newbuf;
}
GstBuffer *
gst_buffer_new_wrapped (gpointer data, gsize size)
{
- return gst_buffer_new_wrapped_full (data, g_free, 0, size);
+ return gst_buffer_new_wrapped_full (0, data, size, 0, size, data, g_free);
}
/**
}
/**
- * gst_buffer_take_memory:
+ * gst_buffer_insert_memory:
* @buffer: a #GstBuffer.
* @idx: the index to add the memory at, or -1 to append it to the end
* @mem: (transfer full): a #GstMemory.
*
- * Add the memory block @mem to @buffer at @idx. This function takes ownership
+ * Insert the memory block @mem to @buffer at @idx. This function takes ownership
* of @mem and thus doesn't increase its refcount.
*/
void
-gst_buffer_take_memory (GstBuffer * buffer, gint idx, GstMemory * mem)
+gst_buffer_insert_memory (GstBuffer * buffer, gint idx, GstMemory * mem)
{
g_return_if_fail (GST_IS_BUFFER (buffer));
g_return_if_fail (gst_buffer_is_writable (buffer));
}
static GstMemory *
-_get_memory (GstBuffer * buffer, guint idx, gboolean write)
+_get_mapped (GstBuffer * buffer, guint idx, GstMapInfo * info,
+ GstMapFlags flags)
{
- GstMemory *mem;
+ GstMemory *mem, *mapped;
mem = GST_BUFFER_MEM_PTR (buffer, idx);
- if (G_UNLIKELY (write && !gst_memory_is_writable (mem))) {
- GstMemory *copy;
- GST_CAT_LOG (GST_CAT_BUFFER,
- "making writable copy of memory %p in buffer %p", mem, buffer);
- /* replace with a writable copy */
- copy = gst_memory_copy (mem, 0, -1);
- GST_BUFFER_MEM_PTR (buffer, idx) = copy;
+ mapped = gst_memory_make_mapped (mem, info, flags);
+ if (!mapped)
+ return NULL;
+
+ if (mapped != mem) {
+ GST_BUFFER_MEM_PTR (buffer, idx) = mapped;
gst_memory_unref (mem);
- mem = copy;
+ mem = mapped;
}
return mem;
}
* gst_buffer_peek_memory:
* @buffer: a #GstBuffer.
* @idx: an index
- * @flags: #GstMapFlags
*
- * Get the memory block in @buffer at @idx for memory access in @flags.
- * This function does not return a refcount to the memory block. The memory
- * block stays valid for as long as the caller has a valid reference to @buffer
- * and as long as no operations that modify the memory blocks are called, such
- * as gst_buffer_remove_memory_range(), gst_buffer_take_memory() and gst_buffer_resize().
+ * Get the memory block at @idx in @buffer. The memory block stays valid until
+ * the memory block in @buffer is removed, replaced or merged, typically with
+ * any call that modifies the memory in @buffer.
*
- * @buffer should be writable when @flags contains #GST_MAP_WRITE. If the memory
- * at @idx is not writable, a new writable copy will be installed in @buffer and
- * returned.
+ * Since this call does not influence the refcount of the memory,
+ * gst_memory_is_exclusive() can be used to check if @buffer is the sole owner
+ * of the returned memory.
*
- * Returns: a #GstMemory at @idx.
+ * Returns: (transfer none): the #GstMemory at @idx.
*/
GstMemory *
-gst_buffer_peek_memory (GstBuffer * buffer, guint idx, GstMapFlags flags)
+gst_buffer_peek_memory (GstBuffer * buffer, guint idx)
{
- GstMemory *mem;
- gboolean write;
+ guint len;
- write = (flags & GST_MAP_WRITE) != 0;
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+ len = GST_BUFFER_MEM_LEN (buffer);
+ g_return_val_if_fail (idx < len, NULL);
+
+ return GST_BUFFER_MEM_PTR (buffer, idx);
+}
+
+/**
+ * gst_buffer_get_memory_range:
+ * @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length
+ *
+ * Get @length memory blocks in @buffer starting at @idx. The memory blocks will
+ * be merged into one large #GstMemory.
+ *
+ * If @length is -1, all memory starting from @idx is merged.
+ *
+ * Returns: (transfer full): a #GstMemory that contains the merged data of @length
+ * blocks starting at @idx. Use gst_memory_unref () after usage.
+ */
+GstMemory *
+gst_buffer_get_memory_range (GstBuffer * buffer, guint idx, gint length)
+{
+ guint len;
+
+ GST_DEBUG ("idx %u, length %d", idx, length);
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
- g_return_val_if_fail (idx < GST_BUFFER_MEM_LEN (buffer), NULL);
+ 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), NULL);
- /* check if we can write when asked for write access */
- if (G_UNLIKELY (write && !gst_buffer_is_writable (buffer)))
- goto not_writable;
+ if (length == -1)
+ length = len - idx;
- mem = _get_memory (buffer, idx, write);
+ return _get_merged_memory (buffer, idx, length);
+}
- return mem;
+/**
+ * gst_buffer_replace_memory_range:
+ * @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length should not be 0
+ * @mem: (transfer full): a #GstMemory
+ *
+ * Replaces @length memory blocks in @buffer starting at @idx with @mem.
+ *
+ * If @length is -1, all memory starting from @idx will be removed and
+ * replaced with @mem.
+ *
+ * @buffer should be writable.
+ */
+void
+gst_buffer_replace_memory_range (GstBuffer * buffer, guint idx, gint length,
+ GstMemory * mem)
+{
+ guint len;
- /* ERRORS */
-not_writable:
- {
- g_return_val_if_fail (gst_buffer_is_writable (buffer), NULL);
- return NULL;
- }
+ g_return_if_fail (GST_IS_BUFFER (buffer));
+ g_return_if_fail (gst_buffer_is_writable (buffer));
+ len = GST_BUFFER_MEM_LEN (buffer);
+ g_return_if_fail ((len == 0 && idx == 0 && length == -1) ||
+ (length == -1 && idx < len) || (length > 0 && length + idx <= len));
+
+ if (length == -1)
+ length = len - idx;
+
+ _replace_memory (buffer, len, idx, length, mem);
}
/**
* @idx: an index
* @length: a length
*
- * Remove @len memory blocks in @buffer starting from @idx.
+ * Remove @length memory blocks in @buffer starting from @idx.
*
* @length can be -1, in which case all memory starting from @idx is removed.
*/
void
-gst_buffer_remove_memory_range (GstBuffer * buffer, guint idx, guint length)
+gst_buffer_remove_memory_range (GstBuffer * buffer, guint idx, gint length)
{
- guint len, i, end;
+ guint len;
g_return_if_fail (GST_IS_BUFFER (buffer));
g_return_if_fail (gst_buffer_is_writable (buffer));
len = GST_BUFFER_MEM_LEN (buffer);
- if (length == -1) {
- g_return_if_fail (idx < len);
- length = len - idx;
- }
+ g_return_if_fail ((len == 0 && idx == 0 && length == -1) ||
+ (length == -1 && idx < len) || length + idx <= len);
- end = idx + length;
- for (i = idx; i < end; i++)
- gst_memory_unref (GST_BUFFER_MEM_PTR (buffer, i));
+ if (length == -1)
+ length = len - idx;
- if (end != len) {
- g_memmove (&GST_BUFFER_MEM_PTR (buffer, idx),
- &GST_BUFFER_MEM_PTR (buffer, end), (len - end) * sizeof (gpointer));
- }
- GST_BUFFER_MEM_LEN (buffer) = len - length;
+ _replace_memory (buffer, len, idx, length, NULL);
}
/**
- * gst_buffer_get_merged_memory:
+ * gst_buffer_find_memory:
* @buffer: a #GstBuffer.
+ * @offset: an offset
+ * @size: a size
+ * @idx: (out): pointer to index
+ * @length: (out): pointer to length
+ * @skip: (out): pointer to skip
*
- * Return a #GstMemory object that contains all the memory in @buffer. If there
- * was only one memory in @buffer, it will be returned directly, otherwise all
- * memory objects will be merged into one object that will be returned.
+ * Find the memory blocks that span @size bytes starting from @offset
+ * in @buffer.
*
- * Returns: a #GstMemory with the merged memory in @buffer. This function can
- * return %NULL if there is no memory in @buffer. Use gst_memory_unref() after
- * usage.
+ * 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
+ * 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
+ * for @offset.
+ *
+ * @size can be -1 to get all the memory blocks after @idx.
+ *
+ * Returns: %TRUE when @size bytes starting from @offset could be found in
+ * @buffer and @idx, @length and @skip will be filled.
*/
-static GstMemory *
-gst_buffer_get_merged_memory (GstBuffer * buffer)
+gboolean
+gst_buffer_find_memory (GstBuffer * buffer, gsize offset, gsize size,
+ guint * idx, guint * length, gsize * skip)
{
- guint len;
- GstMemory *mem;
+ guint i, len, found;
- g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (idx != NULL, FALSE);
+ g_return_val_if_fail (length != NULL, FALSE);
+ g_return_val_if_fail (skip != NULL, FALSE);
len = GST_BUFFER_MEM_LEN (buffer);
- if (G_UNLIKELY (len == 0)) {
- /* no memory */
- mem = NULL;
- } else if (G_LIKELY (len == 1)) {
- /* we can take the first one */
- mem = GST_BUFFER_MEM_PTR (buffer, 0);
- gst_memory_ref (mem);
- } else {
- /* we need to span memory */
- mem = _span_memory (buffer, 0, -1, FALSE);
+ found = 0;
+ for (i = 0; i < len; i++) {
+ GstMemory *mem;
+ gsize s;
+
+ mem = GST_BUFFER_MEM_PTR (buffer, i);
+ s = gst_memory_get_sizes (mem, NULL, NULL);
+
+ if (s <= offset) {
+ /* block before offset, or empty block, skip */
+ offset -= s;
+ } else {
+ /* block after offset */
+ if (found == 0) {
+ /* first block, remember index and offset */
+ *idx = i;
+ *skip = offset;
+ if (size == -1) {
+ /* return remaining blocks */
+ *length = len - i;
+ return TRUE;
+ }
+ s -= offset;
+ offset = 0;
+ }
+ /* count the amount of found bytes */
+ found += s;
+ if (found >= size) {
+ /* we have enough bytes */
+ *length = i - *idx + 1;
+ return TRUE;
+ }
+ }
}
- return mem;
+ return FALSE;
}
/**
- * gst_buffer_get_sizes:
+ * gst_buffer_get_sizes_range:
* @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length
* @offset: a pointer to the offset
* @maxsize: a pointer to the maxsize
*
- * Get the total size of all memory blocks in @buffer.
+ * Get the total size of @length memory blocks stating from @idx in @buffer.
*
- * When not %NULL, @offset will contain the offset of the data in the first
- * memory block in @buffer and @maxsize will contain the sum of the size
- * and @offset and the amount of extra padding on the last memory block.
- * @offset and @maxsize can be used to resize the buffer with
- * gst_buffer_resize().
+ * When not %NULL, @offset will contain the offset of the data in the
+ * memory block in @buffer at @idx and @maxsize will contain the sum of the size
+ * and @offset and the amount of extra padding on the memory block at @idx +
+ * @length -1.
+ * @offset and @maxsize can be used to resize the buffer memory blocks with
+ * gst_buffer_resize_range().
*
- * Returns: the total size of the memory in @buffer.
+ * Returns: total size @length memory blocks starting at @idx in @buffer.
*/
gsize
-gst_buffer_get_sizes (GstBuffer * buffer, gsize * offset, gsize * maxsize)
+gst_buffer_get_sizes_range (GstBuffer * buffer, guint idx, gint length,
+ gsize * offset, gsize * maxsize)
{
guint len;
gsize size;
GstMemory *mem;
g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
-
len = GST_BUFFER_MEM_LEN (buffer);
+ g_return_val_if_fail ((len == 0 && idx == 0 && length == -1) ||
+ (length == -1 && idx < len) || (length + idx <= len), 0);
+
+ if (length == -1)
+ length = len - idx;
- if (G_LIKELY (len == 1)) {
+ if (G_LIKELY (length == 1)) {
/* common case */
- mem = GST_BUFFER_MEM_PTR (buffer, 0);
+ mem = GST_BUFFER_MEM_PTR (buffer, idx);
size = gst_memory_get_sizes (mem, offset, maxsize);
} else {
- guint i;
+ guint i, end;
gsize extra, offs;
+ end = idx + length;
size = offs = extra = 0;
- for (i = 0; i < len; i++) {
+ for (i = idx; i < end; i++) {
gsize s, o, ms;
mem = GST_BUFFER_MEM_PTR (buffer, i);
}
/**
- * gst_buffer_resize:
+ * gst_buffer_resize_range:
* @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length
* @offset: the offset adjustement
* @size: the new size or -1 to just adjust the offset
*
- * Set the total size of the buffer
+ * Set the total size of the @length memory blocks starting at @idx in
+ * @buffer
*/
void
-gst_buffer_resize (GstBuffer * buffer, gssize offset, gssize size)
+gst_buffer_resize_range (GstBuffer * buffer, guint idx, gint length,
+ gssize offset, gssize size)
{
- guint len;
- guint i;
+ guint i, len, end;
gsize bsize, bufsize, bufoffs, bufmax;
GstMemory *mem;
g_return_if_fail (gst_buffer_is_writable (buffer));
g_return_if_fail (size >= -1);
+ len = GST_BUFFER_MEM_LEN (buffer);
+ g_return_if_fail ((len == 0 && idx == 0 && length == -1) ||
+ (length == -1 && idx < len) || (length + idx <= len));
- bufsize = gst_buffer_get_sizes (buffer, &bufoffs, &bufmax);
+ if (length == -1)
+ length = len - idx;
+
+ bufsize = gst_buffer_get_sizes_range (buffer, idx, length, &bufoffs, &bufmax);
GST_CAT_LOG (GST_CAT_BUFFER, "trim %p %" G_GSSIZE_FORMAT "-%" G_GSSIZE_FORMAT
" size:%" G_GSIZE_FORMAT " offs:%" G_GSIZE_FORMAT " max:%"
}
g_return_if_fail (bufmax >= bufoffs + offset + size);
- len = GST_BUFFER_MEM_LEN (buffer);
+ /* no change */
+ if (offset == 0 && size == bufsize)
+ return;
+ end = idx + length;
/* copy and trim */
- for (i = 0; i < len; i++) {
+ for (i = idx; i < end; i++) {
gsize left, noffs;
mem = GST_BUFFER_MEM_PTR (buffer, i);
noffs = 0;
/* last buffer always gets resized to the remaining size */
- if (i + 1 == len)
+ if (i + 1 == end)
left = size;
/* shrink buffers before the offset */
else if ((gssize) bsize <= offset) {
left = MIN (bsize - offset, size);
if (offset != 0 || left != bsize) {
- /* we need to clip something */
- if (gst_memory_is_writable (mem)) {
+ if (gst_memory_is_exclusive (mem)) {
gst_memory_resize (mem, offset, left);
} else {
GstMemory *tmp;
}
/**
- * gst_buffer_map:
+ * gst_buffer_map_range:
* @buffer: a #GstBuffer.
+ * @idx: an index
+ * @length: a length
* @info: (out): info about the mapping
* @flags: flags for the mapping
*
- * This function fills @info with a pointer to the merged memory in @buffer.
+ * This function fills @info with the #GstMapInfo of @length merged memory blocks
+ * starting at @idx in @buffer. When @length is -1, all memory blocks starting
+ * from @idx are merged and mapped.
* @flags describe the desired access of the memory. When @flags is
* #GST_MAP_WRITE, @buffer should be writable (as returned from
* gst_buffer_is_writable()).
* automatically be created and returned. The readonly copy of the buffer memory
* will then also be replaced with this writable copy.
*
- * When the buffer contains multiple memory blocks, the returned pointer will be
- * a concatenation of the memory blocks.
+ * The memory in @info should be unmapped with gst_buffer_unmap() after usage.
*
* Returns: (transfer full): %TRUE if the map succeeded and @info contains valid
* data.
*/
gboolean
-gst_buffer_map (GstBuffer * buffer, GstMapInfo * info, GstMapFlags flags)
+gst_buffer_map_range (GstBuffer * buffer, guint idx, gint length,
+ GstMapInfo * info, GstMapFlags flags)
{
- GstMemory *mem;
+ GstMemory *mem, *nmem;
gboolean write, writable;
+ gsize len;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (info != NULL, FALSE);
+ 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);
write = (flags & GST_MAP_WRITE) != 0;
writable = gst_buffer_is_writable (buffer);
if (G_UNLIKELY (write && !writable))
goto not_writable;
- mem = gst_buffer_get_merged_memory (buffer);
- if (mem == NULL)
+ if (length == -1)
+ length = len - idx;
+
+ mem = _get_merged_memory (buffer, idx, length);
+ if (G_UNLIKELY (mem == NULL))
goto no_memory;
/* now try to map */
- if (!gst_memory_map (mem, info, flags) && write) {
- GstMemory *copy;
- /* make a (writable) copy */
- copy = gst_memory_copy (mem, 0, -1);
- gst_memory_unref (mem);
- mem = copy;
- if (G_UNLIKELY (mem == NULL))
- goto cannot_map;
-
- /* try again */
- if (!gst_memory_map (mem, info, flags))
- goto cannot_map;
+ nmem = gst_memory_make_mapped (mem, info, flags);
+ if (G_UNLIKELY (nmem == NULL))
+ goto cannot_map;
+
+ /* if we merged or when the map returned a different memory, we try to replace
+ * the memory in the buffer */
+ if (G_UNLIKELY (length > 1 || nmem != mem)) {
+ /* if the buffer is writable, replace the memory */
+ if (writable) {
+ _replace_memory (buffer, len, idx, length, gst_memory_ref (nmem));
+ } else {
+ if (len > 1) {
+ GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
+ "temporary mapping for memory %p in buffer %p", nmem, buffer);
+ }
+ }
}
-
- /* if the buffer is writable, replace the memory */
- if (writable)
- _replace_memory (buffer, gst_memory_ref (mem));
-
return TRUE;
/* ERROR */
not_writable:
{
+ GST_WARNING_OBJECT (buffer, "write map requested on non-writable buffer");
g_critical ("write map requested on non-writable buffer");
return FALSE;
}
cannot_map:
{
GST_DEBUG_OBJECT (buffer, "cannot map memory");
- gst_memory_unref (mem);
return FALSE;
}
}
g_return_if_fail (GST_IS_BUFFER (buffer));
g_return_if_fail (info != NULL);
- if (info->memory) {
+ /* we need to check for NULL, it is possible that we tried to map a buffer
+ * without memory and we should be able to unmap that fine */
+ if (G_LIKELY (info->memory)) {
gst_memory_unmap (info->memory, info);
gst_memory_unref (info->memory);
}
gsize tocopy;
GstMemory *mem;
- mem = _get_memory (buffer, i, TRUE);
-
- gst_memory_map (mem, &info, GST_MAP_WRITE);
+ mem = _get_mapped (buffer, i, &info, GST_MAP_WRITE);
if (info.size > offset) {
/* we have enough */
tocopy = MIN (info.size - offset, left);
gsize tocopy;
GstMemory *mem;
- mem = GST_BUFFER_MEM_PTR (buffer, i);
-
- gst_memory_map (mem, &info, GST_MAP_READ);
+ mem = _get_mapped (buffer, i, &info, GST_MAP_READ);
if (info.size > offset) {
/* we have enough */
tocopy = MIN (info.size - offset, left);
gsize tocmp;
GstMemory *mem;
- mem = GST_BUFFER_MEM_PTR (buffer, i);
-
- gst_memory_map (mem, &info, GST_MAP_READ);
+ mem = _get_mapped (buffer, i, &info, GST_MAP_READ);
if (info.size > offset) {
/* we have enough */
tocmp = MIN (info.size - offset, size);
gsize toset;
GstMemory *mem;
- mem = _get_memory (buffer, i, TRUE);
-
- gst_memory_map (mem, &info, GST_MAP_WRITE);
+ mem = _get_mapped (buffer, i, &info, GST_MAP_WRITE);
if (info.size > offset) {
/* we have enough */
toset = MIN (info.size - offset, left);
return copy;
}
-static gboolean
-_gst_buffer_arr_is_span_fast (GstMemory ** mem[], gsize len[], guint n,
- gsize * offset, GstMemory ** parent)
-{
- GstMemory *mcur, *mprv;
- gboolean have_offset = FALSE;
- guint count, i;
-
- mcur = mprv = NULL;
- for (count = 0; count < n; count++) {
- gsize offs, clen;
- GstMemory **cmem;
-
- cmem = mem[count];
- clen = len[count];
-
- for (i = 0; i < clen; i++) {
- if (mcur)
- mprv = mcur;
- mcur = cmem[i];
-
- if (mprv && mcur) {
- /* check is memory is contiguous */
- if (!gst_memory_is_span (mprv, mcur, &offs))
- return FALSE;
-
- if (!have_offset) {
- if (offset)
- *offset = offs;
- if (parent)
- *parent = mprv->parent;
-
- have_offset = TRUE;
- }
- }
- }
- }
- return have_offset;
-}
-
-static GstMemory *
-_gst_buffer_arr_span (GstMemory ** mem[], gsize len[], guint n, gsize offset,
- gsize size, gboolean writable)
-{
- GstMemory *span, *parent = NULL;
- gsize poffset = 0;
-
- if (!writable
- && _gst_buffer_arr_is_span_fast (mem, len, n, &poffset, &parent)) {
- if (parent->flags & GST_MEMORY_FLAG_NO_SHARE)
- span = gst_memory_copy (parent, offset + poffset, size);
- else
- span = gst_memory_share (parent, offset + poffset, size);
- } else {
- gsize count, left;
- GstMapInfo dinfo;
- guint8 *ptr;
-
- span = gst_allocator_alloc (NULL, size, 0);
- gst_memory_map (span, &dinfo, GST_MAP_WRITE);
-
- ptr = dinfo.data;
- left = size;
-
- for (count = 0; count < n; count++) {
- GstMapInfo sinfo;
- gsize i, tocopy, clen;
- GstMemory **cmem;
-
- cmem = mem[count];
- clen = len[count];
-
- for (i = 0; i < clen && left > 0; i++) {
- gst_memory_map (cmem[i], &sinfo, GST_MAP_READ);
- tocopy = MIN (sinfo.size, left);
- if (tocopy > offset) {
- memcpy (ptr, (guint8 *) sinfo.data + offset, tocopy - offset);
- left -= tocopy;
- ptr += tocopy;
- offset = 0;
- } else {
- offset -= tocopy;
- }
- gst_memory_unmap (cmem[i], &sinfo);
- }
- }
- gst_memory_unmap (span, &dinfo);
- }
- return span;
-}
-
-/**
- * gst_buffer_is_span_fast:
- * @buf1: the first #GstBuffer.
- * @buf2: the second #GstBuffer.
- *
- * Determines whether a gst_buffer_span() can be done without copying
- * the contents, that is, whether the data areas are contiguous sub-buffers of
- * the same buffer.
- *
- * MT safe.
- * Returns: TRUE if the buffers are contiguous,
- * FALSE if a copy would be required.
- */
-gboolean
-gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2)
-{
- GstMemory **mem[2];
- gsize len[2];
-
- g_return_val_if_fail (GST_IS_BUFFER (buf1), FALSE);
- g_return_val_if_fail (GST_IS_BUFFER (buf2), FALSE);
- g_return_val_if_fail (buf1->mini_object.refcount > 0, FALSE);
- g_return_val_if_fail (buf2->mini_object.refcount > 0, FALSE);
-
- mem[0] = GST_BUFFER_MEM_ARRAY (buf1);
- len[0] = GST_BUFFER_MEM_LEN (buf1);
- mem[1] = GST_BUFFER_MEM_ARRAY (buf2);
- len[1] = GST_BUFFER_MEM_LEN (buf2);
-
- return _gst_buffer_arr_is_span_fast (mem, len, 2, NULL, NULL);
-}
-
/**
- * gst_buffer_span:
- * @buf1: the first source #GstBuffer to merge.
- * @offset: the offset in the first buffer from where the new
- * buffer should start.
- * @buf2: the second source #GstBuffer to merge.
- * @size: the total size of the new buffer.
- *
- * Creates a new buffer that consists of part of buf1 and buf2.
- * Logically, buf1 and buf2 are concatenated into a single larger
- * buffer, and a new buffer is created at the given offset inside
- * this space, with a given length.
- *
- * If the two source buffers are children of the same larger buffer,
- * and are contiguous, the new buffer will be a child of the shared
- * parent, and thus no copying is necessary. you can use
- * gst_buffer_is_span_fast() to determine if a memcpy will be needed.
+ * gst_buffer_append:
+ * @buf1: (transfer full): the first source #GstBuffer to append.
+ * @buf2: (transfer full): the second source #GstBuffer to append.
*
- * MT safe.
+ * Append all the memory from @buf2 to @buf1. The result buffer will contain a
+ * concatenation of the memory of @buf1 and @buf2.
*
- * Returns: (transfer full): the new #GstBuffer that spans the two source
- * buffers, or NULL if the arguments are invalid.
+ * Returns: (transfer full): the new #GstBuffer that contains the memory
+ * of the two source buffers.
*/
GstBuffer *
-gst_buffer_span (GstBuffer * buf1, gsize offset, GstBuffer * buf2, gsize size)
+gst_buffer_append (GstBuffer * buf1, GstBuffer * buf2)
{
- GstBuffer *newbuf;
- GstMemory *span;
- GstMemory **mem[2];
- gsize len[2], len1, len2;
+ gsize i, len;
g_return_val_if_fail (GST_IS_BUFFER (buf1), NULL);
g_return_val_if_fail (GST_IS_BUFFER (buf2), NULL);
- g_return_val_if_fail (buf1->mini_object.refcount > 0, NULL);
- g_return_val_if_fail (buf2->mini_object.refcount > 0, NULL);
- len1 = gst_buffer_get_size (buf1);
- len2 = gst_buffer_get_size (buf2);
- g_return_val_if_fail (len1 + len2 > offset, NULL);
- if (size == -1)
- size = len1 + len2 - offset;
- else
- g_return_val_if_fail (size <= len1 + len2 - offset, NULL);
- mem[0] = GST_BUFFER_MEM_ARRAY (buf1);
- len[0] = GST_BUFFER_MEM_LEN (buf1);
- mem[1] = GST_BUFFER_MEM_ARRAY (buf2);
- len[1] = GST_BUFFER_MEM_LEN (buf2);
+ buf1 = gst_buffer_make_writable (buf1);
+ buf2 = gst_buffer_make_writable (buf2);
- span = _gst_buffer_arr_span (mem, len, 2, offset, size, FALSE);
+ len = GST_BUFFER_MEM_LEN (buf2);
+ for (i = 0; i < len; i++) {
+ GstMemory *mem;
- newbuf = gst_buffer_new ();
- _memory_add (newbuf, -1, span);
+ mem = GST_BUFFER_MEM_PTR (buf2, i);
+ GST_BUFFER_MEM_PTR (buf2, i) = NULL;
+ _memory_add (buf1, -1, mem);
+ }
-#if 0
- /* if the offset is 0, the new buffer has the same timestamp as buf1 */
- if (offset == 0) {
- GST_BUFFER_OFFSET (newbuf) = GST_BUFFER_OFFSET (buf1);
- GST_BUFFER_PTS (newbuf) = GST_BUFFER_PTS (buf1);
- GST_BUFFER_DTS (newbuf) = GST_BUFFER_DTS (buf1);
-
- /* if we completely merged the two buffers (appended), we can
- * calculate the duration too. Also make sure we's not messing with
- * invalid DURATIONS */
- if (buf1->size + buf2->size == len) {
- if (GST_BUFFER_DURATION_IS_VALID (buf1) &&
- GST_BUFFER_DURATION_IS_VALID (buf2)) {
- /* add duration */
- GST_BUFFER_DURATION (newbuf) = GST_BUFFER_DURATION (buf1) +
- GST_BUFFER_DURATION (buf2);
- }
- if (GST_BUFFER_OFFSET_END_IS_VALID (buf2)) {
- /* add offset_end */
- GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_END (buf2);
- }
- }
+ /* we can calculate the duration too. Also make sure we're not messing
+ * with invalid DURATIONS */
+ if (GST_BUFFER_DURATION_IS_VALID (buf1) &&
+ GST_BUFFER_DURATION_IS_VALID (buf2)) {
+ /* add duration */
+ GST_BUFFER_DURATION (buf1) += GST_BUFFER_DURATION (buf2);
+ }
+ if (GST_BUFFER_OFFSET_END_IS_VALID (buf2)) {
+ /* set offset_end */
+ GST_BUFFER_OFFSET_END (buf1) = GST_BUFFER_OFFSET_END (buf2);
}
-#endif
- return newbuf;
+ GST_BUFFER_MEM_LEN (buf2) = 0;
+ gst_buffer_unref (buf2);
+
+ return buf1;
}
/**
* gst_buffer_get_meta:
* @buffer: a #GstBuffer
- * @info: a #GstMetaInfo
+ * @api: the #GType of an API
*
- * Get the metadata for the api in @info on buffer. When there is no such
+ * Get the metadata for @api on buffer. When there is no such
* metadata, NULL is returned.
*
- * Note that the result metadata might not be of the implementation @info.
- *
- * Returns: the metadata for the api in @info on @buffer.
+ * Returns: the metadata for @api on @buffer.
*/
GstMeta *
-gst_buffer_get_meta (GstBuffer * buffer, const GstMetaInfo * info)
+gst_buffer_get_meta (GstBuffer * buffer, GType api)
{
GstMetaItem *item;
GstMeta *result = NULL;
g_return_val_if_fail (buffer != NULL, NULL);
- g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (api != 0, NULL);
/* find GstMeta of the requested API */
for (item = GST_BUFFER_META (buffer); item; item = item->next) {
GstMeta *meta = &item->meta;
- if (meta->info->api == info->api) {
+ if (meta->info->api == api) {
result = meta;
break;
}
g_return_val_if_fail (buffer != NULL, NULL);
g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), NULL);
/* create a new slice */
size = ITEM_SIZE (info);
g_return_val_if_fail (buffer != NULL, FALSE);
g_return_val_if_fail (meta != NULL, FALSE);
+ g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
+ g_return_val_if_fail (!GST_META_FLAG_IS_SET (meta, GST_META_FLAG_LOCKED),
+ FALSE);
/* find the metadata and delete */
prev = GST_BUFFER_META (buffer);
GST_CAT_DEBUG (GST_CAT_BUFFER, "remove metadata %p (%s)", m,
g_type_name (info->type));
+ g_return_if_fail (gst_buffer_is_writable (buffer));
+ g_return_if_fail (!GST_META_FLAG_IS_SET (m, GST_META_FLAG_LOCKED));
+
/* remove from list */
if (GST_BUFFER_META (buffer) == walk)
GST_BUFFER_META (buffer) = next;