FFmpeg源码走读之内存管理模型

news/2024/7/10 21:52:45 标签: 音视频, FFmpeg, 内存管理, c语言

FFmpeg_0">1 FFmpeg内存管理原理

数据包管理过程中当数据转移到新的数据包时存在两种操作一种是数据包之间相互独立,当新创建一份数据包时,需要将原来的数据重新申请一个数据空间并且将数据拷贝到新的数据包中,具体过程如下图所示。这种数据包的管理优势是在于数据之间相互独立,不会存在数据干扰的问题,但是缺点也很明显就是消耗的内存大大增加,同时数据之间的拷贝也是耗时的。
在这里插入图片描述
另一种内存管理的方式是,只新增数据包对象,用于管理数据对象,对于数据本身采用同一个内存空间进行管理,当所有的内存引用为0时释放这片内存空间,具体如下图所示。这种方式的优势就是数据占用内存最小化,同时大大减少数据拷贝的耗时问题;缺点是增加了内存管理的难度。FFmpeg正是采用这种内存管理的方式进行数据包和数据帧的管理。
在这里插入图片描述

FFmpeg_5">2 FFmpeg内存管理实现

2.1 AVPacket实现

核心API功能
av_packet_alloc申请AVPacket
av_packet_free释放AVPacket
av_init_packet初始化AVPacket
av_new_packet申请AVBufferRef和AVBuffer数据空间,引用计数设置为1
av_buffer_ref新申请AVBufferRef,AVBuffer引用计数加一
av_buffer_unref释放AVBufferRef,AVBuffer引用计数减一

AVPacket 内存模型如下图所示,AVBuffer存放具体数据,AVBufferRef用于管理AVBuffer,AVPacket 是实际对外数据包体。
在这里插入图片描述
引用计数加一和引用计数减一一个逆过程,具体如下图所示,加一主要是增加AVBufferRef和引用计数;减一主要是释放AVBufferRef和引用计数减少。当引用计数减到0时,需要释放AVBuffer数据区。
在这里插入图片描述
源码走读如下所示:

//1 申请AVPacket内存,并初始化
AVPacket *av_packet_alloc(void)
{
	//1 分配AVPacket 内存
	//2 将AVPacket解引用
	av_packet_unref
	{
		//AVPacket 初始化
		av_init_packet
	}
}
//释放AVPacket内存,引用减1
void av_packet_free(AVPacket **pkt);
//初始化AVPacket
void av_init_packet(AVPacket *pkt);
//新申请AVPacket的数据空间
int av_new_packet(AVPacket *pkt, int size)
{
	//分配AVBufferRef内存空间
	packet_alloc
	{
		av_buffer_realloc
		{
			//分配AVBufferRef内存
			av_buffer_create
			{
				//先分配AVBuffer,并且注册释放回调函数,refcount引用计数加一
				//再分配AVBufferRef,并将指针与AVBuffer绑定
			}
		}
	}
}
//引用计数加一
AVBufferRef *av_buffer_ref(AVBufferRef *buf)
{
	//新分配一个AVBufferRef;将旧的AVBufferRef复制给它,同时引用计数加1
	*ret = *buf;//结构体赋值的方式,浅拷贝
}
//引用计数减一
void av_buffer_unref(AVBufferRef **buf)
{
	//AVBufferRef释放,同时AVBuffer计数减一,当计数为1时释放
	buffer_replace
	{
		//利用回调释放数据
		b->free
	}
}

2.2 AVFrame实现

核心API功能
av_frame_alloc申请AVFrame
av_frame_free释放AVFrame
av_frame_get_buffer申请AVBufferRef和AVFrame数据空间
av_frame_ref新申请AVBufferRef,AVFrame引用计数加一
av_frame_unref释放AVBufferRef,AVFrame引用计数减一
av_frame_move_refAVFrame转移引用计数

AVFrame实现原理与AVPacket 一致,都是利用AVBufferRef进行引用计数的管理,同时数据存储在AVBuffer中,只有保存一份,av_frame_ref负责将引用计数加一,av_frame_unref引用计数减一,当引用计数减到0后,进行数据释放。

FFmpeg_82">3 FFmpeg内存接口实践

利用qt进行FFmpeg环境构建,首先是下载FFmpeg依赖库和头文件,这里使用的是win32的已经编译好的FFmpeg库,ffmpeg-4.2.1-win32-dev头文件和库。
首先构建一个空的c项目,同时创建好需要测试的avpacket文件,在依赖项.pro文件需要添加对FFmpeg的依赖,包括两部分一个是头文件路径,第二是库路径。具体如下所示。
在这里插入图片描述

win32{
INCLUDEPATH += $$PWD/ffmpeg-4.2.1-win32-dev/include
LIBS += $$PWD/ffmpeg-4.2.1-win32-dev/lib/avformat.lib   \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/avcodec.lib    \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/avdevice.lib   \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/avfilter.lib   \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/avutil.lib     \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/postproc.lib   \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/swresample.lib \
        $$PWD/ffmpeg-4.2.1-win32-dev/lib/swscale.lib
}

