/* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * * gstbuffer.c: Buffer operations * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* this file makes too much noise for most debugging sessions */ #define GST_DEBUG_FORCE_DISABLE #include "gst_private.h" #include "gstbuffer.h" #include "gstobject.h" static GMemChunk *_buffer_chunk = NULL; void _gst_buffer_initialize (void) { gint buffersize = sizeof (GstBuffer); if (_buffer_chunk == NULL) { _buffer_chunk = g_mem_chunk_new ("GstBufferChunk", buffersize, buffersize * 128, G_ALLOC_AND_FREE); GST_INFO (GST_CAT_BUFFER, "Buffers are initialized now"); } } /** * gst_buffer_alloc: * * Allocate space for a new buffer. Works the same way * as g_malloc. * Buffers allocated with gst_buffer_alloc must be freed * with gst_buffer_free. * * Returns: A new buffer or NULL on failure */ GstBuffer * gst_buffer_alloc (void) { return g_mem_chunk_alloc (_buffer_chunk); } /** * gst_buffer_free: * @buffer: The buffer to free. * * Frees a buffer that wa previously allocated with gst_buffer_alloc. */ void gst_buffer_free (GstBuffer *buffer) { g_mem_chunk_free (_buffer_chunk, buffer); } /** * gst_buffer_init: * @buffer: The buffer to be initialized * @pool: The bufferpool to initialize from * * Initializes a buffer from a given bufferpool. * This function is a convenience function to be used in buffer_new * routines of bufferpools. */ void gst_buffer_init (GstBuffer *buffer, GstBufferPool *pool) { GST_DEBUG (GST_CAT_BUFFER, "initializing new buffer %p", buffer); gst_data_ref (GST_DATA (pool)); gst_data_init (GST_DATA (buffer)); GST_DATA (buffer)->type = GST_DATA_BUFFER; GST_DATA (buffer)->dispose = pool->buffer_dispose; GST_DATA (buffer)->free = pool->buffer_free; buffer->data = NULL; buffer->size = 0; buffer->parent = NULL; buffer->pool = pool; buffer->pool_private = NULL; } /** * gst_buffer_dispose: * @buffer: The buffer to be disposed * * Disposes a buffer. * This function is a convenience function to be used in buffer_dispose * routines of bufferpools. */ void gst_buffer_dispose (GstData *buffer) { /* unreference the parent if there is one */ gst_buffer_unparent (GST_BUFFER (buffer)); gst_data_unref (GST_DATA (GST_BUFFER (buffer)->pool)); gst_data_dispose (buffer); } /** * gst_buffer_new: * @pool: bufferpool to create the buffer from * @size: size of the data, 0 if no data initialization * * Create a new buffer from the given bufferpool. If the bufferpool * is set to NULL, the default pool is used. * * Returns: new buffer or NULL, if buffer couldn't be created. */ GstBuffer* gst_buffer_new (GstBufferPool *pool, guint size) { if (pool == NULL) { GstBufferPool *def = gst_buffer_pool_default (); return def->buffer_new (def, size); } else { return pool->buffer_new (pool, size); } } /** * gst_buffer_set_parent: * @buffer: buffer to set the parent for * @parent: parent to set * * Sets the buffers parent to the given one. */ void gst_buffer_set_parent (GstBuffer *buffer, GstBuffer *parent, guint offset, guint size) { /* make sure we get valid data */ g_return_if_fail (buffer != NULL); g_return_if_fail (parent != NULL); g_return_if_fail (buffer != parent); /* make sure we can get the data from the parent */ g_return_if_fail (parent->size >= offset + size); /* do not reset the parent */ if (buffer->parent == parent) return; /* if we have a parent, remove it */ if (buffer->parent != NULL) gst_buffer_unparent (buffer); /* make sure buffer contains no data */ g_return_if_fail (buffer->data == NULL); g_return_if_fail (buffer->size == 0); gst_data_ref (GST_DATA (parent)); buffer->data = parent->data + offset; buffer->size = size; /* make sure we're child from a root buffer */ while (parent->parent) { parent = parent->parent; } buffer->parent = parent; /* make sure nobody overwrites data in two buffers */ GST_DATA_FLAG_SET(buffer, GST_DATA_READONLY); if (GST_DATA_IS_WRITABLE (parent)) GST_DATA_FLAG_SET(parent, GST_DATA_READONLY); } /** * gst_buffer_unset_parent: * @buffer: buffer to unset the parent for * * Unsets the buffers parent and clears the data. * If the buffer has no parent, it simply returns. */ void gst_buffer_unparent (GstBuffer *buffer) { g_return_if_fail (buffer != NULL); if (buffer->parent == NULL) return; gst_data_unref (GST_DATA (buffer->parent)); buffer->parent = NULL; buffer->data = NULL; buffer->size = 0; } /** * gst_buffer_create_sub: * @parent: parent buffer * @offset: offset into parent buffer * @size: size of new subbuffer * * Creates a sub-buffer from the parent at a given offset. * * Returns: new buffer */ GstBuffer* gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size) { GstBuffer *buffer; g_return_val_if_fail (parent != NULL, NULL); g_return_val_if_fail (GST_BUFFER_REFCOUNT(parent) > 0, NULL); g_return_val_if_fail (size > 0, NULL); buffer = gst_buffer_new (parent->pool, 0); gst_buffer_set_parent (buffer, parent, offset, size); return buffer; } /** * gst_buffer_append: * @buffer: a buffer * @append: the buffer to append * * Creates a new buffer by appending the data of append to the * existing data of buffer. * * Returns: new buffer */ GstBuffer* gst_buffer_append (GstBuffer *buffer, GstBuffer *append) { GstBuffer *newbuf; g_return_val_if_fail (buffer != NULL, NULL); g_return_val_if_fail (append != NULL, NULL); g_return_val_if_fail (buffer->pool == NULL, NULL); g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL); g_return_val_if_fail (GST_BUFFER_REFCOUNT(append) > 0, NULL); GST_INFO (GST_CAT_BUFFER,"appending buffers %p and %p",buffer,append); newbuf = gst_buffer_merge (buffer, append); gst_data_unref (GST_DATA (buffer)); return newbuf; } /** * gst_buffer_copy: * @buffer: the orignal GstBuffer to make a copy of * * Make a full copy of the give buffer, data and all. * * Returns: new buffer */ GstBuffer * gst_buffer_copy (const GstBuffer *buffer) { g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL); return buffer->pool->buffer_copy (buffer); } /** * gst_buffer_is_span_fast: * @buf1: first source buffer * @buf2: second source buffer * * Determines whether a gst_buffer_span is free, or requires a memcpy. * * Returns: TRUE if the buffers are contiguous, FALSE if a copy would be required. */ gboolean gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2) { g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, FALSE); g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, FALSE); return (buf1->parent && buf2->parent && (buf1->parent == buf2->parent) && ((buf1->data + buf1->size) == buf2->data)); } /** * gst_buffer_span: * @buf1: first source buffer to merge * @offset: offset in first buffer to start new buffer * @buf2: second source buffer to merge * @len: length of new buffer * * Create 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. * * Returns: new buffer that spans the two source buffers */ /* FIXME need to think about CoW and such... */ GstBuffer * gst_buffer_span (GstBuffer *buf1, guint offset, GstBuffer *buf2, guint len) { GstBuffer *newbuf; g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, NULL); g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, NULL); /* if the two buffers have the same parent and are adjacent */ if (gst_buffer_is_span_fast(buf1,buf2)) { /* we simply create a subbuffer of the common parent */ newbuf = gst_buffer_create_sub (buf1->parent, buf1->data - buf1->parent->data + offset, len); } else { /* g_print ("slow path taken in buffer_span\n"); */ /* otherwise we simply have to brute-force copy the buffers */ newbuf = gst_buffer_new (buf1->pool, len); /* copy relevant stuff from data struct of buffer1 */ gst_data_copy (GST_DATA (newbuf), GST_DATA (buf1)); /* copy the first buffer's data across */ memcpy (newbuf->data, buf1->data + offset, buf1->size - offset); /* copy the second buffer's data across */ memcpy (newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset)); } return newbuf; } /** * gst_buffer_merge: * @buf1: first source buffer to merge * @buf2: second source buffer to merge * * Create a new buffer that is the concatenation of the two source * buffers. The original source buffers will not be modified or * unref'd. * * Internally is nothing more than a specialized gst_buffer_span, * so the same optimizations can occur. * * Returns: new buffer that's the concatenation of the source buffers */ GstBuffer * gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2) { guint i; GstBuffer *result; /* we're just a specific case of the more general gst_buffer_span() */ result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size); /* but we can include offset info */ for (i = 0; i < GST_OFFSET_TYPES; i++) { GST_DATA (result)->offset[i] = GST_DATA (buf1)->offset[i]; } return result; }