aboutsummaryrefslogtreecommitdiffstats
path: root/libavutil/buffer.c
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2013-01-20 08:03:13 +0100
committerAnton Khirnov <anton@khirnov.net>2013-03-08 07:33:28 +0100
commit1cec0624d0e6f48590283a57169b58b9fe8449d3 (patch)
tree47cf0e154245654e523e9b29cda8f77fb89f9090 /libavutil/buffer.c
parent8e401dbe90cc77b1f3067a917d9fa48cefa3fcdb (diff)
downloadffmpeg-1cec0624d0e6f48590283a57169b58b9fe8449d3.tar.gz
AVBuffer: add a new API for buffer pools
Diffstat (limited to 'libavutil/buffer.c')
-rw-r--r--libavutil/buffer.c143
1 files changed, 143 insertions, 0 deletions
diff --git a/libavutil/buffer.c b/libavutil/buffer.c
index b5e92c9f14..2b38081e1d 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;
+}