---
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