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;
34 static gint _gst_buffer_live;
37 _gst_buffer_initialize (void)
39 int buffersize = sizeof(GstBuffer);
40 static const GTypeInfo buffer_info = {
41 0, /* sizeof(class), */
47 0, /* sizeof(object), */
53 /* round up to the nearest 32 bytes for cache-line and other efficiencies */
54 buffersize = (((buffersize-1) / 32) + 1) * 32;
56 _gst_buffer_chunk = g_mem_chunk_new ("GstBuffer", buffersize,
57 buffersize * 32, G_ALLOC_AND_FREE);
59 _gst_buffer_chunk_lock = g_mutex_new ();
61 _gst_buffer_type = g_type_register_static (G_TYPE_INT, "GstBuffer", &buffer_info, 0);
67 * gst_buffer_print_stats:
69 * Print statistics about live buffers.
72 gst_buffer_print_stats (void)
74 g_log (g_log_domain_gstreamer, G_LOG_LEVEL_INFO,
75 "%d live buffers", _gst_buffer_live);
81 * Create a new buffer.
90 g_mutex_lock (_gst_buffer_chunk_lock);
91 buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
93 g_mutex_unlock (_gst_buffer_chunk_lock);
94 GST_INFO (GST_CAT_BUFFER,"creating new buffer %p",buffer);
96 GST_DATA_TYPE(buffer) = _gst_buffer_type;
98 buffer->lock = g_mutex_new ();
100 atomic_set (&buffer->refcount, 1);
102 buffer->refcount = 1;
109 buffer->timestamp = 0;
110 buffer->parent = NULL;
112 buffer->pool_private = NULL;
120 * gst_buffer_new_from_pool:
121 * @pool: the buffer pool to use
122 * @offset: the offset of the new buffer
123 * @size: the size of the new buffer
125 * Create a new buffer using the specified bufferpool, offset and size.
127 * Returns: new buffer
130 gst_buffer_new_from_pool (GstBufferPool *pool, guint32 offset, guint32 size)
134 g_return_val_if_fail (pool != NULL, NULL);
135 g_return_val_if_fail (pool->buffer_new != NULL, NULL);
137 buffer = pool->buffer_new (pool, offset, size, pool->user_data);
139 buffer->free = pool->buffer_free;
140 buffer->copy = pool->buffer_copy;
142 GST_INFO (GST_CAT_BUFFER,"creating new buffer %p from pool %p (size %x, offset %x)",
143 buffer, pool, size, offset);
149 * gst_buffer_create_sub:
150 * @parent: parent buffer
151 * @offset: offset into parent buffer
152 * @size: size of new subbuffer
154 * Creates a sub-buffer from the parent at a given offset.
156 * Returns: new buffer
159 gst_buffer_create_sub (GstBuffer *parent,
165 g_return_val_if_fail (parent != NULL, NULL);
166 g_return_val_if_fail (GST_BUFFER_REFCOUNT(parent) > 0, NULL);
167 g_return_val_if_fail (size > 0, NULL);
168 g_return_val_if_fail ((offset+size) <= parent->size, NULL);
170 g_mutex_lock (_gst_buffer_chunk_lock);
171 buffer = g_mem_chunk_alloc (_gst_buffer_chunk);
173 g_mutex_unlock (_gst_buffer_chunk_lock);
174 GST_INFO (GST_CAT_BUFFER,"creating new subbuffer %p from parent %p (size %u, offset %u)",
175 buffer, parent, size, offset);
177 GST_DATA_TYPE(buffer) = _gst_buffer_type;
178 buffer->lock = g_mutex_new ();
180 atomic_set (&buffer->refcount, 1);
182 buffer->refcount = 1;
185 /* copy flags and type from parent, for lack of better */
186 buffer->flags = parent->flags;
188 /* set the data pointer, size, offset, and maxsize */
189 buffer->data = parent->data + offset;
191 buffer->maxsize = parent->size - offset;
193 /* deal with bogus/unknown offsets */
194 if (parent->offset != (guint32)-1)
195 buffer->offset = parent->offset + offset;
197 buffer->offset = (guint32)-1;
199 /* again, for lack of better, copy parent's timestamp */
200 buffer->timestamp = parent->timestamp;
201 buffer->maxage = parent->maxage;
203 /* if the parent buffer is a subbuffer itself, use its parent, a real buffer */
204 if (parent->parent != NULL)
205 parent = parent->parent;
207 /* set parentage and reference the parent */
208 buffer->parent = parent;
209 gst_buffer_ref (parent);
217 /* FIXME FIXME: how does this overlap with the newly-added gst_buffer_span() ??? */
221 * @append: the buffer to append
223 * Creates a new buffer by appending the data of append to the
224 * existing data of buffer.
226 * Returns: new buffer
229 gst_buffer_append (GstBuffer *buffer,
235 g_return_val_if_fail (buffer != NULL, NULL);
236 g_return_val_if_fail (append != NULL, NULL);
237 g_return_val_if_fail (buffer->pool == NULL, NULL);
238 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
239 g_return_val_if_fail (GST_BUFFER_REFCOUNT(append) > 0, NULL);
241 GST_INFO (GST_CAT_BUFFER,"appending buffers %p and %p",buffer,append);
243 GST_BUFFER_LOCK (buffer);
244 /* the buffer is not used by anyone else */
245 if (GST_BUFFER_REFCOUNT (buffer) == 1 && buffer->parent == NULL
246 && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE)) {
247 /* save the old size */
249 buffer->size += append->size;
250 buffer->data = g_realloc (buffer->data, buffer->size);
251 memcpy(buffer->data + size, append->data, append->size);
252 GST_BUFFER_UNLOCK (buffer);
254 /* the buffer is used, create a new one */
256 newbuf = gst_buffer_new ();
257 newbuf->size = buffer->size+append->size;
258 newbuf->data = g_malloc (newbuf->size);
259 memcpy (newbuf->data, buffer->data, buffer->size);
260 memcpy (newbuf->data+buffer->size, append->data, append->size);
261 GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (buffer);
262 GST_BUFFER_UNLOCK (buffer);
263 gst_buffer_unref (buffer);
270 * gst_buffer_destroy:
271 * @buffer: the GstBuffer to destroy
276 gst_buffer_destroy (GstBuffer *buffer)
279 g_return_if_fail (buffer != NULL);
281 GST_INFO (GST_CAT_BUFFER, "freeing %sbuffer %p",
282 (buffer->parent?"sub":""),
285 /* free the data only if there is some, DONTFREE isn't set, and not sub */
286 if (GST_BUFFER_DATA (buffer) &&
287 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE) &&
288 (buffer->parent == NULL)) {
289 /* if there's a free function, use it */
290 if (buffer->free != NULL) {
291 (buffer->free)(buffer);
293 g_free (GST_BUFFER_DATA (buffer));
297 /* unreference the parent if there is one */
298 if (buffer->parent != NULL)
299 gst_buffer_unref (buffer->parent);
301 g_mutex_free (buffer->lock);
302 /* g_print("freed mutex\n"); */
304 #ifdef GST_DEBUG_ENABLED
305 /* make it hard to reuse by mistake */
306 memset (buffer, 0, sizeof (GstBuffer));
309 /* remove it entirely from memory */
310 g_mutex_lock (_gst_buffer_chunk_lock);
311 g_mem_chunk_free (_gst_buffer_chunk,buffer);
313 g_mutex_unlock (_gst_buffer_chunk_lock);
318 * @buffer: the GstBuffer to reference
320 * Increment the refcount of this buffer.
323 gst_buffer_ref (GstBuffer *buffer)
325 g_return_if_fail (buffer != NULL);
327 GST_INFO (GST_CAT_BUFFER, "ref buffer %p, current count is %d", buffer,GST_BUFFER_REFCOUNT(buffer));
328 g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
331 atomic_inc (&(buffer->refcount));
333 GST_BUFFER_LOCK (buffer);
335 GST_BUFFER_UNLOCK (buffer);
340 * gst_buffer_ref_by_count:
341 * @buffer: the GstBuffer to reference
344 * Increment the refcount of this buffer by the given number.
347 gst_buffer_ref_by_count (GstBuffer *buffer, gint count)
349 g_return_if_fail (buffer != NULL);
350 g_return_if_fail (count > 0);
353 g_return_if_fail (atomic_read (&(buffer->refcount)) > 0);
354 atomic_add (count, &(buffer->refcount));
356 g_return_if_fail (buffer->refcount > 0);
357 GST_BUFFER_LOCK (buffer);
358 buffer->refcount += count;
359 GST_BUFFER_UNLOCK (buffer);
365 * @buffer: the GstBuffer to unref
367 * Decrement the refcount of this buffer. If the refcount is
368 * zero, the buffer will be destroyed.
371 gst_buffer_unref (GstBuffer *buffer)
375 g_return_if_fail (buffer != NULL);
377 GST_INFO (GST_CAT_BUFFER, "unref buffer %p, current count is %d", buffer,GST_BUFFER_REFCOUNT(buffer));
378 g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
381 zero = atomic_dec_and_test (&(buffer->refcount));
383 GST_BUFFER_LOCK (buffer);
385 zero = (buffer->refcount == 0);
386 GST_BUFFER_UNLOCK (buffer);
389 /* if we ended up with the refcount at zero, destroy the buffer */
391 gst_buffer_destroy (buffer);
397 * @buffer: the orignal GstBuffer to make a copy of
399 * Make a full copy of the give buffer, data and all.
401 * Returns: new buffer
404 gst_buffer_copy (GstBuffer *buffer)
408 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
410 /* if a copy function exists, use it, else copy the bytes */
411 if (buffer->copy != NULL) {
412 newbuf = (buffer->copy)(buffer);
414 /* allocate a new buffer */
415 newbuf = gst_buffer_new();
417 /* copy the absolute size */
418 newbuf->size = buffer->size;
419 /* allocate space for the copy */
420 newbuf->data = (guchar *)g_malloc (buffer->size);
421 /* copy the data straight across */
422 memcpy(newbuf->data,buffer->data,buffer->size);
423 /* the new maxsize is the same as the size, since we just malloc'd it */
424 newbuf->maxsize = newbuf->size;
426 newbuf->offset = buffer->offset;
427 newbuf->timestamp = buffer->timestamp;
428 newbuf->maxage = buffer->maxage;
430 /* since we just created a new buffer, so we have no ties to old stuff */
431 newbuf->parent = NULL;
438 * gst_buffer_is_span_fast:
439 * @buf1: first source buffer
440 * @buf2: second source buffer
442 * Determines whether a gst_buffer_span is free, or requires a memcpy.
444 * Returns: TRUE if the buffers are contiguous, FALSE if a copy would be required.
447 gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2)
449 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, FALSE);
450 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, FALSE);
452 return (buf1->parent && buf2->parent &&
453 (buf1->parent == buf2->parent) &&
454 ((buf1->data + buf1->size) == buf2->data));
460 * @buf1: first source buffer to merge
461 * @offset: offset in first buffer to start new buffer
462 * @buf2: second source buffer to merge
463 * @len: length of new buffer
465 * Create a new buffer that consists of part of buf1 and buf2.
466 * Logically, buf1 and buf2 are concatenated into a single larger
467 * buffer, and a new buffer is created at the given offset inside
468 * this space, with a given length.
470 * If the two source buffers are children of the same larger buffer,
471 * and are contiguous, the new buffer will be a child of the shared
472 * parent, and thus no copying is necessary.
474 * Returns: new buffer that spans the two source buffers
476 /* FIXME need to think about CoW and such... */
478 gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len)
482 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, NULL);
483 g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, NULL);
485 /* make sure buf1 has a lower address than buf2 */
486 if (buf1->data > buf2->data) {
487 GstBuffer *tmp = buf1;
488 /* g_print ("swapping buffers\n"); */
493 /* if the two buffers have the same parent and are adjacent */
494 if (gst_buffer_is_span_fast(buf1,buf2)) {
495 /* we simply create a subbuffer of the common parent */
496 newbuf = gst_buffer_create_sub (buf1->parent, buf1->data - (buf1->parent->data) + offset, len);
499 /* g_print ("slow path taken in buffer_span\n"); */
500 /* otherwise we simply have to brute-force copy the buffers */
501 newbuf = gst_buffer_new ();
503 /* put in new size */
505 /* allocate space for the copy */
506 newbuf->data = (guchar *)g_malloc(len);
507 /* copy the first buffer's data across */
508 memcpy(newbuf->data, buf1->data + offset, buf1->size - offset);
509 /* copy the second buffer's data across */
510 memcpy(newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset));
512 if (newbuf->offset != (guint32)-1)
513 newbuf->offset = buf1->offset + offset;
514 newbuf->timestamp = buf1->timestamp;
515 if (buf2->maxage > buf1->maxage) newbuf->maxage = buf2->maxage;
516 else newbuf->maxage = buf1->maxage;
526 * @buf1: first source buffer to merge
527 * @buf2: second source buffer to merge
529 * Create a new buffer that is the concatenation of the two source
530 * buffers. The original source buffers will not be modified or
533 * Internally is nothing more than a specialized gst_buffer_span,
534 * so the same optimizations can occur.
536 * Returns: new buffer that's the concatenation of the source buffers
539 gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2)
542 /* we're just a specific case of the more general gst_buffer_span() */
543 result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size);
545 GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (buf1);