FFmpeg5.0源码阅读——内存池AVBufferPool
摘要:FFmpeg中大多数数据存储比如AVFrame,AVPacket都是通过AVBufferRef管理的,而承载数据的结构为AVBuffer。本文主要通过FFmpeg源码来分析下FFmpeg中AVBuffer相关的实现。
关键字:AVBuffer、AVBufferPool、AVBufferPool
1. AVBufferRef
1.1 AVBuffer结构定义
AVBuffer声明在libavutil/buffer_internal.h文件中,而相关的操作函数定义在libavutil/buffer.c中。先简单看下AVBuffer的结构:
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 datavoid *opaque;//an opaque pointer, to be used by the freeing callbackint flags;//A combination of AV_BUFFER_FLAG_*int flags_internal;//A combination of BUFFER_FLAG_*
};
该结构比较简单,就是一个含有引用计数的数据类型:
data:buffer中的数据指针;size:数据的大小,即data中数据的大小;refcount:引用计数,无需多说,当引用计数为0时销毁对应的内存。该变量的操作是原子的,ffmpeg内部针对不同的编译期和平台实现了一套源自变量,具体就深入了,理解意思就行;free:释放内存的函数指针,如果不指定的话会使用默认的函数指针av_buffer_default_free释放内存;opaque:user-defined的指针,用户可以通过该指针将数据传递给free函数;flags:目前只有一个值AV_BUFFER_FLAG_READONLY;flags_internal:目前只有一个值BUFFER_FLAG_REALLOCATABLE;
1.2 AVBufferRef结构定义
AVBufferRef可以看做AVBuffer的一个句柄,用来操作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结构比较简单,不详细描述,主要注意data字段是指向其成员buffer.data的。
1.3 操作函数
AVBufferRef *av_buffer_create(uint8_t *data, size_t size, void (*free)(void *opaque, uint8_t *data), void *opaque, int flags):该函数用来创建一个AVBufferRef,具体就是申请内存函数根据参数初始化各个成员。需要注意的是返回的指针和其成员buffer是在堆上的,以及AVBuferRef::data == AVBufferRef::buffer::data;AVBufferRef *av_buffer_alloc(size_t size):通过av_buffer_create创建对象,只不过参数都是默认值;AVBufferRef *av_buffer_allocz(size_t size):相比av_buffer_alloc只是对内存进行了0初始化;AVBufferRef *av_buffer_ref(AVBufferRef *buf):FFmpeg中以_ref结尾的API都是引用计数+1的含义,相反_unref就是引用计数-1。但是需要注意两点:- 这里不是单纯的引用计数+1,而是
malloc了一个AVBufferRef作为返回值,然后浅拷贝输入参数; - 仅仅引用计数是原子的,类似
shared_ptr,对象本身不线程安全;
- 这里不是单纯的引用计数+1,而是
void av_buffer_unref(AVBufferRef **buf):引用计数-1,释放内存,调用free释放data内存;int av_buffer_is_writable(const AVBufferRef *buf):当flags设置了AV_BUFFER_FLAG_READONLY时始终不可写,否则只有引用计数为1时才可写;int av_buffer_make_writable(AVBufferRef **pbuf):实现就是copy-on-write,将pbuf复制一份避免写共享的内存影响其他对象;int av_buffer_realloc(AVBufferRef **pbuf, size_t size):重新申请内存,如果传入的*pbuf为空则create一份。当输入的对象不可写或者不是BUFFER_FLAG_REALLOCATABLE时会拷贝一份再realloc;int av_buffer_replace(AVBufferRef **pdst, AVBufferRef *src):可以简单的理解就是*pds=*src,当pdst和src指向同一个buffer时,什么也不会做,实现类似C++中对象的拷贝构造函数;
2. AVBufferRef
2.1 结构定义
AVBufferPool是一个单链表,用来管理其中的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;
从结构定义中可以看到BufferPollEntry就是链表中的节点用来管理对应的AVBufferRef。但是仔细看又发现其中并没有AVBuffer的指针节点,而是保存了opaque和free函数指针,因为有这两个值我们就可以很顺利的释放对应的AVBuffer,而pool中又保存了对应的allocate的函数指针能够创建对象。
data:指向AVBuffer的地址,因为没有保存AVBuffer的地址所以需要一个指针来指向数据;opaque:实现中BufferPoolEntry::opaque->AVBuffer::opaque->BufferPoolEntry,这样能够保证通过AVBuffer调用释放函数时找到管理自己的handle;free:释放函数指针,实际上是固定的pool_release_buffer;pool:直接指向当前的内存池;next:链表的节点指针;
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就是内存池的管理对象:
mutex:线程安全用的锁;opaque:pool_free函数指针的第一个参数;alloc:默认会被设置成av_buffer_alloc;alloc2:自定义的分配函数,申请AVBufferRef时优先使用,没有指定则使用alloc;pool_free:释放内存池的回调;size:单个对象的大小,即整个内存池管理的对象大小是相同的;refcount:当前从内存池中分配但是并没有在内存池链表中的节点的引用计数之和。
2.2 接口实现
AVBufferPool *av_buffer_pool_init2(size_t size, void *opaque, AVBufferRef* (*alloc)(void *opaque, size_t size), void (*pool_free)(void *opaque)):初始化pool的链表,根据参数设置相应的成员,alloc2会设置输入的参数alloc,而-alloc会设置成av_buffer_alloc;AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size)):只会申请pool的内存设置相关参数,如果alloc为空则pool中的alloc设置为av_buffer_alloc;void av_buffer_pool_uninit(AVBufferPool **ppool):销毁pool,如果引用计数为1则销毁对象(不知道为什么命名没有类似_unref,可能因为没有ref吧);AVBufferRef *av_buffer_pool_get(AVBufferPool *pool):获取一个AVBufferRef该内存是通过pool管理的。
2.3 内存管理
AVBufferPool是一个以单链表形式实现的栈式内存池。其基本过程就是如果链表非空则出栈头结点,否则申请内存时就创建一个AVBUfferRef返回给用户,用户释放时就会将节点入栈到头结点,并且申请和释放内存是线程安全的。AVBufferPool就是一个空闲链表栈,通过指定对应的AVBufferRef的释放函数为pool_release_buffer来对内存进行管理。
对于一个刚初始化的内存池,连续申请两个Buffer就是下面这种状态:

