* commit '1cec0624d0e6f48590283a57169b58b9fe8449d3': AVBuffer: add a new API for buffer pools Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n2.0
| @@ -192,3 +192,146 @@ int av_buffer_realloc(AVBufferRef **pbuf, int size) | |||||
| buf->buffer->size = buf->size = size; | buf->buffer->size = buf->size = size; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)) | |||||
| { | |||||
| AVBufferPool *pool = av_mallocz(sizeof(*pool)); | |||||
| if (!pool) | |||||
| return NULL; | |||||
| pool->size = size; | |||||
| pool->alloc = alloc ? alloc : av_buffer_alloc; | |||||
| avpriv_atomic_int_set(&pool->refcount, 1); | |||||
| return pool; | |||||
| } | |||||
| /* | |||||
| * This function gets called when the pool has been uninited and | |||||
| * all the buffers returned to it. | |||||
| */ | |||||
| static void buffer_pool_free(AVBufferPool *pool) | |||||
| { | |||||
| while (pool->pool) { | |||||
| BufferPoolEntry *buf = pool->pool; | |||||
| pool->pool = buf->next; | |||||
| buf->free(buf->opaque, buf->data); | |||||
| av_freep(&buf); | |||||
| } | |||||
| av_freep(&pool); | |||||
| } | |||||
| void av_buffer_pool_uninit(AVBufferPool **ppool) | |||||
| { | |||||
| AVBufferPool *pool; | |||||
| if (!ppool || !*ppool) | |||||
| return; | |||||
| pool = *ppool; | |||||
| *ppool = NULL; | |||||
| if (!avpriv_atomic_int_add_and_fetch(&pool->refcount, -1)) | |||||
| buffer_pool_free(pool); | |||||
| } | |||||
| /* remove the whole buffer list from the pool and return it */ | |||||
| static BufferPoolEntry *get_pool(AVBufferPool *pool) | |||||
| { | |||||
| BufferPoolEntry *cur = NULL, *last = NULL; | |||||
| do { | |||||
| FFSWAP(BufferPoolEntry*, cur, last); | |||||
| cur = avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, last, NULL); | |||||
| if (!cur) | |||||
| return NULL; | |||||
| } while (cur != last); | |||||
| return cur; | |||||
| } | |||||
| static void add_to_pool(BufferPoolEntry *buf) | |||||
| { | |||||
| AVBufferPool *pool; | |||||
| BufferPoolEntry *cur, *end = buf; | |||||
| if (!buf) | |||||
| return; | |||||
| pool = buf->pool; | |||||
| while (end->next) | |||||
| end = end->next; | |||||
| while ((cur = avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, NULL, buf))) { | |||||
| /* pool is not empty, retrieve it and append it to our list */ | |||||
| cur = get_pool(pool); | |||||
| end->next = cur; | |||||
| while (end->next) | |||||
| end = end->next; | |||||
| } | |||||
| } | |||||
| static void pool_release_buffer(void *opaque, uint8_t *data) | |||||
| { | |||||
| BufferPoolEntry *buf = opaque; | |||||
| AVBufferPool *pool = buf->pool; | |||||
| add_to_pool(buf); | |||||
| if (!avpriv_atomic_int_add_and_fetch(&pool->refcount, -1)) | |||||
| buffer_pool_free(pool); | |||||
| } | |||||
| /* allocate a new buffer and override its free() callback so that | |||||
| * it is returned to the pool on free */ | |||||
| static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) | |||||
| { | |||||
| BufferPoolEntry *buf; | |||||
| AVBufferRef *ret; | |||||
| ret = pool->alloc(pool->size); | |||||
| if (!ret) | |||||
| return NULL; | |||||
| buf = av_mallocz(sizeof(*buf)); | |||||
| if (!buf) { | |||||
| av_buffer_unref(&ret); | |||||
| return NULL; | |||||
| } | |||||
| buf->data = ret->buffer->data; | |||||
| buf->opaque = ret->buffer->opaque; | |||||
| buf->free = ret->buffer->free; | |||||
| buf->pool = pool; | |||||
| ret->buffer->opaque = buf; | |||||
| ret->buffer->free = pool_release_buffer; | |||||
| avpriv_atomic_int_add_and_fetch(&pool->refcount, 1); | |||||
| return ret; | |||||
| } | |||||
| AVBufferRef *av_buffer_pool_get(AVBufferPool *pool) | |||||
| { | |||||
| AVBufferRef *ret; | |||||
| BufferPoolEntry *buf; | |||||
| /* check whether the pool is empty */ | |||||
| buf = get_pool(pool); | |||||
| if (!buf) | |||||
| return pool_alloc_buffer(pool); | |||||
| /* keep the first entry, return the rest of the list to the pool */ | |||||
| add_to_pool(buf->next); | |||||
| buf->next = NULL; | |||||
| ret = av_buffer_create(buf->data, pool->size, pool_release_buffer, | |||||
| buf, 0); | |||||
| if (!ret) { | |||||
| add_to_pool(buf); | |||||
| return NULL; | |||||
| } | |||||
| avpriv_atomic_int_add_and_fetch(&pool->refcount, 1); | |||||
| return ret; | |||||
| } | |||||
| @@ -190,6 +190,76 @@ int av_buffer_make_writable(AVBufferRef **buf); | |||||
| */ | */ | ||||
| int av_buffer_realloc(AVBufferRef **buf, int size); | int av_buffer_realloc(AVBufferRef **buf, int size); | ||||
| /** | |||||
| * @} | |||||
| */ | |||||
| /** | |||||
| * @defgroup lavu_bufferpool AVBufferPool | |||||
| * @ingroup lavu_data | |||||
| * | |||||
| * @{ | |||||
| * AVBufferPool is an API for a lock-free thread-safe pool of AVBuffers. | |||||
| * | |||||
| * Frequently allocating and freeing large buffers may be slow. AVBufferPool is | |||||
| * meant to solve this in cases when the caller needs a set of buffers of the | |||||
| * same size (the most obvious use case being buffers for raw video or audio | |||||
| * frames). | |||||
| * | |||||
| * At the beginning, the user must call av_buffer_pool_init() to create the | |||||
| * buffer pool. Then whenever a buffer is needed, call av_buffer_pool_get() to | |||||
| * get a reference to a new buffer, similar to av_buffer_alloc(). This new | |||||
| * reference works in all aspects the same way as the one created by | |||||
| * av_buffer_alloc(). However, when the last reference to this buffer is | |||||
| * unreferenced, it is returned to the pool instead of being freed and will be | |||||
| * reused for subsequent av_buffer_pool_get() calls. | |||||
| * | |||||
| * When the caller is done with the pool and no longer needs to allocate any new | |||||
| * buffers, av_buffer_pool_uninit() must be called to mark the pool as freeable. | |||||
| * Once all the buffers are released, it will automatically be freed. | |||||
| * | |||||
| * Allocating and releasing buffers with this API is thread-safe as long as | |||||
| * either the default alloc callback is used, or the user-supplied one is | |||||
| * thread-safe. | |||||
| */ | |||||
| /** | |||||
| * The buffer pool. This structure is opaque and not meant to be accessed | |||||
| * directly. It is allocated with av_buffer_pool_init() and freed with | |||||
| * av_buffer_pool_uninit(). | |||||
| */ | |||||
| typedef struct AVBufferPool AVBufferPool; | |||||
| /** | |||||
| * Allocate and initialize a buffer pool. | |||||
| * | |||||
| * @param size size of each buffer in this pool | |||||
| * @param alloc a function that will be used to allocate new buffers when the | |||||
| * pool is empty. May be NULL, then the default allocator will be used | |||||
| * (av_buffer_alloc()). | |||||
| * @return newly created buffer pool on success, NULL on error. | |||||
| */ | |||||
| AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)); | |||||
| /** | |||||
| * Mark the pool as being available for freeing. It will actually be freed only | |||||
| * once all the allocated buffers associated with the pool are released. Thus it | |||||
| * is safe to call this function while some of the allocated buffers are still | |||||
| * in use. | |||||
| * | |||||
| * @param pool pointer to the pool to be freed. It will be set to NULL. | |||||
| * @see av_buffer_pool_can_uninit() | |||||
| */ | |||||
| void av_buffer_pool_uninit(AVBufferPool **pool); | |||||
| /** | |||||
| * Allocate a new AVBuffer, reusing an old buffer from the pool when available. | |||||
| * This function may be called simultaneously from multiple threads. | |||||
| * | |||||
| * @return a reference to the new buffer on success, NULL on error. | |||||
| */ | |||||
| AVBufferRef *av_buffer_pool_get(AVBufferPool *pool); | |||||
| /** | /** | ||||
| * @} | * @} | ||||
| */ | */ | ||||
| @@ -57,4 +57,36 @@ struct AVBuffer { | |||||
| int flags; | int flags; | ||||
| }; | }; | ||||
| typedef struct BufferPoolEntry { | |||||
| uint8_t *data; | |||||
| /* | |||||
| * Backups of the original opaque/free of the AVBuffer corresponding to | |||||
| * data. They will be used to free the buffer when the pool is freed. | |||||
| */ | |||||
| void *opaque; | |||||
| void (*free)(void *opaque, uint8_t *data); | |||||
| AVBufferPool *pool; | |||||
| struct BufferPoolEntry * volatile next; | |||||
| } BufferPoolEntry; | |||||
| struct AVBufferPool { | |||||
| BufferPoolEntry * volatile pool; | |||||
| /* | |||||
| * This is used to track when the pool is to be freed. | |||||
| * The pointer to the pool itself held by the caller is considered to | |||||
| * be one reference. Each buffer requested by the caller increases refcount | |||||
| * by one, returning the buffer to the pool decreases it by one. | |||||
| * refcount reaches zero when the buffer has been uninited AND all the | |||||
| * buffers have been released, then it's safe to free the pool and all | |||||
| * the buffers in it. | |||||
| */ | |||||
| volatile int refcount; | |||||
| int size; | |||||
| AVBufferRef* (*alloc)(int size); | |||||
| }; | |||||
| #endif /* AVUTIL_BUFFER_INTERNAL_H */ | #endif /* AVUTIL_BUFFER_INTERNAL_H */ | ||||