注意ffmpeg-4.2.1-win32-dev文件放到项目的根路径如下所示。
在这里插入图片描述
接下来是测试FFmpeg是否可以正常使用,具体测试程序如下,获取FFmpeg的版本。
在这里插入图片描述
执行效果如下所示:
在这里插入图片描述
1 实现简单的内存分配和释放操作
需要注意的点:

  • av_packet_alloc内部会进行初始化,所以不需要再调用av_init_packet初始化;
  • av_packet_free内部会进行解引用,所以不需要再调用av_packet_unref;
  • av_init_packet一定不能在av_new_packet之后使用会造成内存泄漏
  • 对同一个AVPacket进行多次av_packet_ref而没有av_packet_unref会造成内存泄漏。

在这里插入图片描述
av_new_packet用于分配到:AVBufferRef引用管理,同时会分配数据部分AVBuffer,同时引用计数设置refcount为1.
在这里插入图片描述
经过av_packet_move_ref之后AVBufferRef整体被转移到pkt2中,同时会调用av_init_packet初始化pkt;
在这里插入图片描述
经过av_packet_clone之后会主动分配一个AVPacket因此不需要事先分配alloc一个;clone后pkt和pkt2数据区都是指向同一个AVFrame数据区。
在这里插入图片描述
经过av_packet_ref后,pkt2首先需要av_packet_alloc一个AVPacket,然后经过此函数后会分配AVBufferRef和将数据指向同一个AVFrame数据区。
在这里插入图片描述
AVFrame帧的操作与packet分配原理一致,使用方式也类似。主要包括几个步骤一个是av_frame_alloc分配一个AVFrame帧,然后稍微有点不同的是需要为帧进行初始化,然后来确认是视频帧还是音频帧。第二步是av_frame_get_buffer获取帧的数据区也就是AVBufferRef和AVBuffer这里有一个比较特殊的地方是这里预制了一个长度为8的AVBufferRef指针数组,主要是用于不同的数据存储格式不一样需要多个内存空间。最后是确保AVFrame是可写的,在进行数据操作。释放利用av_frame_free。
在这里插入图片描述
获取分配数据帧的需要设置相关的参数,帧的数据格式;如果是视频需要设置分辨率;如果是音频需要设置步长,通道数等参数;主要原因是分配数据时根据这些参数计算分配数据空间大小。
请添加图片描述


http://www.niftyadmin.cn/n/927436.html

相关文章

VLC-3.0(3.x) 版本 Ubuntu环境编译教程

VLC-3.0(3.x) 版本 Linux 环境编译教程 盼星星盼月亮,vlc 3.x 版本年初正式发布了。(Nightly 版本也从 3.x 更新至 4.x) 和以往版本一样,按照 wiki 的教程直接编译是不可能编译成功的 :&#…

一个老程序员写给换行业的朋友的信

Hi, 感谢你昨天的监考。这封信有点长,建议你耐心看完。抱歉,又要讨论下职业的问题了。作为『朋友』,我觉得有义务分析下现在的情况及可能采取的对策。这些话可能听着不太舒服,但是跳出『我』的思维,以旁观者身份分析下…

安装全新的 pytorch 1.0

新的pytorch 已经发布,在conda下安装1.0的命令如下: 支持cuda 10.0 版本 conda install pytorch torchvision cudatoolkit10.0 -c pytorch

使用google 免费的gpu学习人工智能

访问 https://colab.research.google.com/drive/ 创建一个 ipynb 文件,然后在菜单上的修改-笔记本设置 中,使用GPU 测试下: 如果你还不明白,可以看更进一步的教程: https://blog.csdn.net/qq_26690795/article/detail…

anaconda+vscode在显示虚拟环境上的bug修复

当我们安装anaconda和vscode,编写代码并按下ctrlf5后会发现,创建的一些虚拟环境显示不出来,无法切换。 经过几小时的折腾终于明白原因: 由于bug,vscode会少显示一个环境,所以你需要创建一个新的仅用来占位的环境&…

CSDN优雅打印代码,解决新版VIP广告干扰(2019.12.26测试通过)

在chrome中打开要打印的csdn网页,按F12,选择控制 台(console), 复制以下代码并回车即可(注意把csdn的脚注去掉,纯js代码) (function(){ $("#side").remove(); $("#comment_title, #comment_list, #comm…

vscode中不能显示 anaconda的 多个env问题的解决办法

安装anaconda后,可能在vscode中不能显示创建的多个envs, 比如只能显示一个base 环境,此时,修改vscode的python配置即可: 菜单-> file->preference->settings, 然后点击右上角的:open settings, 加入以下内容…

解决vscode+anaconde+tensorflow 2.0 报 Failed to load the native TensorFlow runtime 的问题

anaconda下用conda 安装 tensorflow默认已经是2.0, 在vscode中使用时,会报下面的错 Failed to load the native TensorFlow runtime 还带有一堆不能import的错误,这其实是环境变量中没有设置好你的conda中的对应的tensorflow env的path环境变量导致的。…