ffmpeg 内存模型

ops/2024/9/24 14:23:35/

最近在学习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.ppmy.cn/ops/92927.html

相关文章

未来已来:人工智能如何重塑Facebook的用户体验?

在数字化时代的浪潮中,人工智能(AI)正成为推动技术进步和用户体验优化的核心力量。Facebook(现Meta Platforms)作为全球领先的社交媒体平台,正在充分利用人工智能技术,以重塑用户体验&#xff0…

简易版GTK安装,Linux、Windows平台

你可以选择Linux或Windows(需要MSYS2)系统。 目录 一、Linux端 - Ubuntu 241. 去到 [GTK 官网](https://www.gtk.org/)2. 安装 Gnome 或 Glade Windows端1. 安装GTK2. 安装语言库 一、Linux端 - Ubuntu 24 1. 去到 GTK 官网 Ubuntu 系统下直接输入以下…

Python 为Excel单元格设置填充\背景色 (纯色、渐变、图案)

在使用Excel进行数据处理和分析时,对特定单元格进行背景颜色填充不仅能够提升工作表的视觉吸引力,还能帮助用户快速识别和区分不同类别的数据,增强数据的可读性和理解性。 本文将通过以下三个示例详细介绍如何使用Python在Excel中设置不同的单…

C++ 知识点(长期更新)

C++ 知识点 C/C++1. `cin`, `cin.get()`, `getchar()`, `getline()`, 和 `cin.getline()`的区别。2. 有关 cin >>3. 定义和声明的区别4. `union`、`struct`和`class`的区别5. 深拷贝 vs 浅拷贝6. new 和 malloc 的区别7. 被free回收的内存是立即返还给操作系统吗?为什么…

微服务实现-sleuth+zipkin分布式链路追踪和nacos配置中心

1. sleuthzipkin分布式链路追踪 在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。 这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软…

【RAG检索增强生成】Ollama+AnythingLLM本地搭建RAG大模型私有知识库

目录 前言一、Ollama:革新性的本地LLM服务工具1.核心优势2.技术亮点 二、AnythingLLM 概览1.核心特性2.技术生态支持 三、搭建本地智能知识库1. Ollama的安装启航2. AnythingLLM的安装对接3. AnythingLLM的配置精调4. 工作区与文档管理5. 聊天与检索的智能交互 四、…

CLEFT 基于高效大语言模型和快速微调的语言-图像对比学习

CLEFT: Language-Image Contrastive Learning with Efficient Large Language Model and Prompt Fine-Tuning github.com paper CLEFT是一种新型的对比语言图像预训练框架,专为医学图像而设计。它融合了医学LLM的预训练、高效微调和提示上下文学习,展…

5个适用于Linux系统的PDF转Word工具

凭借其跨平台和设备的统一标准、兼容性和规模小巧等主要优点,可携带文档格式(PDF)可谓最主流的文件格式之一。 市面上有许多查看PDF文件的强大工具,因此所有Linux系统的用户都可以根据自身喜好找到合适的PDF查看工具。然而&#x…