ffmpeg 内存模型

最近在学习ffmpeg,阅读了一些packet和frame关于内存操作的api。在此长话短说,只说核心点。

ffmpeg模型

AVFrame  表示编码前的原始数据帧,AVPacket 表示编码后的压缩数据包

问题:

(1)从av_read_frame读取到一个AVPacket后怎么放入队列?

(2)从avcodec_recevice_frame读取到一个AVFrame后又怎么放入队列?

一句话就是:通过std::move转移,既然是move那就有内存模型。从现有的Packet拷贝一个新Packet的时候,有两种情况:

• ①两个Packet的buf引用的是同一数据缓存空间,这时 候要注意数据缓存空间的释放问题;

• ②两个Packet的buf引用不同的数据缓存空间,每个 Packet都有数据缓存空间的copy;

avpacket和avframe设计

AVFrame 结构体代表一个音频或视频帧
• data:解码后的图像像素数据(音频采样数据)
• linesize:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小
• width, height:图像的宽高(只针对视频)
• key_frame:是否为关键帧(只针对视频) 。
• pict_type:帧类型(只针对视频) 。例如I, P, B
• sample_rate:音频采样率(只针对音频)
• nb_samples:该音频帧中包含的采样数量
• pts:显示时间戳
• channel_layout: 音频数据的通道布局,单声道音频只有一个声道,
即左右声道完全相同。立体声(双声道)音频有两个独立的声道,分别对应左声道和右声道。• format:对于视频帧:视频帧的像素格式,例如 AV_PIX_FMT_RGB24、AV_PIX_FMT_YUV420P 等。
对于音频帧:表示该音频帧的采样格式,例如 AV_SAMPLE_FMT_S16、AV_SAMPLE_FMT_FLT 等。AVPacket 结构体代表一个压缩的音频或视频帧
• pts:显示时间戳
• dts:解码时间戳
• data:压缩编码数据
• size:压缩编码数据大小
• pos:数据的偏移地址
• stream_index:所属的AVStream

常用API

AVPacket 

AVPacket *av_packet_alloc(void);   malloc分配AVPacket对象	这个时候buffer并没malloc
void av_packet_free(AVPacket **pkt);  释放AVPacket 和_alloc对应
void av_init_packet(AVPacket *pkt); 初始化AVPacket字段为null,不分配内存
int av_new_packet(AVPacket *pkt, int size); buff分配内存,并初始化pkt,引用计数初始化为1 所有不能再调用init了 导致buffer指针丢失
int av_packet_ref(AVPacket *dst, const AVPacket *src); 
增加引用计数 多次ref如果没有对应多次unref将会内存泄漏
void av_packet_unref(AVPacket *pkt); 					
清空pkt pack->buff 引用计数为0释放 并调用init
void av_packet_move_ref(AVPacket *dst, AVPacket *src);  
转移引用计数后init(src) 需要free(src)
AVPacket *av_packet_clone(const AVPacket *src); 	
等于 深拷贝 av_packet_alloc()+av_packet_ref()
*pkt2 = *pkt;  语义:  void av_packet_move_ref(pkt2, pkt);  


AVFrame

与avpacket使用类似

AVFrame *av_frame_alloc(void); 
分配AVFrame
int av_frame_get_buffer(AVFrame *frame, int align); 
根据AVFrame分配内存
void av_frame_free(AVFrame **frame); 
释放AVFrame
int av_frame_ref(AVFrame *dst, const AVFrame *src); 
增加引用计数
void av_frame_unref(AVFrame *frame); 减少引用计数
void av_frame_move_ref(AVFrame *dst, AVFrame *src); 
转移引用计数
AVFrame *av_frame_clone(const AVFrame *src);     
等于av_frame_alloc() + av_frame_ref()
int av_frame_make_writable(AVFrame *frame);  
用来确保一个 AVFrame 是独立且可以被安全地写入的。如果 AVFrame 只有一个引用,
那么直接返回 AVFrame 本身。如果 AVFrame 有多个引用,那么它会创建一个新的独立的AVFrame,并将数据拷贝到新的 AVFrame 中。
这样做的目的是为了防止多个引用同时修改 AVFrame 的数据缓冲区,从而引发数据竞争和崩溃问题。

图解 

avpacket使用坑点 内存泄漏 

多次ref调用

void av_packet_test5()
{AVPacket *pkt = NULL;AVPacket *pkt2 = NULL;int ret = 0;pkt = av_packet_alloc(); //ret = av_new_packet(pkt, MEM_ITEM_SIZE);memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);pkt2 = av_packet_alloc();   // 必须先allocav_packet_move_ref(pkt2, pkt); // av_packet_move_refav_packet_ref(pkt, pkt2);av_packet_ref(pkt, pkt2);     // 多次ref如果没有对应多次unref将会内存泄漏if(pkt->buf)        // 打印referenc-counted,必须保证传入的是有效指针{    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt->buf)); //3}if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针{    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt2->buf));//3}av_packet_unref(pkt);   // 将为2av_packet_unref(pkt);   // 做第二次是没有用的if(pkt->buf)printf("pkt->buf没有被置NULL\n"); elseprintf("pkt->buf已经被置NULL\n"); //执行if(pkt2->buf)        // 打印referenc-counted,必须保证传入的是有效指针{    printf("%s(%d) ref_count(pkt) = %d\n", __FUNCTION__, __LINE__,av_buffer_get_ref_count(pkt2->buf)); //2}av_packet_unref(pkt2); //1  而pkt和pkt2 都已经解除引用 但是引用计数还是1 这就导致buff内存泄漏了。av_packet_free(&pkt);  av_packet_free(&pkt2);
}

