---
 libavutil/buffer.c          |  182 +++++++++++++++++++++++++++++++++++++++++++
 libavutil/buffer.h          |   80 +++++++++++++++++++
 libavutil/buffer_internal.h |   21 +++++
 3 files changed, 283 insertions(+)

diff --git a/libavutil/buffer.c b/libavutil/buffer.c
index 44262f1..e6e2e2e 100644
--- a/libavutil/buffer.c
+++ b/libavutil/buffer.c
@@ -20,8 +20,10 @@
 #include <string.h>
 
 #include "atomic.h"
+#include "avassert.h"
 #include "buffer_internal.h"
 #include "common.h"
+#include "log.h"
 #include "mem.h"
 
 AVBufferRef *av_buffer_create(uint8_t *data, int size,
@@ -192,3 +194,183 @@ int av_buffer_realloc(AVBufferRef **pbuf, int size)
     buf->buffer->size = buf->size = size;
     return 0;
 }
+
+
+static const AVClass pool_class = {
+    .class_name = "AVBufferPool",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size))
+{
+    AVBufferPool *ret = av_mallocz(sizeof(*ret));
+
+    if (!ret)
+        return NULL;
+
+    ret->class = &pool_class;
+    ret->size  = size;
+    ret->alloc = alloc ? alloc : av_buffer_alloc;
+
+    return ret;
+}
+
+void av_buffer_pool_uninit(AVBufferPool **ppool)
+{
+    AVBufferPool *pool;
+
+    if (!ppool || !*ppool)
+        return;
+    pool = *ppool;
+
+    if (avpriv_atomic_int_get(&pool->nb_allocated) !=
+        avpriv_atomic_int_get(&pool->nb_in_pool)) {
+        /* better leak than crash */
+        av_log(pool, AV_LOG_ERROR, "%d buffers are still not released.\n",
+               pool->nb_allocated - pool->nb_in_pool);
+        return;
+    }
+
+    while (pool->pool) {
+        BufferPoolEntry *buf = pool->pool;
+        pool->pool = buf->next;
+
+        buf->free(buf->opaque, buf->data);
+        av_freep(&buf);
+    }
+
+    av_freep(ppool);
+}
+
+int av_buffer_pool_can_uninit(AVBufferPool *pool)
+{
+    return (avpriv_atomic_int_get(&pool->nb_allocated) ==
+            avpriv_atomic_int_get(&pool->nb_in_pool));
+}
+
+/* remove the whole buffer list from the pool and return it */
+static BufferPoolEntry *get_pool(AVBufferPool *pool)
+{
+    BufferPoolEntry *cur, *last = NULL;
+    int nb_removed = 1, nb_remaining;
+
+    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);
+    }
+
+    /* count the removed buffers and update the number of buffers in pool */
+    while (last->next) {
+        last = last->next;
+        nb_removed++;
+    }
+
+    nb_remaining = avpriv_atomic_int_add_and_fetch(&pool->nb_in_pool, 
-nb_removed);
+    av_assert0(nb_remaining >= 0);
+
+    return cur;
+}
+
+static void add_to_pool(BufferPoolEntry *buf)
+{
+    AVBufferPool *pool;
+    BufferPoolEntry *cur, *end = buf;
+    int nb_added = 1, av_unused(nb_total);
+
+    if (!buf)
+        return;
+    pool = buf->pool;
+
+    while (end->next) {
+        end = end->next;
+        nb_added++;
+    }
+
+    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;
+            nb_added++;
+        }
+    }
+
+    nb_total = avpriv_atomic_int_add_and_fetch(&pool->nb_in_pool, nb_added);
+    av_assert2(avpriv_atomic_int_add_and_fetch(&pool->nb_allocated) >= 
nb_total);
+}
+
+static void pool_release_buffer(void *opaque, uint8_t *data)
+{
+    BufferPoolEntry *buf = opaque;
+    add_to_pool(buf);
+}
+
+/* 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->nb_allocated, 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;
+    }
+    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..470b06e 100644
--- a/libavutil/buffer.h
+++ b/libavutil/buffer.h
@@ -192,4 +192,84 @@ 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.
+ *
+ * After all the buffers were returned to the pool (av_buffer_pool_can_uninit()
+ * can check whether this is true), the pool must be freed with
+ * av_buffer_pool_uninit(). At this point all the buffers stored in the pool
+ * will 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));
+
+/**
+ * Free all the buffers in the pool, then free the pool itself. This function
+ * may only be called once all allocated buffers were return to the pool.
+ *
+ * @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);
+
+/**
+ * @return 1 if all the allocated buffers were returned to the pool, so it can
+ * be freed. Return 0 otherwise.
+ */
+int av_buffer_pool_can_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..709b55b 100644
--- a/libavutil/buffer_internal.h
+++ b/libavutil/buffer_internal.h
@@ -22,6 +22,7 @@
 #include <stdint.h>
 
 #include "buffer.h"
+#include "log.h"
 
 /**
  * The buffer is always treated as read-only.
@@ -57,4 +58,24 @@ struct AVBuffer {
     int flags;
 };
 
+typedef struct BufferPoolEntry {
+    uint8_t *data;
+    void *opaque;
+    void (*free)(void *opaque, uint8_t *data);
+
+    AVBufferPool *pool;
+    struct BufferPoolEntry * volatile next;
+} BufferPoolEntry;
+
+struct AVBufferPool {
+    const AVClass *class;
+
+    BufferPoolEntry * volatile pool;
+    volatile int nb_allocated;
+    volatile int nb_in_pool;
+
+    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

Reply via email to