diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2013-03-08 16:05:59 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2013-03-08 16:06:20 +0100 |
commit | 532f31a695c9530ce67a847be00d72e6e8acfd11 (patch) | |
tree | aa78d9b5840629c23ab2aaf94079930eca8e8c02 /libavutil/buffer.c | |
parent | 36099df521b5e26403b3379f8ffafdfb7b9e96c7 (diff) | |
parent | 1cec0624d0e6f48590283a57169b58b9fe8449d3 (diff) | |
download | ffmpeg-532f31a695c9530ce67a847be00d72e6e8acfd11.tar.gz |
Merge commit '1cec0624d0e6f48590283a57169b58b9fe8449d3'
* commit '1cec0624d0e6f48590283a57169b58b9fe8449d3':
AVBuffer: add a new API for buffer pools
Merged-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavutil/buffer.c')
-rw-r--r-- | libavutil/buffer.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/libavutil/buffer.c b/libavutil/buffer.c index 719f4c25a6..21e102d8e0 100644 --- a/libavutil/buffer.c +++ b/libavutil/buffer.c @@ -192,3 +192,146 @@ int av_buffer_realloc(AVBufferRef **pbuf, int size) buf->buffer->size = buf->size = size; 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; +} |