除了内存管理之外,数据的前后级流转也涉及到buffer管理. 个人觉得ffmpeg里面的buffer管理实现极为巧妙,也很值得借鉴.
概述
重要数据结构
AVBufferPool
在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.
- mutex 用于多线程资源保护
- pool 指向BufferPoolEntry链表的头,AVBufferPool可以管理所有的BufferPoolEntry.
- 在初始化AVBufferPool时pool是空,只有在av_buffer_pool_get()时才会判断
- 和创建.如图av_buffer_pool_get函数流程:
- refcount AVBufferPool的引用次数,初始化之后为1,av_buffer_pool_get()会+1,
- pool_release_buffer() av_buffer_pool_uninit()会-1. 为0时会buffer_pool_free
- 释放pool
- size 不是指缓存池的个数,而是指AVBuffer.size
- opaque 用户自定义需要传递给回调函数参数,对应pool_free回调函数的opaque参数
- allo alloc2 提供两种内存分配方式,1: 注册回调函数 2: av_buffer_alloc
- pool_free 注册回调函数,av_buffer_pool_init2()注册,用于指定一种更负责的内存分配器。
BufferPoolEntry
在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.
从数据结构中开看到BufferPoolEntry就是一个链表,AVBufferPool中存储了BufferPoolEntry根节点.
- data opaqu free 以上的BufferPoolEntry的opaque、data、free都是指向了AVBuffer最
- 开始的opaque、data、free,然后AVBuffer自己的opaque和free则指
- 向了BufferPoolEntry和pool_release_buffer
- pool 当前BufferPoolEntry指向创建他的AVBufferPool. 这样每个BufferPoolEntry都可
- 以找到创建和管理他的AVBufferPool
- next
- buffer 实际存储buffer相关信息,新版本才增加的
AVBufferRef
在libavutil/buffer.h中定义,是对外的,可以在应用程序中直接引用.
- *buffer
- *data size AVBufferRef.data和size与AVBuffer.data和size是一致的
有两种方式创建AVBufferRef,一种是通过av_buffer_alloc,只要指定大小即可;另一种是
通过av_buffer_create,这种主要是用于已经有了数据的情况,同时它也支持自定义释放此
数据内存的方法
AVBuffer
在libavutil/buffer_internal.h中定义,为内部数据结构,不能在应用程序中直接引用.
AVBuffer对外不公开,必须要通过AVBufferRef来间接使用AVBuffer.
- *data 实际存放数据的地址
- size 实际存放数据的大小
- refcount 记录当前buffer的引用计数
- free 回调函数用于释放数据,当引用计数变为0时会被调用来释放内存。
- opaque 用户自定义需要传递给回调函数参数,对应了回调函数的opaque参数
- flags 标识一些属性 , AV_BUFFER_FLAG_READONLY
- 表示只读,引用计数为1时表示只有一个对象引用它,此时是可写的,否则就是只读的
- flags_internal 标识一些buffer属性, 有BUFFER_FLAG_REALLOCATABLE, BUFFER_FLAG_NO_FREE两个。
- BUFFER_FLAG_REALLOCATABLE标识是否可以重分配
- BUFFER_FLAG_NO_FREE标识被其他结构体引用,不能释放.需要手动释放内存.
- 新版本中
- 1. BUFFER_FLAG_REALLOCATABLE移动到了flags_internal
- 2. BUFFER_FLAG_READONLY改为了AV_BUFFER_FLAG_READONLY
- 3. 新增加了BUFFER_FLAG_NO_FREE
AVBuffer在创建的时候引用计数为1,当调用av_buffer_ref()对其进行操作时,引用计数+1,当
av_buffer_unref对其操作时则引用计数-1(当-1后引用计数为0时,av_buffer_unref将自动释放分配的数据缓存
定义在libavutil/buffer.c中操作接口
Buffer管理
以上的BufferPoolEntry的opaque、data、free都是指向了AVBuffer最开始的opaque、data、free,
然后AVBuffer自己的opaque和free则指向了BufferPoolEntry和pool_release_buffer为何这样设计呢,
因为AVBuffer最终要进行释放的话,那还是得调用它自己最本来的free函数,但是此时由于要放到
缓存池管理,因此在free时不能真正把AVBuffer释放了,因此用BufferPoolEntry来保存AVBuffer真正
的释放内存的函数,然后再用缓存池释放函数pool_release_buffer来代替free函数,这样,当用户
释放的时候,AVBuffer的free函数已经指向了pool_release_buffer函数,因此可以在pool_release_buffer
里把AVBuffer返回给缓存池,等到缓存池自己想要被释放的时候,这个时候缓存池就从BufferPoolEntry
把之前保存的真正释放AVBuffer的函数取出来,进行调用;不得不说,这个设计虽然非常绕,其实很巧妙.
如图:
- 365行,调用pool.alloc分配一个新的AVBufferRef
- 370行, 分配一个BufferPoolEntry
- 376-379行,将AVBufferRef的data, opaque, free赋值给BufferPoolEntry
- 381-382行,重新赋值AVBuffer.opaque和AVBuffer.free, pool_release_buffer来代替free函数,
- 用户释放的时并不会真正释放内存而是通过pool_release_buffer将AVBuffer返回
- 给了缓存池.当用于真正需要释放内存时,再调用BufferPoolEntry.free实现真正意义释放.
- 以上机制对用户不可见,巧妙将内存释放和缓存池回收buffer结合在一起了.
粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