init乱用

void av_packet_test4()
{AVPacket *pkt = NULL;// av_packet_alloc()没有必要,因为av_packet_clone内部有调用 av_packet_allocAVPacket *pkt2 = NULL;int ret = 0;pkt = av_packet_alloc();ret = av_new_packet(pkt, MEM_ITEM_SIZE);memccpy(pkt->data, (void *)&av_packet_test1, 1, MEM_ITEM_SIZE);pkt2 = av_packet_clone(pkt); // av_packet_alloc()+av_packet_ref(), 调用该函数后,pkt和pkt2对应的buf引用计数变成2av_init_packet(pkt);  // 这里是故意去做init的操作,让这个函数出现内存泄漏av_packet_free(&pkt);   // pkt在调用av_init_packet后,对应的buff被置为NULL,在调用av_packet_free没法做引用计数-1的操作av_packet_free(&pkt2); // 触发引用计数变为1,但因为不是0,所以buff不会被释放,导致内存泄漏
}

avframe使用

void av_frame_test1()
{AVFrame *frame = NULL;int ret = 0;frame = av_frame_alloc();// 没有类似的AVPacket的av_new_packet的API// 1024 *2 * (16/8) =frame->nb_samples     = 1024; //AV_SAMPLE_FMT_S16P 非交错 AV_SAMPLE_FMT_S16 交错frame->format         = AV_SAMPLE_FMT_S16P;//AV_CH_LAYOUT_MONO 单声道 AV_CH_LAYOUT_STEREO 立体声frame->channel_layout = AV_CH_LAYOUT_STEREO; ret = av_frame_get_buffer(frame, 0);    // 根据格式分配内存{if(frame->buf && frame->buf[0])
36            printf("%s(%d) 1 frame->buf[0]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[0]->size);    //受frame->format等参数影响if(frame->buf && frame->buf[1])
38            printf("%s(%d) 1 frame->buf[1]->size = %d\n", __FUNCTION__, __LINE__, frame->buf[1]->size);    //受frame->format等参数影响}if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针
42      printf("%s(%d) ref_count1(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));// ref_count1(frame) = 1ret = av_frame_make_writable(frame);    // 当frame本身为空时不能make writableprintf("av_frame_make_writable ret = %d\n", ret);49    if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("%s(%d) ref_count2(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));av_frame_unref(frame);if(frame->buf && frame->buf[0])        // 打印referenc-counted,必须保证传入的是有效指针printf("%s(%d) ref_count3(frame) = %d\n", __FUNCTION__, __LINE__, av_buffer_get_ref_count(frame->buf[0]));av_frame_free(&frame);
}
av_frame_test1(36) 1 frame->buf[0]->size = 2048
av_frame_test1(38) 1 frame->buf[1]->size = 2048
av_frame_test1(42) ref_count1(frame) = 1
av_frame_make_writable ret = 0
av_frame_test1(49) ref_count2(frame) = 1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397691.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法打卡 Day20(二叉树)-找树左下角的值 + 路径总和 + 从中序与后序遍历序列构造二叉树

文章目录 Leetcode 513-找树左下角的值题目描述解题思路 Leetcode 112-路径总和题目描述解题思路相关题目Leetcode 113-路径总和 ii Leetcode 106-从中序与后序遍历序列构造二叉树题目描述解题思路类似题目Leetcode 105-从前序与中序遍历序列构造二叉树 Leetcode 513-找树左下角…

HSL模型和HSB模型,和懒人配色的Color Hunt

色彩不仅仅是视觉上的享受,它在数据可视化中也扮演着关键角色。通过合理运用色彩模型,我们可以使数据更具可读性和解释性。在这篇文章将探讨HSL(Hue, Saturation, Lightness)和HSB(Hue, Saturation, Brightness&#x…

Java中的抽象类与接口

1. 抽象类 1.1 抽象类概念 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的, 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 比如&…

freeRTOS学习之ARM架构

分析了arm架构以及RISC指令集的部分内容,同时复习了计算机组成原理中函数的汇编指令流程,也就是CPU的工作流程,大有裨益!

【python】使用FastAPI开发文件下载和上传服务的详细分析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

如何使用Zoom API创建一个会议?

一、注册一个免费的Zoom账号(zoom.us) 二、在Zoom 应用市场(App Marketplace)创建一个server to server 的app,授予创建会议的权限。 三、创建一个Zoom API的服务端程序(node.js) 1、git clone https://github.com/zoom/server-to-server-o…

英语口语成人英语生活英语口语表达四六级英语培训柯桥小语种学习

全红婵向外国人展示金牌夺冠后,全红婵向外国友人展示金牌。视频中,一位外国男子对全红婵说:“How are you?”全红婵回应:“Good!Good!全红婵比出“拿捏”手势对方说全红婵是奥运冠军&#xff0c…

SpringCloud与SpringBoot之间的关系解析

Spring Cloud和Spring Boot是两个独立的项目,分别用于构建微服务架构和快速构建Java应用程序。它们之间有着密切的关系,可以相互配合使用。 Spring Boot简介 Spring Boot是一个用于快速构建Java应用程序的框架。它简化了Spring应用程序的开发过程&#x…

IDEA使用LiveTemplate快速生成方法注释

文章目录 1 场景2 要点2.1 新增LiveTemplate模版2.2 模版内容填写 3 练习手段 1 场景 方法的注释,一般包含作者、创建时间、功能描述、输入参数、返回值,如果每个方法的注释都手写,非常耗时,且容易随着后期变更代码导致差异&#…

Python酷库之旅-第三方库Pandas(075)

目录 一、用法精讲 306、pandas.Series.str.cat方法 306-1、语法 306-2、参数 306-3、功能 306-4、返回值 306-5、说明 306-6、用法 306-6-1、数据准备 306-6-2、代码示例 306-6-3、结果输出 307、pandas.Series.str.center方法 307-1、语法 307-2、参数 307-3、…

Python | Leetcode Python题解之第331题验证二叉树的前序序列化

题目: 题解: class Solution:def isValidSerialization(self, preorder: str) -> bool:pre 1for i in preorder.split(,):if i.isdigit():if pre 0:return Falsepre 1else:if pre 0:return Falsepre - 1return pre 0

GPS跟踪环路MATLAB之——数字锁频环

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 GPS跟踪环路MATLAB之——数字锁频环 前言为什么要锁频环科斯塔斯环鉴别器环路滤波器matlab程序获取完整程序 前言 从事卫星导航基带处理的童鞋都知道,跟踪环路属…

【DM】Linux下安装 DM数据库-命令行安装

【DM】Linux下安装 DM数据库-图形化安装 1、安装前准备工作1.1 检查 Linux系统信息1.2 创建DM安装用户1.3 检查操作系统限制1.4 检查系统内存与存储空间1.4.1 检查内存1.4.2 检查存储空间1.4.2 检查临时文件目录1.4.3 设置 JAVA 环境 2、使用dmdba用户安装DM82.1 挂载 DM 安装光…

vue中v-html 后端返回html + script js中click事件不生效

效果图&#xff1a; 需求&#xff1a;点击加号执行后端返回的script中的代码 后端返回的html&#xff1a; <!DOCTYPE html> <html langzh> <head> <title>xxx</title> <style>body{font-size: 14px}p{text-indent: 30px;}textarea{width…

华兮云创始人王正一——探索未来之路

在竞争激烈且机遇丛生的行业大环境中&#xff0c;我们有幸邀请到王正一走进直播间&#xff0c;开启一场关于破局与发展、理想与现实的深度交流。 当谈及在行业中应秉持何种心态时&#xff0c;王正一的见解独到且深刻。他强调&#xff0c;无论在融整产业中处于怎样的位置、扮演何…

【C++算法】双指针

移动零 题目链接&#xff1a;移动零https://leetcode.cn/problems/move-zeroes/description/ 算法原理 这类题是属于数组划分、数组分开题型 代码步骤&#xff1a; 使用cur遍历数组当cur所指的元素等于0时&#xff0c;cur向后面移动当cur所指的元素不等于0时&#xff0c;de…

JAVA—异常

认识异常&#xff0c;学会从报错信息中发现问题&#xff0c;解决问题。并学会构建自定义异常&#xff0c;提醒编程时注意 目录 1.认识异常 2.自定义异常 1.自定义运行时异常 2.自定义编译时异常 3.异常的处理 1.认识异常 异常就是代表程序出现的问题&#xff0c;用来查询B…

(自用)交互协议设计——protobuf序列化

protobuf是一种比json和xml等序列化工具更加轻量和高效的结构化数据存储格式&#xff0c;性能比json和xml真的强很多&#xff0c;毕竟google出品。 protobuf原理 protobuf如何使用 创建xxx.proto文件 开头写上 syntax"proto2"package tutorial; 表明使用的proto…

机器学习——支持向量机(SVM)(2)

目录 一、SVC理解进阶 1. C&#xff08;硬间隔与软间隔&#xff09; 2. class_weight 二、模型评估指标&#xff08;SVC&#xff09; 1. 混淆矩阵 &#xff08;Confusion Matrix&#xff09; &#xff08;1&#xff09;准确率 —— 模型整体效果 &#xff08;2&#xff…

Spring AI 更新:支持OpenAI的结构化输出,增强对JSON响应的支持

就在昨晚&#xff0c;Spring AI发了个比较重要的更新。 由于最近OpenAI推出了结构化输出的功能&#xff0c;可确保 AI 生成的响应严格遵守预定义的 JSON 模式。此功能显着提高了人工智能生成内容在现实应用中的可靠性和可用性。Spring AI 紧随其后&#xff0c;现在也可以对Open…