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

这篇具有很好参考价值的文章主要介绍了FFmpeg源码走读之内存管理模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 FFmpeg内存管理原理

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

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 是实际对外数据包体。
FFmpeg源码走读之内存管理模型
引用计数加一和引用计数减一一个逆过程,具体如下图所示,加一主要是增加AVBufferRef和引用计数;减一主要是释放AVBufferRef和引用计数减少。当引用计数减到0时,需要释放AVBuffer数据区。
FFmpeg源码走读之内存管理模型
源码走读如下所示:

//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_ref AVFrame转移引用计数

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

3 FFmpeg内存接口实践

利用qt进行FFmpeg环境构建,首先是下载FFmpeg依赖库和头文件,这里使用的是win32的已经编译好的FFmpeg库,ffmpeg-4.2.1-win32-dev头文件和库。
首先构建一个空的c项目,同时创建好需要测试的avpacket文件,在依赖项.pro文件需要添加对FFmpeg的依赖,包括两部分一个是头文件路径,第二是库路径。具体如下所示。
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是否可以正常使用,具体测试程序如下,获取FFmpeg的版本。
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会造成内存泄漏。

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

到了这里,关于FFmpeg源码走读之内存管理模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Kafka源码走读】Admin接口的客户端与服务端的连接流程

    注:本文对应的kafka的源码的版本是trunk分支。写这篇文章的主要目的是当作自己阅读源码之后的笔记,写的有点凌乱,还望大佬们海涵,多谢! 最近在写一个Web版的kafka客户端工具,然后查看Kafka官网,发现想要与Server端建立连接,只需要执行 方法即可,但其内部是如何工作

    2024年02月16日
    浏览(47)
  • 【Zookeeper源码走读】第三章 服务器处理客户端请求的流程

    前一篇文章,已经大致介绍了Server的启动流程,在NIOServerCnxnFactory.start()方法中,启动了多个线程,其中就有接收socket报文的线程,代码如下: 注意这里,acceptThread是接收socket的线程(AcceptThread),acceptThread的初始化是在NIOServerCnxnFactory.configure()中实现的: NIOServerCnxnFactory.confi

    2024年02月02日
    浏览(52)
  • 【内存管理】flink内存管理(一):内存管理概述:flink主动管理内存原理、flink内存模型

    本节从整体使用的角度了解Flink如何实现对内存的积极管理,然后对比基于JVM带来的内存管理问题,介绍Flink如何抽象出合理内存模型,解决大规模场景下的内存使用问题。 在JVM上运行的系统,需要将数据存储到JVM堆内存中进行处理和运算,借助JVM提供的GC能力能够实现内存的

    2024年01月20日
    浏览(54)
  • 24.Netty源码之合理管理堆内存

    内存使用目标 •内存占用少(空间) •应用速度快(时间) 即多快好省 对 Java 而言:减少 Full GC 的 STW(Stop the world)时间 内存使用技巧 • 减少对象本身大小 md 例 1:用基本类型就不要用包装类。 例 2: 应该定义成类变量(静态变量)的不要定义为实例变量。 ​ •一个类 - 一个类变量

    2024年02月13日
    浏览(41)
  • 一个简单的MCU内存管理模块(附源码)

    现在非常多的的MCU性能都还不错,同时用户也会去扩展一些外部RAM,那么如何高效便捷的管理这些内存是一个重要话题。 今天给大家分享一份源码:基于无操作系统的STM32单片机开发,功能强大,可申请到地址空间连续的不同大小的内存空间,且用户接口简单,使用方便。

    2024年02月08日
    浏览(41)
  • 根据源码,模拟实现 RabbitMQ - 内存数据管理(4)

    目录 一、内存数据管理 1.1、需求分析 1.2、实现 MemoryDataCenter 类 1.2.1、ConcurrentHashMap 数据管理 1.2.2、封装交换机操作 1.2.3、封装队列操作 1.2.4、封装绑定操作 1.2.5、封装消息操作 1.2.6、封装未确认消息操作 1.2.7、封装恢复数据操作 当前已经使用 数据库 管理了 交换机、绑定

    2024年02月11日
    浏览(41)
  • 【Linux 内核源码分析】内存管理——Slab 分配器

    在Linux内核中,伙伴分配器是一种内存管理方式,以页为单位进行内存的管理和分配。但是在内核中,经常会面临结构体内存分配问题,而这些结构体的大小通常是小于一页的。如果使用伙伴分配器来分配这些小内存,将造成很大的内存浪费。因此,为了解决这个问题,Sun公

    2024年02月22日
    浏览(61)
  • 手把手教你FreeRTOS源码解析(一)——内存管理

    FreeRTOS中一共有5种内存分配的方法,分别在文件heap_1.c,heap_2.c, heap_3.c,heap_4.c,heap_5.c种。 虽然标准C库中的 malloc()和 free()也可以实现动态内存管理,但是它有以下缺陷: 1、在小型嵌入式系统种效率不高。 2、线程不安全。 3、具有不确定性,每次执行的时间不同。 4、会导致内

    2024年02月07日
    浏览(39)
  • 深入理解 Spark(四)Spark 内存管理模型

    Executor 进程作为一个 JVM 进程,其内存管理建立在 JVM 的内存管理之上,整个大致包含两种方式:堆内内存和堆外内存。 一个 Executor 当中的所有 Task 是共享堆内内存的。一个 Work 中的多个 Executor 中的多个 Task 是共享堆外内存的。 堆内内存和堆外内存 大数据领域两个比较常见

    2024年01月24日
    浏览(43)
  • Linux内存管理 | 四、物理地址空间设计模型

    我的圈子: 高级工程师聚集地 我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强企业! 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 前面几篇文章,主要讲解了虚拟内存空间的布局和管理,下面同步来聊聊物理内

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包