连续申请3个buffer,再释放2个就是下面这种状态(红色为链表的连接线):

相关文章:
FFmpeg5.0源码阅读——内存池AVBufferPool
摘要:FFmpeg中大多数数据存储比如AVFrame,AVPacket都是通过AVBufferRef管理的,而承载数据的结构为AVBuffer。本文主要通过FFmpeg源码来分析下FFmpeg中AVBuffer相关的实现。 关键字:AVBuffer、AVBufferPool、AVBufferPool 1. AVBufferRef 1.…...
Python学习------起步7(字符串的连接、删除、修改、查询与统计、类型判断及字符串字母大小写转换)
目录 前言: 1.字符串的连接 join() 函数 2.字符串的删除&取代 replace()函数 3.字符串的修改&切割 (1)strip() 函数 (2)lstrip()函数 和 rstrip()函数 (3)split()函数-->…...
雪花算法snowflake
snowflake中文的意思是 雪花,雪片,所以翻译成雪花算法。它最早是twitter内部使用的分布式环境下的唯一ID生成算法。在2014年开源。雪花算法产生的背景当然是twitter高并发环境下对唯一ID生成的需求,得益于twitter内部高超的技术,雪…...
Part 4 描述性统计分析(占比 10%)——上
文章目录【后续会持续更新CDA Level I&II备考相关内容,敬请期待】【考试大纲】【考试内容】【备考资料】1、统计基本概念1.1、统计学的含义及应用1.1.1、统计学的含义1.2.1、统计学的应用1.2、统计学的基本概念1.2.1、数据及数据的分类1.2.2、总体和样本1.2.3、…...
Linux系统安全:安全技术和防火墙
目录 一、安全技术 1、安全技术 2、防火墙分类 二、防火墙 1、iptables五表五链 2、黑白名单 3、iptables基本语法 4、iptables选项 5、控制类型 6、隐藏扩展模块 7、显示扩展模块 8、iptables规则保存 9、自定义链使用 一、安全技术 1、安全技术 ①入侵检测系统…...
【干货】Python:turtle库的用法
【干货】Python:turtle库的用法1. turtle库概述2. turtle库与基本绘图2.1 导入库的三种方式2.1.12.1.22.1.32.2 窗体函数2.2 画笔状态函数2.2.1 seed(s)2.2.2 random()2.2.3 randint(a, b)2.2.4 getrandbits(k)2.2.5 randrange(start, stop[ , step])2.2.6 uniform(…...
信息安全与网络安全有什么区别?
生活中我们经常会听到要保障自己的或者企业的信息安全。那到底什么是信息安全呢?信息安全包含哪些内容?与网络安全又有什么区别呢?今天我们就一起来详细了解一下。什么叫做信息安全?信息安全定义如下:为数据处理系统建…...
花了5年时间,用过市面上95%的工具,终于找到这款万能报表工具
经常有粉丝问我有“哪个报表工具好用易上手?”或者是“有哪些适合绝大多数普通职场人的万能报表工具?” 从这里我大概总结出了大家选择报表工具最期望满足的3点: (1)简单易上手:也就是所谓的学习门槛要低…...
ESP32S3系列--SPI主机驱动详解(一)
一、目的SPI是一种串行同步接口,可用于与外围设备进行通信。ESP32S3自带4个SPI控制器外设,其中SPI0/SPI1内部专用,共用一组信号线,通过一个仲裁器访问外部Flash和PSRAM;SPI2/3各自使用一组信号线;开发者可以使用SPI2/3控制外部SPI…...
2023开工开学火热!远行的人们,把淘特箱包送上顶流
春暖花开,被疫情偷走的三年在今年开学季找补回来了。多个数据反馈,居民消费意愿大幅提升。在淘特上,开工开学节点就很是明显:1月30日以来,淘特箱包品类甚至远超2022年双11,成为开年“第一爆品”。与此同时&…...
Intel x86_64 PMU简介
文章目录前言一、性能监控概述二、CPUID information三、架构性能监控3.1 架构性能监控 Version 13.1.1 架构性能监控 Version 1 Facilities3.1.2 预定义的体系结构性能事件3.1.3 cmask demo测试参考资料前言 Intel 64 和 IA-32 架构提供了 PMU(Performance Monito…...
Vue (2)
文章目录1. 模板语法1.1 插值语法1.2 指令语法2. 数据绑定3. 穿插 el 和 data 的两种写法4. MVVM 模型1. 模板语法 root 容器中的代码称为 vue 模板 1.1 插值语法 1.2 指令语法 图一 : 简写 : v-bind: 是可以简写成 : 的 总结 : …...
ESP8266 + STC15基于AT指令通过TCP通讯协议获取时间
ESP8266 + STC15基于AT指令通过TCP通讯协议获取时间 如果纯粹拿32位的ESP8266模块给8位的单片机仅供授时工具使用,有点大材小用了。这里不讨论这个拿esp8266来单独开发使用。本案例只是通过学习esp8266 AT指令功能来验证方案的可行性。 🔖STC15 单片机采用的是:STC15F2K60S…...
谈谈Spring中Bean的生命周期?(让你瞬间通透~)
目录 1.Bean的生命周期 1.1、概括 1.2、图解 2、代码示例 2.1、初始化代码 2.2、初始化的前置方法和后置方法(重写) 2.3、Spring启动类 2.4、执行结果 2.5、经典面试问题 3.总结 1.Bean的生命周期 1.1、概括 Spring中Bean的生命周期就是Bean在…...
如何将VirtualBox虚拟机转换到VMware中
转换前的准备 首先需要你找到你的virtualbox以及VM安装到哪个文件夹里了,需要将这两个文件夹添加进环境变量Path中。 如果你记不清了,可以用everything全局搜索一下“VBoxManage.exe’以及“vmware-vdiskmanager.exe”,看一眼这个程序放到哪…...
洞庭龙梦(开发技巧和结构理论集)
1、经验来源,单一获取方式。进行形态等级展示。唯一游戏系统经验来源。无主线和支线剧情。2、玩家使用流通货币(充值货币),到玩家空间商城充值游戏,两人以上玩家进行游戏,掉落道具。交易系统游戏玩法&#…...
【23种设计模式】创建型模式详细介绍
前言 本文为 【23种设计模式】创建型模式详细介绍 相关内容介绍,下边具体将对单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,具体包括它们的特点与实现等进行详尽介绍~ 📌博主主页&…...
@Bean的处理流程,源码分析@Bean背后发生的事
文章目录写在前面关键类ConfigurationClassPostProcessor1、ConfigurationClassPostProcessor的注册2、ConfigurationClassPostProcessor的处理过程(1)parse方法中,Bean方法的处理(2)注册解析Bean标注的方法写在前面 …...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
