- 1. 参考
- 2. 背景
- 3. 结构体定义
- 4. API
- 4.1. AVBufferRef *av_buffer_alloc(size)
- 4.2. AVBufferRef av_buffer_create(uint8_t *data, buffer_size_t size, void (free)(void *opaque, uint8_t *data), void *opaque, int flags)
- 4.3. AVBufferRef *av_buffer_ref(AVBufferRef *buf)
- 4.4. void av_buffer_unref(AVBufferRef *buf)
- 4.5. int av_buffer_is_writable(const AVBufferRef *buf)
- 4.6. int av_buffer_make_writable(AVBufferRef **pbuf)
1. 参考
2. 背景
翻FFMPEG代码时经常看到AVBufferRef,压缩数据AVPacket和未压缩数据AVFrame都用它记录实际数据,在不熟悉代码情况下, 要分析数据在生产、使用、消费等各个环节的异常,比如RTSP视频硬件解码,往往可以通过搜索关键数据的流转关系,找到各个环节分别由谁分配、谁使用、谁释放,从而在脑海中完成一条输入、处理、输出的拼图。
3. 结构体定义
3.1. AVBufferRef定义在libavutil/buffer.h,她引用了下面AVBuffer
1
2
3
4
5
typedef struct AVBufferRef {
AVBuffer *buffer;
uint8_t *data;
size_t size;
} AVBufferRef;
buffer.h中对外API基于该结构并公开定义,而AVBuffer则对外隐藏。AVBufferRef的data变量值等于buffer->data,size也一样,所以AVBufferRef是一个AVBuffer的引用。
3.2. AVBuffer定义在libavutil/buffer_internal.h
1
2
3
4
5
6
7
8
9
struct AVBuffer {
uint8_t *data; /**< data described by this buffer */
buffer_size_t size; /**< size of data in bytes */
atomic_uint refcount;
void (*free)(void *opaque, uint8_t *data);
void *opaque;
int flags;
int flags_internal;
};
AVBuffer中refcount是一个原子变量,保障线程安全。FFMPEG中有一组atomic_开头的API,提供跨平台的原子操作。
free指针标记data空间的释放函数,虽然大多数时候内存管理都是标准的malloc/free函数,但对于特定场合,可以自行申请data空间,比如来自显存,又或者为了更好的回收相关资源,此时就可以传入特定的释放API。
opaque是一个free时的userdata,flags和flags_internal标记结构特性。
4. API
4.1. AVBufferRef *av_buffer_alloc(size)
通过av_malloc申请指定长度的内存空间,并通过av_buffer_create传入该空间、空间长度、释放data的函数指针,来创建AVBufferRef并返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AVBufferRef *av_buffer_alloc(buffer_size_t size)
{
AVBufferRef *ret = NULL;
uint8_t *data = NULL;
data = av_malloc(size);
if (!data)
return NULL;
ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
if (!ret)
av_freep(&data);
return ret;
}
4.2. AVBufferRef av_buffer_create(uint8_t *data, buffer_size_t size, void (free)(void *opaque, uint8_t *data), void *opaque, int flags)
使用av_malloc*系列函数创建AVBuffer和AVBufferRef,他们的data、size都等于传入的data和size变量。free是释放data的回调函数,如果没有传入会使用内部默认实现,最后调用av_free()。flags 可指定为AV_BUFFER_FLAG_READONLY表示只读。函数返回时AVBufferRef引用计数器是1。
通过av_buffer_create()和av_buffer_alloc()可以看出AVBufferRef、AVBuffer和AVBufferRef->data的三者关系:
- AVBufferRef结构的第1个成员变量就是AVBuffer,同时他们没有强制分配在连续的内存空间。
- Data可以在内存,也可以在其他设备地址空间。
4.3. AVBufferRef *av_buffer_ref(AVBufferRef *buf)
常常和av_buffer_unref()一起使用,每次av_buffer_ref()都会在内存中分配一个新的AVBufferRef,各个成员变量的值完全等于buf,并通过原子操作API给buf->buffer记录的AVBuffer引用计数器加1。如果最后没有成对的av_buffer_unref()调用会产生泄露。
4.4. void av_buffer_unref(AVBufferRef *buf)
释放buf,并当buf->buffer指向的AVBuffer引用计数为1也就是最后一个时,释放buf->buffer->data和buf->buffer。
4.5. int av_buffer_is_writable(const AVBufferRef *buf)
当使用者想要修改buf指向空间时需要使用本API,满足下面条件会返回0,否则返回1:
- buf->flags等于AV_BUFFER_FLAG_READONLY。
- buf->buffer的引用计数大于1也就是多个使用者。
4.6. int av_buffer_make_writable(AVBufferRef **pbuf)
当pbuf不可写时重新申请一个AVBufferRef和其指向的AVBuffer、data空间,将pbuf->data数据复制到新的空间,最后让*pbuf等于这个新分配的AVBufferRef,完成一次克隆。需要注意的是,只针对默认的内存中分配data和默认的free释放API。