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,flagsflags_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都等于传入的datasize变量。free是释放data的回调函数,如果没有传入会使用内部默认实现,最后调用av_free()。flags 可指定为AV_BUFFER_FLAG_READONLY表示只读。函数返回时AVBufferRef引用计数器是1

AVBufferRef、AVBuffer和AVBufferRef->data的三者关系

通过av_buffer_create()和av_buffer_alloc()可以看出AVBufferRef、AVBuffer和AVBufferRef->data的三者关系:

  1. AVBufferRef结构的第1个成员变量就是AVBuffer,同时他们没有强制分配在连续的内存空间。
  2. 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->databuf->buffer

4.5. int av_buffer_is_writable(const AVBufferRef *buf)

当使用者想要修改buf指向空间时需要使用本API,满足下面条件会返回0,否则返回1:

  1. buf->flags等于AV_BUFFER_FLAG_READONLY。
  2. buf->buffer的引用计数大于1也就是多个使用者。

4.6. int av_buffer_make_writable(AVBufferRef **pbuf)

pbuf不可写时重新申请一个AVBufferRef和其指向的AVBuffer、data空间,将pbuf->data数据复制到新的空间,最后让*pbuf等于这个新分配的AVBufferRef,完成一次克隆。需要注意的是,只针对默认的内存中分配data和默认的free释放API。