当前位置: 首页 > news >正文

FFmpeg5.0源码阅读——内存池AVBufferPool

摘要:FFmpeg中大多数数据存储比如AVFrame,AVPacket都是通过AVBufferRef管理的,而承载数据的结构为AVBuffer。本文主要通过FFmpeg源码来分析下FFmpeg中AVBuffer相关的实现。
关键字AVBufferAVBufferPoolAVBufferPool

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,对象本身不线程安全;
  • 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,当pdstsrc指向同一个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的指针节点,而是保存了opaquefree函数指针,因为有这两个值我们就可以很顺利的释放对应的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:线程安全用的锁;
  • opaquepool_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

摘要&#xff1a;FFmpeg中大多数数据存储比如AVFrame,AVPacket都是通过AVBufferRef管理的&#xff0c;而承载数据的结构为AVBuffer。本文主要通过FFmpeg源码来分析下FFmpeg中AVBuffer相关的实现。 关键字&#xff1a;AVBuffer、AVBufferPool、AVBufferPool 1. AVBufferRef 1.…...

Python学习------起步7(字符串的连接、删除、修改、查询与统计、类型判断及字符串字母大小写转换)

目录 前言&#xff1a; 1.字符串的连接 join() 函数 2.字符串的删除&取代 replace()函数 3.字符串的修改&切割 &#xff08;1&#xff09;strip() 函数 &#xff08;2&#xff09;lstrip()函数 和 rstrip()函数 &#xff08;3&#xff09;split()函数-->…...

雪花算法snowflake

snowflake中文的意思是 雪花&#xff0c;雪片&#xff0c;所以翻译成雪花算法。它最早是twitter内部使用的分布式环境下的唯一ID生成算法。在2014年开源。雪花算法产生的背景当然是twitter高并发环境下对唯一ID生成的需求&#xff0c;得益于twitter内部高超的技术&#xff0c;雪…...

Part 4 描述性统计分析(占比 10%)——上

文章目录【后续会持续更新CDA Level I&II备考相关内容&#xff0c;敬请期待】【考试大纲】【考试内容】【备考资料】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&#xff1a;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(…...

信息安全与网络安全有什么区别?

生活中我们经常会听到要保障自己的或者企业的信息安全。那到底什么是信息安全呢&#xff1f;信息安全包含哪些内容&#xff1f;与网络安全又有什么区别呢&#xff1f;今天我们就一起来详细了解一下。什么叫做信息安全&#xff1f;信息安全定义如下&#xff1a;为数据处理系统建…...

花了5年时间,用过市面上95%的工具,终于找到这款万能报表工具

经常有粉丝问我有“哪个报表工具好用易上手&#xff1f;”或者是“有哪些适合绝大多数普通职场人的万能报表工具&#xff1f;” 从这里我大概总结出了大家选择报表工具最期望满足的3点&#xff1a; &#xff08;1&#xff09;简单易上手&#xff1a;也就是所谓的学习门槛要低…...

ESP32S3系列--SPI主机驱动详解(一)

一、目的SPI是一种串行同步接口&#xff0c;可用于与外围设备进行通信。ESP32S3自带4个SPI控制器外设&#xff0c;其中SPI0/SPI1内部专用,共用一组信号线,通过一个仲裁器访问外部Flash和PSRAM&#xff1b;SPI2/3各自使用一组信号线&#xff1b;开发者可以使用SPI2/3控制外部SPI…...

2023开工开学火热!远行的人们,把淘特箱包送上顶流

春暖花开&#xff0c;被疫情偷走的三年在今年开学季找补回来了。多个数据反馈&#xff0c;居民消费意愿大幅提升。在淘特上&#xff0c;开工开学节点就很是明显&#xff1a;1月30日以来&#xff0c;淘特箱包品类甚至远超2022年双11&#xff0c;成为开年“第一爆品”。与此同时&…...

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&#xff08;Performance Monito…...

Vue (2)

文章目录1. 模板语法1.1 插值语法1.2 指令语法2. 数据绑定3. 穿插 el 和 data 的两种写法4. MVVM 模型1. 模板语法 root 容器中的代码称为 vue 模板 1.1 插值语法 1.2 指令语法 图一 &#xff1a; 简写 &#xff1a; v-bind: 是可以简写成 &#xff1a; 的 总结 &#xff1a; …...

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、初始化的前置方法和后置方法&#xff08;重写&#xff09; 2.3、Spring启动类 2.4、执行结果 2.5、经典面试问题 3.总结 1.Bean的生命周期 1.1、概括 Spring中Bean的生命周期就是Bean在…...

如何将VirtualBox虚拟机转换到VMware中

转换前的准备 首先需要你找到你的virtualbox以及VM安装到哪个文件夹里了&#xff0c;需要将这两个文件夹添加进环境变量Path中。 如果你记不清了&#xff0c;可以用everything全局搜索一下“VBoxManage.exe’以及“vmware-vdiskmanager.exe”&#xff0c;看一眼这个程序放到哪…...

洞庭龙梦(开发技巧和结构理论集)

1、经验来源&#xff0c;单一获取方式。进行形态等级展示。唯一游戏系统经验来源。无主线和支线剧情。2、玩家使用流通货币&#xff08;充值货币&#xff09;&#xff0c;到玩家空间商城充值游戏&#xff0c;两人以上玩家进行游戏&#xff0c;掉落道具。交易系统游戏玩法&#…...

【23种设计模式】创建型模式详细介绍

前言 本文为 【23种设计模式】创建型模式详细介绍 相关内容介绍&#xff0c;下边具体将对单例模式&#xff0c;工厂方法模式&#xff0c;抽象工厂模式&#xff0c;建造者模式&#xff0c;原型模式&#xff0c;具体包括它们的特点与实现等进行详尽介绍~ &#x1f4cc;博主主页&…...

@Bean的处理流程,源码分析@Bean背后发生的事

文章目录写在前面关键类ConfigurationClassPostProcessor1、ConfigurationClassPostProcessor的注册2、ConfigurationClassPostProcessor的处理过程&#xff08;1&#xff09;parse方法中&#xff0c;Bean方法的处理&#xff08;2&#xff09;注册解析Bean标注的方法写在前面 …...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

【Java】Ajax 技术详解

文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...

Linux入门(十五)安装java安装tomcat安装dotnet安装mysql

安装java yum install java-17-openjdk-devel查找安装地址 update-alternatives --config java设置环境变量 vi /etc/profile #在文档后面追加 JAVA_HOME"通过查找安装地址命令显示的路径" #注意一定要加$PATH不然路径就只剩下新加的路径了&#xff0c;系统很多命…...