---
Improved the API so that the pool itself is refcounted, so the caller
safely uninit it at any time. The pool itself is then actually freed when
the last buffer is returned to it.
---
libavutil/buffer.c | 156 +++++++++++++++++++++++++++++++++++++++++++
libavutil/buffer.h | 75 +++++++++++++++++++++
libavutil/buffer_internal.h | 32 +++++++++
3 files changed, 263 insertions(+)
diff --git a/libavutil/buffer.c b/libavutil/buffer.c
index 44262f1..6fc19be 100644
--- a/libavutil/buffer.c
+++ b/libavutil/buffer.c
@@ -192,3 +192,159 @@ 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, *last = NULL;
+
+ cur = avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, NULL, NULL);
+ if (!cur)
+ return NULL;
+
+ while (cur != last) {
+ FFSWAP(BufferPoolEntry*, cur, last);
+ cur = avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, last,
NULL);
+ }
+
+ 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_alloc_pool(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;
+}
+
+AVBufferRef *av_buffer_allocz_pool(AVBufferPool *pool)
+{
+ AVBufferRef *ret = av_buffer_alloc_pool(pool);
+
+ if (!ret)
+ return NULL;
+
+ memset(ret->data, 0, ret->size);
+ return ret;
+}
diff --git a/libavutil/buffer.h b/libavutil/buffer.h
index 9b23bc3..d0b3db5 100644
--- a/libavutil/buffer.h
+++ b/libavutil/buffer.h
@@ -192,4 +192,79 @@ 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_alloc_pool()
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_alloc_pool() 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.
+ *
+ * @return a reference to the new buffer on success, NULL on error.
+ */
+AVBufferRef *av_buffer_alloc_pool(AVBufferPool *pool);
+
+/**
+ * Same as av_buffer_allocz_pool(), except the data in the returned buffer will
+ * be initialized to zero.
+ */
+AVBufferRef *av_buffer_allocz_pool(AVBufferPool *pool);
+
+/**
+ * @}
+ */
+
#endif /* AVUTIL_BUFFER_H */
diff --git a/libavutil/buffer_internal.h b/libavutil/buffer_internal.h
index 677c341..cce83c3 100644
--- a/libavutil/buffer_internal.h
+++ b/libavutil/buffer_internal.h
@@ -57,4 +57,36 @@ struct AVBuffer {
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 */
--
1.7.10.4
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel