X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstmemory.c;h=5a7f289ea54f00d1dd8bbdcdcc029bf135d09d6f;hb=1074a4e99a473efd5ee690da9ecd797c55cec23a;hp=ebaefc3a42aa09085355f09d69073572d5e44925;hpb=b8e1506b08b1e17493d13abcf7b190fee767267d;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstmemory.c b/gst/gstmemory.c index ebaefc3..5a7f289 100644 --- a/gst/gstmemory.c +++ b/gst/gstmemory.c @@ -27,13 +27,19 @@ * GstMemory is a lightweight refcounted object that wraps a region of memory. * They are typically used to manage the data of a #GstBuffer. * + * A GstMemory object has an allocated region of memory of maxsize. The maximum + * size does not change during the lifetime of the memory object. The memory + * also has an offset and size property that specifies the valid range of memory + * in the allocated region. + * * Memory is usually created by allocators with a gst_allocator_alloc() * method call. When NULL is used as the allocator, the default allocator will * be used. * * New allocators can be registered with gst_allocator_register(). * Allocators are identified by name and can be retrieved with - * gst_allocator_find(). + * gst_allocator_find(). gst_allocator_set_default() can be used to change the + * default allocator. * * New memory can be created with gst_memory_new_wrapped() that wraps the memory * allocated elsewhere. @@ -45,26 +51,42 @@ * gst_memory_get_sizes() and gst_memory_resize() respectively. * * Getting access to the data of the memory is performed with gst_memory_map(). + * The call will return a pointer to offset bytes into the region of memory. * After the memory access is completed, gst_memory_unmap() should be called. * - * Memory can be copied with gst_memory_copy(), which will returnn a writable + * Memory can be copied with gst_memory_copy(), which will return a writable * copy. gst_memory_share() will create a new memory block that shares the * memory with an existing memory block at a custom offset and with a custom * size. * * Memory can be efficiently merged when gst_memory_is_span() returns TRUE. * - * Last reviewed on 2011-06-08 (0.11.0) + * Last reviewed on 2012-03-28 (0.11.3) */ +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + #include "gst_private.h" #include "gstmemory.h" +#ifndef GST_DISABLE_TRACE +#include "gsttrace.h" +static GstAllocTrace *_gst_memory_trace; +static GstAllocTrace *_gst_allocator_trace; +#endif + +G_DEFINE_BOXED_TYPE (GstMemory, gst_memory, (GBoxedCopyFunc) gst_memory_ref, + (GBoxedFreeFunc) gst_memory_unref); + +G_DEFINE_BOXED_TYPE (GstAllocator, gst_allocator, + (GBoxedCopyFunc) gst_allocator_ref, (GBoxedFreeFunc) gst_allocator_unref); + +G_DEFINE_BOXED_TYPE (GstAllocationParams, gst_allocation_params, + (GBoxedCopyFunc) gst_allocation_params_copy, + (GBoxedFreeFunc) gst_allocation_params_free); -/* buffer alignment in bytes - 1 - * an alignment of 7 would be the same as malloc() guarantees - */ #if defined(MEMORY_ALIGNMENT_MALLOC) size_t gst_memory_alignment = 7; #elif defined(MEMORY_ALIGNMENT_PAGESIZE) @@ -79,9 +101,12 @@ size_t gst_memory_alignment = 0; struct _GstAllocator { - GQuark name; + gint refcount; GstMemoryInfo info; + + gpointer user_data; + GDestroyNotify notify; }; /* default memory implementation */ @@ -90,40 +115,47 @@ typedef struct GstMemory mem; gsize slice_size; guint8 *data; - GFreeFunc free_func; - gsize maxsize; - gsize offset; - gsize size; + gpointer user_data; + GDestroyNotify notify; } GstMemoryDefault; /* the default allocator */ -static const GstAllocator *_default_allocator; +static GstAllocator *_default_allocator; /* our predefined allocators */ -static const GstAllocator *_default_mem_impl; +static GstAllocator *_default_mem_impl; /* initialize the fields */ static void _default_mem_init (GstMemoryDefault * mem, GstMemoryFlags flags, GstMemory * parent, gsize slice_size, gpointer data, - GFreeFunc free_func, gsize maxsize, gsize offset, gsize size) + gsize maxsize, gsize offset, gsize size, gsize align, + gpointer user_data, GDestroyNotify notify) { mem->mem.allocator = _default_mem_impl; mem->mem.flags = flags; mem->mem.refcount = 1; mem->mem.parent = parent ? gst_memory_ref (parent) : NULL; + mem->mem.state = (flags & GST_MEMORY_FLAG_READONLY ? 0x1 : 0); + mem->mem.maxsize = maxsize; + mem->mem.align = align; + mem->mem.offset = offset; + mem->mem.size = size; mem->slice_size = slice_size; mem->data = data; - mem->free_func = free_func; - mem->maxsize = maxsize; - mem->offset = offset; - mem->size = size; + mem->user_data = user_data; + mem->notify = notify; + + GST_CAT_DEBUG (GST_CAT_MEMORY, "new memory %p, maxsize:%" G_GSIZE_FORMAT + " offset:%" G_GSIZE_FORMAT " size:%" G_GSIZE_FORMAT, mem, maxsize, + offset, size); } /* create a new memory block that manages the given memory */ static GstMemoryDefault * _default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data, - GFreeFunc free_func, gsize maxsize, gsize offset, gsize size) + gsize maxsize, gsize offset, gsize size, gsize align, gpointer user_data, + GDestroyNotify notify) { GstMemoryDefault *mem; gsize slice_size; @@ -132,17 +164,18 @@ _default_mem_new (GstMemoryFlags flags, GstMemory * parent, gpointer data, mem = g_slice_alloc (slice_size); _default_mem_init (mem, flags, parent, slice_size, - data, free_func, maxsize, offset, size); + data, maxsize, offset, size, align, user_data, notify); return mem; } /* allocate the memory and structure in one block */ static GstMemoryDefault * -_default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size) +_default_mem_new_block (GstMemoryFlags flags, gsize maxsize, gsize align, + gsize offset, gsize size) { GstMemoryDefault *mem; - gsize aoffset, slice_size; + gsize aoffset, slice_size, padding; guint8 *data; /* ensure configured alignment */ @@ -158,69 +191,58 @@ _default_mem_new_block (gsize maxsize, gsize align, gsize offset, gsize size) data = (guint8 *) mem + sizeof (GstMemoryDefault); - if ((aoffset = ((guintptr) data & align))) + /* do alignment */ + if ((aoffset = ((guintptr) data & align))) { aoffset = (align + 1) - aoffset; + data += aoffset; + maxsize -= aoffset; + } - _default_mem_init (mem, 0, NULL, slice_size, data, NULL, maxsize, - aoffset + offset, size); + if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED)) + memset (data, 0, offset); - return mem; -} + padding = maxsize - (offset + size); + if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED)) + memset (data + offset + size, 0, padding); -static GstMemory * -_default_mem_alloc (const GstAllocator * allocator, gsize maxsize, gsize align) -{ - return (GstMemory *) _default_mem_new_block (maxsize, align, 0, maxsize); -} - -static gsize -_default_mem_get_sizes (GstMemoryDefault * mem, gsize * offset, gsize * maxsize) -{ - if (offset) - *offset = mem->offset; - if (maxsize) - *maxsize = mem->maxsize; + _default_mem_init (mem, flags, NULL, slice_size, data, maxsize, + offset, size, align, NULL, NULL); - return mem->size; + return mem; } -static void -_default_mem_resize (GstMemoryDefault * mem, gssize offset, gsize size) +static GstMemory * +_default_alloc_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params, gpointer user_data) { - g_return_if_fail (size + mem->offset + offset <= mem->maxsize); + gsize maxsize = size + params->prefix + params->padding; - mem->offset += offset; - mem->size = size; + return (GstMemory *) _default_mem_new_block (params->flags, + maxsize, params->align, params->prefix, size); } static gpointer -_default_mem_map (GstMemoryDefault * mem, gsize * size, gsize * maxsize, - GstMapFlags flags) +_default_mem_map (GstMemoryDefault * mem, GstMapFlags flags) { - if (size) - *size = mem->size; - if (maxsize) - *maxsize = mem->maxsize; - - return mem->data + mem->offset; + return mem->data; } static gboolean -_default_mem_unmap (GstMemoryDefault * mem, gpointer data, gsize size) +_default_mem_unmap (GstMemoryDefault * mem) { - if (size != -1) - mem->size = size; return TRUE; } static void _default_mem_free (GstMemoryDefault * mem) { + GST_CAT_DEBUG (GST_CAT_MEMORY, "free memory %p", mem); + if (mem->mem.parent) gst_memory_unref (mem->mem.parent); - if (mem->free_func) - mem->free_func (mem->data); + if (mem->notify) + mem->notify (mem->user_data); g_slice_free1 (mem->slice_size, mem); } @@ -231,10 +253,13 @@ _default_mem_copy (GstMemoryDefault * mem, gssize offset, gsize size) GstMemoryDefault *copy; if (size == -1) - size = mem->size > offset ? mem->size - offset : 0; + size = mem->mem.size > offset ? mem->mem.size - offset : 0; - copy = _default_mem_new_block (mem->maxsize, 0, mem->offset + offset, size); - memcpy (copy->data, mem->data, mem->maxsize); + copy = + _default_mem_new_block (0, mem->mem.maxsize, 0, mem->mem.offset + offset, + size); + memcpy (copy->data, mem->data, mem->mem.maxsize); + GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy); return copy; } @@ -250,10 +275,12 @@ _default_mem_share (GstMemoryDefault * mem, gssize offset, gsize size) parent = (GstMemory *) mem; if (size == -1) - size = mem->size - offset; + size = mem->mem.size - offset; - sub = _default_mem_new (parent->flags, parent, mem->data, NULL, mem->maxsize, - mem->offset + offset, size); + sub = + _default_mem_new (parent->flags, parent, mem->data, + mem->mem.maxsize, mem->mem.offset + offset, size, mem->mem.align, NULL, + NULL); return sub; } @@ -268,59 +295,78 @@ _default_mem_is_span (GstMemoryDefault * mem1, GstMemoryDefault * mem2, parent = (GstMemoryDefault *) mem1->mem.parent; - *offset = mem1->offset - parent->offset; + *offset = mem1->mem.offset - parent->mem.offset; } /* and memory is contiguous */ - return mem1->data + mem1->offset + mem1->size == mem2->data + mem2->offset; + return mem1->data + mem1->mem.offset + mem1->mem.size == + mem2->data + mem2->mem.offset; } static GstMemory * -_fallback_copy (GstMemory * mem, gssize offset, gsize size) +_fallback_mem_copy (GstMemory * mem, gssize offset, gssize size) { GstMemory *copy; - guint8 *data, *dest; - gsize msize; + GstMapInfo sinfo, dinfo; + GstAllocationParams params = { 0, 0, 0, mem->align, }; + + if (!gst_memory_map (mem, &sinfo, GST_MAP_READ)) + return NULL; - data = gst_memory_map (mem, &msize, NULL, GST_MAP_READ); if (size == -1) - size = msize > offset ? msize - offset : 0; - /* use the same allocator as the memory we copy, FIXME, alignment? */ - copy = gst_allocator_alloc (mem->allocator, size, 0); - dest = gst_memory_map (copy, NULL, NULL, GST_MAP_WRITE); - memcpy (dest, data + offset, size); - gst_memory_unmap (copy, dest, size); + size = sinfo.size > offset ? sinfo.size - offset : 0; - gst_memory_unmap (mem, data, msize); + /* use the same allocator as the memory we copy */ + copy = gst_allocator_alloc (mem->allocator, size, ¶ms); + if (!gst_memory_map (copy, &dinfo, GST_MAP_WRITE)) { + GST_CAT_WARNING (GST_CAT_MEMORY, "could not write map memory %p", copy); + gst_memory_unmap (mem, &sinfo); + return NULL; + } + + memcpy (dinfo.data, sinfo.data + offset, size); + GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy memory %p -> %p", mem, copy); + gst_memory_unmap (copy, &dinfo); + gst_memory_unmap (mem, &sinfo); - return (GstMemory *) copy; + return copy; } static gboolean -_fallback_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset) +_fallback_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset) { return FALSE; } -static GStaticRWLock lock = G_STATIC_RW_LOCK_INIT; +static GRWLock lock; static GHashTable *allocators; +static void +_priv_sysmem_notify (gpointer user_data) +{ + g_warning ("The default memory allocator was freed!"); +} + void _priv_gst_memory_initialize (void) { static const GstMemoryInfo _mem_info = { - (GstMemoryAllocFunction) _default_mem_alloc, - (GstMemoryGetSizesFunction) _default_mem_get_sizes, - (GstMemoryResizeFunction) _default_mem_resize, + GST_ALLOCATOR_SYSMEM, + (GstAllocatorAllocFunction) _default_alloc_alloc, (GstMemoryMapFunction) _default_mem_map, (GstMemoryUnmapFunction) _default_mem_unmap, (GstMemoryFreeFunction) _default_mem_free, (GstMemoryCopyFunction) _default_mem_copy, (GstMemoryShareFunction) _default_mem_share, (GstMemoryIsSpanFunction) _default_mem_is_span, - NULL }; +#ifndef GST_DISABLE_TRACE + _gst_memory_trace = _gst_alloc_trace_register ("GstMemory", -1); + _gst_allocator_trace = _gst_alloc_trace_register ("GstAllocator", -1); +#endif + + g_rw_lock_init (&lock); allocators = g_hash_table_new (g_str_hash, g_str_equal); #ifdef HAVE_GETPAGESIZE @@ -329,36 +375,50 @@ _priv_gst_memory_initialize (void) #endif #endif - GST_DEBUG ("memory alignment: %" G_GSIZE_FORMAT, gst_memory_alignment); + GST_CAT_DEBUG (GST_CAT_MEMORY, "memory alignment: %" G_GSIZE_FORMAT, + gst_memory_alignment); - _default_mem_impl = gst_allocator_register (GST_ALLOCATOR_SYSMEM, &_mem_info); + _default_mem_impl = gst_allocator_new (&_mem_info, NULL, _priv_sysmem_notify); - _default_allocator = _default_mem_impl; + _default_allocator = gst_allocator_ref (_default_mem_impl); + gst_allocator_register (GST_ALLOCATOR_SYSMEM, + gst_allocator_ref (_default_mem_impl)); } /** * gst_memory_new_wrapped: * @flags: #GstMemoryFlags * @data: data to wrap - * @free_func: function to free @data * @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 * * Allocate a new memory block that wraps the given @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: a new #GstMemory. */ GstMemory * gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data, - GFreeFunc free_func, gsize maxsize, gsize offset, gsize size) + gsize maxsize, gsize offset, gsize size, gpointer user_data, + GDestroyNotify notify) { GstMemoryDefault *mem; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (offset + size <= maxsize, NULL); - mem = _default_mem_new (flags, NULL, data, free_func, maxsize, offset, size); + mem = + _default_mem_new (flags, NULL, data, maxsize, offset, size, 0, user_data, + notify); + +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_new (_gst_memory_trace, mem); +#endif return (GstMemory *) mem; } @@ -376,6 +436,9 @@ gst_memory_ref (GstMemory * mem) { g_return_val_if_fail (mem != NULL, NULL); + GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount, + mem->refcount + 1); + g_atomic_int_inc (&mem->refcount); return mem; @@ -394,8 +457,32 @@ gst_memory_unref (GstMemory * mem) g_return_if_fail (mem != NULL); g_return_if_fail (mem->allocator != NULL); - if (g_atomic_int_dec_and_test (&mem->refcount)) - mem->allocator->info.free (mem); + GST_CAT_TRACE (GST_CAT_MEMORY, "memory %p, %d->%d", mem, mem->refcount, + mem->refcount - 1); + + if (g_atomic_int_dec_and_test (&mem->refcount)) { + /* there should be no outstanding mappings */ + g_return_if_fail (g_atomic_int_get (&mem->state) < 4); +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_free (_gst_memory_trace, mem); +#endif + mem->allocator->info.mem_free (mem); + } +} + +/** + * gst_memory_is_exclusive: + * @mem: a #GstMemory + * + * Check if the current ref to @mem is exclusive, this means that no other + * references exist other than @mem. + */ +gboolean +gst_memory_is_exclusive (GstMemory * mem) +{ + g_return_val_if_fail (mem != NULL, FALSE); + + return (g_atomic_int_get (&mem->refcount) == 1); } /** @@ -413,7 +500,12 @@ gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize) { g_return_val_if_fail (mem != NULL, 0); - return mem->allocator->info.get_sizes (mem, offset, maxsize); + if (offset) + *offset = mem->offset; + if (maxsize) + *maxsize = mem->maxsize; + + return mem->size; } /** @@ -424,66 +516,209 @@ gst_memory_get_sizes (GstMemory * mem, gsize * offset, gsize * maxsize) * * Resize the memory region. @mem should be writable and offset + size should be * less than the maxsize of @mem. + * + * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED will be + * cleared when offset or padding is increased respectively. */ void gst_memory_resize (GstMemory * mem, gssize offset, gsize size) { g_return_if_fail (mem != NULL); - g_return_if_fail (GST_MEMORY_IS_WRITABLE (mem)); + g_return_if_fail (offset >= 0 || mem->offset >= -offset); + g_return_if_fail (size + mem->offset + offset <= mem->maxsize); + + /* if we increase the prefix, we can't guarantee it is still 0 filled */ + if ((offset > 0) && GST_MEMORY_IS_ZERO_PREFIXED (mem)) + GST_MEMORY_FLAG_UNSET (mem, GST_MEMORY_FLAG_ZERO_PREFIXED); + + /* if we increase the padding, we can't guarantee it is still 0 filled */ + if ((offset + size < mem->size) && GST_MEMORY_IS_ZERO_PADDED (mem)) + GST_MEMORY_FLAG_UNSET (mem, GST_MEMORY_FLAG_ZERO_PADDED); - mem->allocator->info.resize (mem, offset, size); + mem->offset += offset; + mem->size = size; +} + +static gboolean +gst_memory_lock (GstMemory * mem, GstMapFlags flags) +{ + gint access_mode, state, newstate; + + access_mode = flags & 3; + + do { + state = g_atomic_int_get (&mem->state); + if (state == 0) { + /* nothing mapped, set access_mode and refcount */ + newstate = 4 | access_mode; + } else { + /* access_mode must match */ + if ((state & access_mode) != access_mode) + goto lock_failed; + /* increase refcount */ + newstate = state + 4; + } + } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate)); + + return TRUE; + +lock_failed: + { + GST_CAT_DEBUG (GST_CAT_MEMORY, "lock failed %p: state %d, access_mode %d", + mem, state, access_mode); + return FALSE; + } } +static void +gst_memory_unlock (GstMemory * mem) +{ + gint state, newstate; + + do { + state = g_atomic_int_get (&mem->state); + /* decrease the refcount */ + newstate = state - 4; + /* last refcount, unset access_mode */ + if (newstate < 4) + newstate = 0; + } while (!g_atomic_int_compare_and_exchange (&mem->state, state, newstate)); +} + + /** - * gst_memory_map: - * @mem: a #GstMemory - * @size: pointer for size - * @maxsize: pointer for maxsize + * gst_memory_make_mapped: + * @mem: (transfer full): a #GstMemory + * @info: (out): pointer for info * @flags: mapping flags * - * Get a pointer to the memory of @mem that can be accessed according to @flags. + * Create a #GstMemory object that is mapped with @flags. If @mem is mappable + * with @flags, this function returns the mapped @mem directly. Otherwise a + * mapped copy of @mem is returned. * - * @size and @maxsize will contain the size of the memory and the maximum - * allocated memory of @mem respectively. They can be set to NULL. + * This function takes ownership of old @mem and returns a reference to a new + * #GstMemory. * - * Returns: a pointer to the memory of @mem. + * Returns: (transfer full): a #GstMemory object mapped with @flags or NULL when + * a mapping is not possible. */ -gpointer -gst_memory_map (GstMemory * mem, gsize * size, gsize * maxsize, - GstMapFlags flags) +GstMemory * +gst_memory_make_mapped (GstMemory * mem, GstMapInfo * info, GstMapFlags flags) { - g_return_val_if_fail (mem != NULL, NULL); - g_return_val_if_fail (!(flags & GST_MAP_WRITE) || - GST_MEMORY_IS_WRITABLE (mem), NULL); + GstMemory *result; + + if (gst_memory_map (mem, info, flags)) { + result = mem; + } else { + result = gst_memory_copy (mem, 0, -1); + gst_memory_unref (mem); + + if (result == NULL) + goto cannot_copy; + + if (!gst_memory_map (result, info, flags)) + goto cannot_map; + } + return result; - return mem->allocator->info.map (mem, size, maxsize, flags); + /* ERRORS */ +cannot_copy: + { + GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot copy memory %p", mem); + return NULL; + } +cannot_map: + { + GST_CAT_DEBUG (GST_CAT_MEMORY, "cannot map memory %p with flags %d", mem, + flags); + gst_memory_unref (result); + return NULL; + } } /** - * gst_memory_unmap: + * gst_memory_map: * @mem: a #GstMemory - * @data: data to unmap - * @size: new size of @mem + * @info: (out): pointer for info + * @flags: mapping flags + * + * Fill @info with the pointer and sizes of the memory in @mem that can be + * accessed according to @flags. + * + * This function can return %FALSE for various reasons: + * - the memory backed by @mem is not accessible with the given @flags. + * - the memory was already mapped with a different mapping. + * + * @info and its contents remain valid for as long as @mem is valid and + * until gst_memory_unmap() is called. * - * Release the memory pointer obtained with gst_memory_map() and set the size of - * the memory to @size. @size can be set to -1 when the size should not be - * updated. + * For each gst_memory_map() call, a corresponding gst_memory_unmap() call + * should be done. * - * Returns: TRUE when the memory was release successfully. + * Returns: %TRUE if the map operation was successful. */ gboolean -gst_memory_unmap (GstMemory * mem, gpointer data, gsize size) +gst_memory_map (GstMemory * mem, GstMapInfo * info, GstMapFlags flags) { g_return_val_if_fail (mem != NULL, FALSE); + g_return_val_if_fail (info != NULL, FALSE); - return mem->allocator->info.unmap (mem, data, size); + if (!gst_memory_lock (mem, flags)) + goto lock_failed; + + info->data = mem->allocator->info.mem_map (mem, mem->maxsize, flags); + + if (G_UNLIKELY (info->data == NULL)) + goto error; + + info->memory = mem; + info->flags = flags; + info->size = mem->size; + info->maxsize = mem->maxsize - mem->offset; + info->data = info->data + mem->offset; + + return TRUE; + + /* ERRORS */ +lock_failed: + { + GST_CAT_DEBUG (GST_CAT_MEMORY, "mem %p: lock %d failed", mem, flags); + return FALSE; + } +error: + { + /* something went wrong, restore the orginal state again */ + GST_CAT_ERROR (GST_CAT_MEMORY, "mem %p: map failed", mem); + gst_memory_unlock (mem); + return FALSE; + } +} + +/** + * gst_memory_unmap: + * @mem: a #GstMemory + * @info: a #GstMapInfo + * + * Release the memory obtained with gst_memory_map() + */ +void +gst_memory_unmap (GstMemory * mem, GstMapInfo * info) +{ + g_return_if_fail (mem != NULL); + g_return_if_fail (info != NULL); + g_return_if_fail (info->memory == mem); + /* there must be a ref */ + g_return_if_fail (g_atomic_int_get (&mem->state) >= 4); + + mem->allocator->info.mem_unmap (mem); + gst_memory_unlock (mem); } /** * gst_memory_copy: * @mem: a #GstMemory * @offset: an offset to copy - * @size: size to copy + * @size: size to copy or -1 to copy all bytes from offset * * Return a copy of @size bytes from @mem starting from @offset. This copy is * guaranteed to be writable. @size can be set to -1 to return a copy all bytes @@ -492,32 +727,50 @@ gst_memory_unmap (GstMemory * mem, gpointer data, gsize size) * Returns: a new #GstMemory. */ GstMemory * -gst_memory_copy (GstMemory * mem, gssize offset, gsize size) +gst_memory_copy (GstMemory * mem, gssize offset, gssize size) { + GstMemory *copy; + g_return_val_if_fail (mem != NULL, NULL); - return mem->allocator->info.copy (mem, offset, size); + copy = mem->allocator->info.mem_copy (mem, offset, size); + +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_new (_gst_memory_trace, copy); +#endif + + return copy; } /** * gst_memory_share: * @mem: a #GstMemory * @offset: an offset to share - * @size: size to share + * @size: size to share or -1 to share bytes from offset * - * Return a shared copy of @size bytes from @mem starting from @offset. No memory - * copy is performed and the memory region is simply shared. The result is - * guaranteed to be not-writable. @size can be set to -1 to return a share all bytes - * from @offset. + * Return a shared copy of @size bytes from @mem starting from @offset. No + * memory copy is performed and the memory region is simply shared. The result + * is guaranteed to be not-writable. @size can be set to -1 to return a share + * all bytes from @offset. * * Returns: a new #GstMemory. */ GstMemory * -gst_memory_share (GstMemory * mem, gssize offset, gsize size) +gst_memory_share (GstMemory * mem, gssize offset, gssize size) { + GstMemory *shared; + g_return_val_if_fail (mem != NULL, NULL); + g_return_val_if_fail (!GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NO_SHARE), + NULL); - return mem->allocator->info.share (mem, offset, size); + shared = mem->allocator->info.mem_share (mem, offset, size); + +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_new (_gst_memory_trace, shared); +#endif + + return shared; } /** @@ -550,128 +803,289 @@ gst_memory_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset) return FALSE; /* and memory is contiguous */ - if (!mem1->allocator->info.is_span (mem1, mem2, offset)) + if (!mem1->allocator->info.mem_is_span (mem1, mem2, offset)) return FALSE; return TRUE; } /** - * gst_allocator_register: - * @name: the name of the allocator - * @info: #GstMemoryInfo + * gst_allocator_new: + * @info: a #GstMemoryInfo + * @user_data: user data + * @notify: a #GDestroyNotify for @user_data * - * Registers the memory allocator with @name and implementation functions - * @info. + * Create a new memory allocator with @info and @user_data. * * All functions in @info are mandatory exept the copy and is_span * functions, which will have a default implementation when left NULL. * - * The user_data field in @info will be passed to all calls of the alloc - * function. + * The @user_data will be passed to all calls of the alloc function. @notify + * will be called with @user_data when the allocator is freed. * * Returns: a new #GstAllocator. */ -const GstAllocator * -gst_allocator_register (const gchar * name, const GstMemoryInfo * info) +GstAllocator * +gst_allocator_new (const GstMemoryInfo * info, gpointer user_data, + GDestroyNotify notify) { GstAllocator *allocator; #define INSTALL_FALLBACK(_t) \ if (allocator->info._t == NULL) allocator->info._t = _fallback_ ##_t; - g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (info != NULL, NULL); g_return_val_if_fail (info->alloc != NULL, NULL); - g_return_val_if_fail (info->get_sizes != NULL, NULL); - g_return_val_if_fail (info->resize != NULL, NULL); - g_return_val_if_fail (info->map != NULL, NULL); - g_return_val_if_fail (info->unmap != NULL, NULL); - g_return_val_if_fail (info->free != NULL, NULL); - g_return_val_if_fail (info->share != NULL, NULL); + g_return_val_if_fail (info->mem_map != NULL, NULL); + g_return_val_if_fail (info->mem_unmap != NULL, NULL); + g_return_val_if_fail (info->mem_free != NULL, NULL); + g_return_val_if_fail (info->mem_share != NULL, NULL); allocator = g_slice_new (GstAllocator); - allocator->name = g_quark_from_string (name); + allocator->refcount = 1; allocator->info = *info; - INSTALL_FALLBACK (copy); - INSTALL_FALLBACK (is_span); + allocator->user_data = user_data; + allocator->notify = notify; + INSTALL_FALLBACK (mem_copy); + INSTALL_FALLBACK (mem_is_span); #undef INSTALL_FALLBACK - GST_DEBUG ("registering allocator \"%s\"", name); + GST_CAT_DEBUG (GST_CAT_MEMORY, "new allocator %p", allocator); - g_static_rw_lock_writer_lock (&lock); - g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator); - g_static_rw_lock_writer_unlock (&lock); +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_new (_gst_allocator_trace, allocator); +#endif return allocator; } /** + * gst_allocator_get_memory_type: + * @allocator: a #GstAllocator + * + * Get the memory type allocated by this allocator + * + * Returns: the memory type provided by @allocator + */ +const gchar * +gst_allocator_get_memory_type (GstAllocator * allocator) +{ + g_return_val_if_fail (allocator != NULL, NULL); + + return allocator->info.mem_type; +} + +/** + * gst_allocator_ref: + * @allocator: a #GstAllocator + * + * Increases the refcount of @allocator. + * + * Returns: @allocator with increased refcount + */ +GstAllocator * +gst_allocator_ref (GstAllocator * allocator) +{ + g_return_val_if_fail (allocator != NULL, NULL); + + GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator, + allocator->refcount, allocator->refcount + 1); + + g_atomic_int_inc (&allocator->refcount); + + return allocator; +} + +/** + * gst_allocator_unref: + * @allocator: a #GstAllocator + * + * Decreases the refcount of @allocator. When the refcount reaches 0, the notify + * function of @allocator will be called and the allocator will be freed. + */ +void +gst_allocator_unref (GstAllocator * allocator) +{ + g_return_if_fail (allocator != NULL); + + GST_CAT_TRACE (GST_CAT_MEMORY, "allocator %p, %d->%d", allocator, + allocator->refcount, allocator->refcount - 1); + + if (g_atomic_int_dec_and_test (&allocator->refcount)) { + if (allocator->notify) + allocator->notify (allocator->user_data); +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_free (_gst_allocator_trace, allocator); +#endif + g_slice_free1 (sizeof (GstAllocator), allocator); + } +} + +/** + * gst_allocator_register: + * @name: the name of the allocator + * @allocator: (transfer full): #GstAllocator + * + * Registers the memory @allocator with @name. This function takes ownership of + * @allocator. + */ +void +gst_allocator_register (const gchar * name, GstAllocator * allocator) +{ + g_return_if_fail (name != NULL); + g_return_if_fail (allocator != NULL); + + GST_CAT_DEBUG (GST_CAT_MEMORY, "registering allocator %p with name \"%s\"", + allocator, name); + + g_rw_lock_writer_lock (&lock); + g_hash_table_insert (allocators, (gpointer) name, (gpointer) allocator); + g_rw_lock_writer_unlock (&lock); +} + +/** * gst_allocator_find: * @name: the name of the allocator * * Find a previously registered allocator with @name. When @name is NULL, the * default allocator will be returned. * - * Returns: a #GstAllocator or NULL when the allocator with @name was not - * registered. + * Returns: (transfer full): a #GstAllocator or NULL when the allocator with @name was not + * registered. Use gst_allocator_unref() to release the allocator after usage. */ -const GstAllocator * +GstAllocator * gst_allocator_find (const gchar * name) { - const GstAllocator *allocator; + GstAllocator *allocator; - g_static_rw_lock_reader_lock (&lock); + g_rw_lock_reader_lock (&lock); if (name) { allocator = g_hash_table_lookup (allocators, (gconstpointer) name); } else { allocator = _default_allocator; } - g_static_rw_lock_reader_unlock (&lock); + if (allocator) + gst_allocator_ref (allocator); + g_rw_lock_reader_unlock (&lock); return allocator; } /** * gst_allocator_set_default: - * @allocator: a #GstAllocator + * @allocator: (transfer full): a #GstAllocator * - * Set the default allocator. + * Set the default allocator. This function takes ownership of @allocator. */ void -gst_allocator_set_default (const GstAllocator * allocator) +gst_allocator_set_default (GstAllocator * allocator) { + GstAllocator *old; g_return_if_fail (allocator != NULL); - g_static_rw_lock_writer_lock (&lock); + g_rw_lock_writer_lock (&lock); + old = _default_allocator; _default_allocator = allocator; - g_static_rw_lock_writer_unlock (&lock); + g_rw_lock_writer_unlock (&lock); + + if (old) + gst_allocator_unref (old); +} + +/** + * gst_allocation_params_init: + * @params: a #GstAllocationParams + * + * Initialize @params to its default values + */ +void +gst_allocation_params_init (GstAllocationParams * params) +{ + g_return_if_fail (params != NULL); + + memset (params, 0, sizeof (GstAllocationParams)); +} + +/** + * gst_allocation_params_copy: + * @params: (transfer none): a #GstAllocationParams + * + * Create a copy of @params. + * + * Free-function: gst_allocation_params_free + * + * Returns: (transfer full): a new ##GstAllocationParams, free with + * gst_allocation_params_free(). + */ +GstAllocationParams * +gst_allocation_params_copy (const GstAllocationParams * params) +{ + GstAllocationParams *result = NULL; + + if (params) { + result = + (GstAllocationParams *) g_slice_copy (sizeof (GstAllocationParams), + params); + } + return result; +} + +/** + * gst_allocation_params_free: + * @params: (in) (transfer full): a #GstAllocationParams + * + * Free @params + */ +void +gst_allocation_params_free (GstAllocationParams * params) +{ + g_slice_free (GstAllocationParams, params); } /** * gst_allocator_alloc: - * @allocator: a #GstAllocator to use - * @maxsize: allocated size of @data - * @align: alignment for the data + * @allocator: (transfer none) (allow-none): a #GstAllocator to use + * @size: size of the visible memory area + * @params: (transfer none) (allow-none): optional parameters * * Use @allocator to allocate a new memory block with memory that is at least - * @maxsize big and has the given alignment. + * @size big. + * + * The optional @params can specify the prefix and padding for the memory. If + * NULL is passed, no flags, no extra prefix/padding and a default alignment is + * used. + * + * The prefix/padding will be filled with 0 if flags contains + * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively. * * When @allocator is NULL, the default allocator will be used. * - * @align is given as a bitmask so that @align + 1 equals the amount of bytes to - * align to. For example, to align to 8 bytes, use an alignment of 7. + * The alignment in @params is given as a bitmask so that @align + 1 equals + * the amount of bytes to align to. For example, to align to 8 bytes, + * use an alignment of 7. * - * Returns: a new #GstMemory. + * Returns: (transfer full): a new #GstMemory. */ GstMemory * -gst_allocator_alloc (const GstAllocator * allocator, gsize maxsize, gsize align) +gst_allocator_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) { - g_return_val_if_fail (((align + 1) & align) == 0, NULL); + GstMemory *mem; + static GstAllocationParams defparams = { 0, 0, 0, 0, }; + + if (params) { + g_return_val_if_fail (((params->align + 1) & params->align) == 0, NULL); + } else { + params = &defparams; + } if (allocator == NULL) allocator = _default_allocator; - return allocator->info.alloc (allocator, maxsize, align, - allocator->info.user_data); + mem = allocator->info.alloc (allocator, size, params, allocator->user_data); + +#ifndef GST_DISABLE_TRACE + _gst_alloc_trace_new (_gst_memory_trace, mem); +#endif + return mem; }