mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
AVBuffer: add a new API for buffer pools
This commit is contained in:
parent
8e401dbe90
commit
1cec0624d0
@ -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;
|
||||
}
|
||||
|
@ -190,6 +190,76 @@ int av_buffer_make_writable(AVBufferRef **buf);
|
||||
*/
|
||||
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_pool_get() 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_pool_get() 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.
|
||||
* This function may be called simultaneously from multiple threads.
|
||||
*
|
||||
* @return a reference to the new buffer on success, NULL on error.
|
||||
*/
|
||||
AVBufferRef *av_buffer_pool_get(AVBufferPool *pool);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user