2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gstbuffer.c: Buffer operations
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 /* this file makes too much noise for most debugging sessions */
24 #define GST_DEBUG_FORCE_DISABLE
25 #include "gst_private.h"
27 #include "gstbuffer.h"
30 GType _gst_buffer_type;
32 static GMemChunk *_gst_buffer_chunk;
33 static GMutex *_gst_buffer_chunk_lock;
36 _gst_buffer_initialize (void)
38 int buffersize = sizeof(GstBuffer);
39 static const GTypeInfo buffer_info = {
51 // round up to the nearest 32 bytes for cache-line and other efficiencies
52 buffersize = (((buffersize-1) / 32) + 1) * 32;
54 _gst_buffer_chunk = g_mem_chunk_new ("GstBuffer", buffersize,
55 buffersize * 32, G_ALLOC_AND_FREE);
57 _gst_buffer_chunk_lock = g_mutex_new ();
59 _gst_buffer_type = g_type_register_static (G_TYPE_INT, "GstBuffer", &buffer_info, 0);
65 * Create a new buffer.
74 g_mutex_lock (_gst_buffer_chunk_lock);
75 buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
76 g_mutex_unlock (_gst_buffer_chunk_lock);
77 GST_INFO (GST_CAT_BUFFER,"creating new buffer %p",buffer);
79 GST_DATA_TYPE(buffer) = _gst_buffer_type;
81 buffer->lock = g_mutex_new ();
83 atomic_set (&buffer->refcount, 1);
92 buffer->timestamp = 0;
93 buffer->parent = NULL;
95 buffer->pool_private = NULL;
103 * gst_buffer_new_from_pool:
104 * @pool: the buffer pool to use
106 * Create a new buffer using the specified bufferpool.
108 * Returns: new buffer
111 gst_buffer_new_from_pool (GstBufferPool *pool, guint32 offset, guint32 size)
115 g_return_val_if_fail (pool != NULL, NULL);
116 g_return_val_if_fail (pool->buffer_new != NULL, NULL);
118 buffer = pool->buffer_new (pool, offset, size, pool->user_data);
120 buffer->free = pool->buffer_free;
121 buffer->copy = pool->buffer_copy;
123 GST_INFO (GST_CAT_BUFFER,"creating new buffer %p from pool %p (size %x, offset %x)",
124 buffer, pool, size, offset);
130 * gst_buffer_create_sub:
131 * @parent: parent buffer
132 * @offset: offset into parent buffer
133 * @size: size of new subbuffer
135 * Creates a sub-buffer from the parent at a given offset.
137 * Returns: new buffer
140 gst_buffer_create_sub (GstBuffer *parent,
146 g_return_val_if_fail (parent != NULL, NULL);
147 g_return_val_if_fail (GST_BUFFER_REFCOUNT(parent) > 0, NULL);
148 g_return_val_if_fail (size > 0, NULL);
149 g_return_val_if_fail ((offset+size) <= parent->size, NULL);
151 g_mutex_lock (_gst_buffer_chunk_lock);
152 buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
153 GST_DATA_TYPE(buffer) = _gst_buffer_type;
154 g_mutex_unlock (_gst_buffer_chunk_lock);
155 GST_INFO (GST_CAT_BUFFER,"creating new subbuffer %p from parent %p (size %u, offset %u)",
156 buffer, parent, size, offset);
158 buffer->lock = g_mutex_new ();
160 atomic_set (&buffer->refcount, 1);
162 buffer->refcount = 1;
165 // copy flags and type from parent, for lack of better
166 buffer->flags = parent->flags;
168 // set the data pointer, size, offset, and maxsize
169 buffer->data = parent->data + offset;
171 buffer->maxsize = parent->size - offset;
173 // deal with bogus/unknown offsets
174 if (parent->offset != -1)
175 buffer->offset = parent->offset + offset;
179 // again, for lack of better, copy parent's timestamp
180 buffer->timestamp = parent->timestamp;
181 buffer->maxage = parent->maxage;
183 // if the parent buffer is a subbuffer itself, use its parent, a real buffer
184 if (parent->parent != NULL)
185 parent = parent->parent;
187 // set parentage and reference the parent
188 buffer->parent = parent;
189 gst_buffer_ref (parent);
197 // FIXME FIXME: how does this overlap with the newly-added gst_buffer_span() ???
201 * @append: the buffer to append
203 * Creates a new buffer by appending the data of append to the
204 * existing data of buffer.
206 * Returns: new buffer
209 gst_buffer_append (GstBuffer *buffer,
215 g_return_val_if_fail (buffer != NULL, NULL);
216 g_return_val_if_fail (append != NULL, NULL);
217 g_return_val_if_fail (buffer->pool == NULL, NULL);
218 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
219 g_return_val_if_fail (GST_BUFFER_REFCOUNT(append) > 0, NULL);
221 GST_INFO (GST_CAT_BUFFER,"appending buffers %p and %p",buffer,append);
223 GST_BUFFER_LOCK (buffer);
224 // the buffer is not used by anyone else
225 if (GST_BUFFER_REFCOUNT (buffer) == 1 && buffer->parent == NULL
226 && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE)) {
229 buffer->size += append->size;
230 buffer->data = g_realloc (buffer->data, buffer->size);
231 memcpy(buffer->data + size, append->data, append->size);
232 GST_BUFFER_UNLOCK (buffer);
234 // the buffer is used, create a new one
236 newbuf = gst_buffer_new ();
237 newbuf->size = buffer->size+append->size;
238 newbuf->data = g_malloc (newbuf->size);
239 memcpy (newbuf->data, buffer->data, buffer->size);
240 memcpy (newbuf->data+buffer->size, append->data, append->size);
241 GST_BUFFER_UNLOCK (buffer);
242 gst_buffer_unref (buffer);
249 * gst_buffer_destroy:
250 * @buffer: the GstBuffer to destroy
255 gst_buffer_destroy (GstBuffer *buffer)
258 g_return_if_fail (buffer != NULL);
260 GST_INFO (GST_CAT_BUFFER, "freeing %sbuffer %p",
261 (buffer->parent?"sub":""),
264 // free the data only if there is some, DONTFREE isn't set, and not sub
265 if (GST_BUFFER_DATA (buffer) &&
266 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE) &&
267 (buffer->parent == NULL)) {
268 // if there's a free function, use it
269 if (buffer->free != NULL) {
270 (buffer->free)(buffer);
272 g_free (GST_BUFFER_DATA (buffer));
276 // unreference the parent if there is one
277 if (buffer->parent != NULL)
278 gst_buffer_unref (buffer->parent);
280 g_mutex_free (buffer->lock);
281 //g_print("freed mutex\n");
283 #ifdef GST_DEBUG_ENABLED
284 // make it hard to reuse by mistake
285 memset (buffer, 0, sizeof (GstBuffer));
288 // remove it entirely from memory
289 g_mutex_lock (_gst_buffer_chunk_lock);
290 g_mem_chunk_free (_gst_buffer_chunk,buffer);
291 g_mutex_unlock (_gst_buffer_chunk_lock);
296 * @buffer: the GstBuffer to reference
298 * Increment the refcount of this buffer.
301 gst_buffer_ref (GstBuffer *buffer)
303 g_return_if_fail (buffer != NULL);
304 g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
306 GST_INFO (GST_CAT_BUFFER, "ref buffer %p\n", buffer);
309 atomic_inc (&(buffer->refcount));
311 GST_BUFFER_LOCK (buffer);
313 GST_BUFFER_UNLOCK (buffer);
319 * @buffer: the GstBuffer to unref
321 * Decrement the refcount of this buffer. If the refcount is
322 * zero, the buffer will be destroyed.
325 gst_buffer_unref (GstBuffer *buffer)
329 g_return_if_fail (buffer != NULL);
330 g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
332 GST_INFO (GST_CAT_BUFFER, "unref buffer %p\n", buffer);
335 zero = atomic_dec_and_test (&(buffer->refcount));
337 GST_BUFFER_LOCK (buffer);
339 zero = (buffer->refcount == 0);
340 GST_BUFFER_UNLOCK (buffer);
343 /* if we ended up with the refcount at zero, destroy the buffer */
345 gst_buffer_destroy (buffer);
351 * @buffer: the orignal GstBuffer to make a copy of
353 * Make a full copy of the give buffer, data and all.
355 * Returns: new buffer
358 gst_buffer_copy (GstBuffer *buffer)
362 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
364 // if a copy function exists, use it, else copy the bytes
365 if (buffer->copy != NULL) {
366 newbuf = (buffer->copy)(buffer);
368 // allocate a new buffer
369 newbuf = gst_buffer_new();
371 // copy the absolute size
372 newbuf->size = buffer->size;
373 // allocate space for the copy
374 newbuf->data = (guchar *)g_malloc (buffer->size);
375 // copy the data straight across
376 memcpy(newbuf->data,buffer->data,buffer->size);
377 // the new maxsize is the same as the size, since we just malloc'd it
378 newbuf->maxsize = newbuf->size;
380 newbuf->offset = buffer->offset;
381 newbuf->timestamp = buffer->timestamp;
382 newbuf->maxage = buffer->maxage;
384 // since we just created a new buffer, so we have no ties to old stuff
385 newbuf->parent = NULL;
392 * gst_buffer_is_span_fast
393 * @buf1: first source buffer
394 * @buf2: second source buffer
396 * Determines whether a gst_buffer_span is free, or requires a memcpy.
398 * Returns: TRUE if the buffers are contiguous, FALSE if a copy would be required.
401 gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2)
403 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, FALSE);
404 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, FALSE);
406 return (buf1->parent && buf2->parent &&
407 (buf1->parent == buf2->parent) &&
408 ((buf1->data + buf1->size) == buf2->data));
414 * @buf1: first source buffer to merge
415 * @offset: offset in first buffer to start new buffer
416 * @buf2: second source buffer to merge
417 * @len: length of new buffer
419 * Create a new buffer that consists of part of buf1 and buf2.
420 * Logically, buf1 and buf2 are concatenated into a single larger
421 * buffer, and a new buffer is created at the given offset inside
422 * this space, with a given length.
424 * If the two source buffers are children of the same larger buffer,
425 * and are contiguous, the new buffer will be a child of the shared
426 * parent, and thus no copying is necessary.
428 * Returns: new buffer that spans the two source buffers
430 // FIXME need to think about CoW and such...
432 gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len)
436 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, NULL);
437 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, NULL);
439 // make sure buf1 has a lower address than buf2
440 if (buf1->data > buf2->data) {
441 GstBuffer *tmp = buf1;
442 g_print ("swapping buffers\n");
447 // if the two buffers have the same parent and are adjacent
448 if (gst_buffer_is_span_fast(buf1,buf2)) {
449 // we simply create a subbuffer of the common parent
450 newbuf = gst_buffer_create_sub (buf1->parent, buf1->data - (buf1->parent->data) + offset, len);
453 g_print ("slow path taken in buffer_span\n");
454 // otherwise we simply have to brute-force copy the buffers
455 newbuf = gst_buffer_new ();
459 // allocate space for the copy
460 newbuf->data = (guchar *)g_malloc(len);
461 // copy the first buffer's data across
462 memcpy(newbuf->data, buf1->data + offset, buf1->size - offset);
463 // copy the second buffer's data across
464 memcpy(newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset));
466 if (newbuf->offset != -1)
467 newbuf->offset = buf1->offset + offset;
468 newbuf->timestamp = buf1->timestamp;
469 if (buf2->maxage > buf1->maxage) newbuf->maxage = buf2->maxage;
470 else newbuf->maxage = buf1->maxage;
480 * @buf1: first source buffer to merge
481 * @buf2: second source buffer to merge
483 * Create a new buffer that is the concatenation of the two source
484 * buffers. The original source buffers will not be modified or
487 * Internally is nothing more than a specialized gst_buffer_span,
488 * so the same optimizations can occur.
490 * Returns: new buffer that's the concatenation of the source buffers
493 gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2)
495 // we're just a specific case of the more general gst_buffer_span()
496 return gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size);