Abstract : Most of the data storage in FFmpeg, for example AVFrame
, AVPacket
is AVBufferRef
managed, and the structure of the carrying data is AVBuffer
. AVBuffer
This article mainly analyzes the related implementation in FFmpeg through FFmpeg source code .
Keywords : AVBuffer
, AVBufferPool
,AVBufferPool
1. AVBufferRef
1.1 AVBuffer
Structure Definition
AVBuffer
The declaration is in libavutil/buffer_internal.h
the file, and the related operation functions are defined in libavutil/buffer.c
. First look at AVBuffer
the structure briefly:
struct AVBuffer {
uint8_t *data; /**< data described by this buffer */
size_t size; /**< size of data in bytes */
atomic_uint refcount; //number of existing AVBufferRef instances referring to this **buffer**
void (*free)(void *opaque, uint8_t *data);//a callback for freeing the data
void *opaque;//an opaque pointer, to be used by the freeing callback
int flags;//A combination of AV_BUFFER_FLAG_*
int flags_internal;//A combination of BUFFER_FLAG_*
};
The structure is relatively simple, it is a data type with a reference count:
data
: Data pointer in buffer;size
: The size of the data, that is,data
the size of the data in the middle;refcount
: Reference counting, needless to say, when the reference count is 0, the corresponding memory is destroyed. The operation of this variable is atomic. ffmpeg internally implements a set of derived variables for different compilation periods and platforms. The specifics are in-depth, just understand the meaning;free
: The function pointer to release the memory, if not specified, the default function pointer will be usedav_buffer_default_free
to release the memory;opaque
: A user-defined pointer through which the user can pass data tofree
the function;flags
: currently only one valueAV_BUFFER_FLAG_READONLY
;flags_internal
: currently only one valueBUFFER_FLAG_REALLOCATABLE
;
1.2 AVBufferRef
Structure Definition
AVBufferRef
It can be regarded AVBuffer
as a handle to operate AVBuffer
:
typedef struct AVBufferRef {
AVBuffer *buffer;
/**
* The data buffer. It is considered writable if and only if
* this is the only reference to the buffer, in which case
* av_buffer_is_writable() returns 1.
*/
uint8_t *data;
size_t size;//Size of data in bytes.
} AVBufferRef;
AVBufferRef
The structure is relatively simple and will not be described in detail. The main attention data
is that the fields point to its members buffer.data
.
1.3 Operation function
AVBufferRef *av_buffer_create(uint8_t *data, size_t size, void (*free)(void *opaque, uint8_t *data), void *opaque, int flags)
: This function is used to create oneAVBufferRef
, specifically, the application memory function initializes each member according to the parameters. Note that the returned pointer and its membersbuffer
are on the heap, andAVBuferRef::data == AVBufferRef::buffer::data
;AVBufferRef *av_buffer_alloc(size_t size)
: Byav_buffer_create
creating an object, but the parameters are all default values;AVBufferRef *av_buffer_allocz(size_t size)
: compared toav_buffer_alloc
just 0-initialization of memory;AVBufferRef *av_buffer_ref(AVBufferRef *buf)
: The API ending in FFmpeg_ref
is the meaning of reference count +1, and the opposite_unref
is reference count -1. But you need to pay attention to two points:- Here is not a simple reference count + 1, but
malloc
aAVBufferRef
return value, and then shallow copy the input parameters; - Only the reference count is atomic, similarly
shared_ptr
, the object itself is not thread-safe;
- Here is not a simple reference count + 1, but
void av_buffer_unref(AVBufferRef **buf)
: Reference count -1, release memory, callfree
releasedata
memory;int av_buffer_is_writable(const AVBufferRef *buf)
: Whenflags
setAV_BUFFER_FLAG_READONLY
, it is always non-writable, otherwise it can only be written when the reference count is 1;int av_buffer_make_writable(AVBufferRef **pbuf)
: The implementation iscopy-on-write
topbuf
copy a copy to avoid writing shared memory from affecting other objects;int av_buffer_realloc(AVBufferRef **pbuf, size_t size)
: Re-apply for memory, if the input is*pbuf
empty, it will becreate
one copy. When the input object is not writable or not,BUFFER_FLAG_REALLOCATABLE
a copy will be maderealloc
;int av_buffer_replace(AVBufferRef **pdst, AVBufferRef *src)
: It can be simply understood that*pds=*src
whenpdst
andsrc
point to the same onebuffer
, nothing will be done, and the copy constructor similar to the object in C++ will be implemented;
2. AVBufferRef
2.1 Structure Definition
AVBufferPool
Is a singly linked list, used to manage it AVBuffer
.
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 *next;
} BufferPoolEntry;
It can be seen from the structure definition BufferPollEntry
that the nodes in the linked list are used to manage the corresponding AVBufferRef
. But if you look carefully, you find that there are no pointer nodes, but function pointers AVBuffer
are saved , because with these two values, we can release the corresponding ones smoothly , and the corresponding allocate function pointers can be created in the pool. object.opaque
free
AVBuffer
data
:AVBuffer
The address pointed to, because there is no savedAVBuffer
address, a pointer is needed to point to the data;opaque
: In the implementationBufferPoolEntry::opaque->AVBuffer::opaque->BufferPoolEntry
, this can ensure thatAVBuffer
the handle that manages itself can be found when calling the release function;free
: Release the function pointer, which is actually fixedpool_release_buffer
;pool
: Directly point to the current memory pool;next
: the node pointer of the linked list;
struct AVBufferPool {
AVMutex mutex;
BufferPoolEntry *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.
*/
atomic_uint refcount;
size_t size;
void *opaque;
AVBufferRef* (*alloc)(size_t size);
AVBufferRef* (*alloc2)(void *opaque, size_t size);
void (*pool_free)(void *opaque);
};
AVBufferPool
It is the management object of the memory pool:
mutex
: Lock for thread safety;opaque
:pool_free
the first parameter of the function pointer;alloc
: By default it will be set toav_buffer_alloc
;alloc2
: A custom allocation function, whichAVBufferRef
is used first when applying, and used if not specifiedalloc
;pool_free
: The callback to release the memory pool;size
: The size of a single object, that is, the size of objects managed by the entire memory pool is the same;refcount
: The sum of reference counts of nodes currently allocated from the memory pool but not in the memory pool linked list.
2.2 Interface implementation
AVBufferPool *av_buffer_pool_init2(size_t size, void *opaque, AVBufferRef* (*alloc)(void *opaque, size_t size), void (*pool_free)(void *opaque))
: Initialize the linked list of the pool, set the corresponding members according to the parameters,alloc2
the input parameters will be setalloc
, and -alloc
will be set toav_buffer_alloc
;AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size))
alloc
: It will only apply for the memory setting related parameters of the pool. If alloc is empty, the setting in the pool isav_buffer_alloc
;void av_buffer_pool_uninit(AVBufferPool **ppool)
: Destroy the pool, and destroy the object if the reference count is 1 (I don’t know why the naming is not similar_unref
, maybe because there is noref
);AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
: Get aAVBufferRef
memory that is managed by pool.
2.3 Memory Management
AVBufferPool
It is a stack memory pool implemented in the form of a singly linked list. The basic process is that if the linked list is not empty, the node at the head of the stack will be popped out; otherwise, a node will be created and returned AVBUfferRef
to the user when applying for memory, and the node will be pushed into the stack to the head node when the user releases it, and applying and releasing memory is thread-safe. AVBufferPool
It is a free linked list stack, which manages the memory by specifying the corresponding AVBufferRef
release function . For a memory pool that has just been initialized, applying for two Buffers consecutively is in the following state:pool_release_buffer
Continuously apply for 3 buffers, and release 2 more, which is the following state (red is the connecting line of the linked list):