FFMPEG关键结构体——AVFrame

一、AVFrame结构体

AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。

比如说,解码的时候存储了宏块类型表,QP表,运动矢量表等数据。编码的时候也存储了相关的数据。因此在使用FFMPEG进行码流分析的时候,AVFrame是一个很重要的结构体。

AVFramet通常在解码时包含较多的码流参数,编码时主要用于承载图像数据或者音频采样数据。结构体的定义位于libavutil/frame.h,这里介绍解码情况下的主要变量
在这里插入图片描述

1.变量介绍

uint8_t *data[AV_NUM_DATA_POINTERS];
(1)图像数据:
对于packed格式的数据(例如RGB24),会存到data[0]里面。
对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]…(YUV420P中data[0]存Y,data[1]存U,data[2]存V)
(2)音频数据:
采样数据PCM, 保存方式同图像数据。 对于对于planar格式的音频数据通道数超过8时,其余通道数据存放于extended_data中。

int linesize[AV_NUM_DATA_POINTERS];
行字节的跨度,相当于stride。对于data[i]区域中的一行像素占用的字节数,对于RGB24理论是{wh3, 0, …}; 对于yuv420p,理论是{w, w/2, w/2, 0, …}。但ffmpeg内存会填充对齐,实际行字节数会大于等于理论值。
enum AVPictureType pict_type;
帧数据类型,部分摘取如下
在这里插入图片描述

二、常用函数介绍

1、av_frame_alloc()
申请AVFrame结构体空间,同时会对申请的结构体初始化。注意哦,这个函数只是创建AVFrame结构的空间,AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS]空间此时NULL,不会创建的。

2、av_frame_free()
释放AVFrame的结构体空间。这个函数就有点意思了。因为他不仅仅释放结构体空间,还涉及到AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS];字段的释放问题。,如果AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS]中的引用==1,则释放data的空间。

3、int av_frame_ref(AVFrame *dst, const AVFrame *src)
对已有AVFrame的引用,这个引用做了两个动作:1、将src属性内容复制到dst,2、对AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS]字段引用计数+1。

4、void av_frame_unref(AVFrame *frame)
对frame释放引用,做了两个动作:1、将frame的各个属性初始化,2、如果AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS]中的引用==1,则释放data的空间。当然,如果data的引用计数>1则由别的frame去检测释放。

5、av_frame_get_buffer()
这个函数是建立AVFrame中的uint8_t *data[AV_NUM_DATA_POINTERS]内存空间,使用这个函数之前frame结构中的format、width、height:必须赋值,要不然函数怎么知道创建多少字节的空间呢!

三、常规解码流程使用

3个步骤:
①AVFrame *pFrame = av_frame_alloc()分配一个AVFrame对象,缓冲区data[]未分配。
②使用调用av_receive_frame解码,会对pFrame分配data[]缓冲区并保存解码数据;每一次使用后,必须需要使用av_frame_unref释放缓冲区,否则重复解码会造成内存泄露。
③需要使用av_frame_free 释放整个对象。

AVFrame *pFrame = av_frame_alloc();  // [1]
while(){av_receive_frame(ctx, pFrame);// process
	av_frame_unref(pFrame);         // [2]
} 
av_frame_free(pFrame);              // [3]

四、图像处理

前面提到,ffmpeg内部编码器对于图像处理部分为了方便优化处理,通常创建的缓冲区比原始图像大,实际有效数据部分只是缓冲区的一部分。这一种优化方案直接反应在linesize上,使用8或16或32字节对齐,取决于平台。

对于分辨率为638*272的视频解码后yuv420p的缓冲区 linsesize为{640,320,320,…},内存布局结构如下图
在这里插入图片描述
图中,其中w = 640 , h = 320。可以看到,在三个通道中每一行数据间进行了数据填充,Y区域的行字节数不等于638,U/V区域的行字节数也不等于319。注意观察data[2],data[1],data[0]之间的差值。

如果我们需要yuv三个分量无填充的 yuv420p数据,可以手动分配大小为wh3/2的内存,再从解码后AVFrame的data[]中拷贝出来。

1、分配缓冲区内存

①方法一:使用malloc原生的内存管理方式,

uint8_t yuv_buf = malloc(w*h*3/2);

②方法二:fmpeg内存分配函数

// yuv420p对齐处理 变量
AVFrame *frame_yuv = av_frame_alloc();
// 分配缓冲区,接收转换后yuv420p的1字节对齐数据,分辨率不改变
av_image_alloc(frame_yuv->data, frame_yuv->linesize,
               video_decoder_ctx->width, video_decoder_ctx->height, AV_PIX_FMT_YUV420P, 1);
// 对于编码,需要AVFrame有对应的参数
frame_yuv->width = video_decoder_ctx->width;
frame_yuv->height = video_decoder_ctx->height;
frame_yuv->format = AV_PIX_FMT_YUV420P;

③方法三:原生指针加ffmpeg内存分配函数

uint8_t *yuvbuf;
int linesize[4];
av_image_alloc(&yuvbuf, linesize, video_decoder_ctx->width, video_decoder_ctx->height, 
                   AV_PIX_FMT_YUV420P, 1);

④方法四:使用av_frame_get_buffer函数(注意检查内存是否连续)

AVFrame *frame_yuv = av_frame_alloc();
frame_yuv->width = video_decoder_ctx->width;
frame_yuv->height = video_decoder_ctx->height;
frame_yuv->format = AV_PIX_FMT_YUV420P;
// 使用一下函数必须先指定frame的 音频/视频 参数
av_frame_get_buffer(frame_yuv, 1);  // align = 0,  由系统选择最优对齐方式

注意,av_frame_get_buffer(frame_yuv, 1); 保证y,u,v分量数据区域是1字节对齐的、连续的,但是y,u,v三个数据区不是1字节对齐、也不是连续的。例如,手动分配100*100尺寸的yuv420的数据分量部分

AVFrame *frame_yuv = av_frame_alloc();
frame_yuv->width = 100;
frame_yuv->height = 100;
frame_yuv->format = AV_PIX_FMT_YUV420P;
av_frame_get_buffer(frame_yuv, 1);

结果如下图,三个分量数据区指针从小到大为 u < y < v,指针之间的间隔不等于wh,也不等于wh/2。

注意:后两种方式一种是使用裸指针,一种借助AVFrame,虽然访问管理缓冲区的方式不同,但是都用到了av_image_alloc,都需要传递缓冲区指针和接收linesize的数组。

AVFrameFFmpeg中的一个结构体,用于表示视频帧的数据。它包含了视频帧的像素数据、宽度、高度、格式等信息。在使用AVFrame之前,需要先分配内存,并设置其各个字段的值。可以使用av_frame_alloc()函数来分配AVFrame实例,并使用av_frame_get_buffer()或av_image_alloc()函数来分配像素数据的内存。其中av_frame_get_buffer()函数会自动选择最优的对齐方式来分配内存,而av_image_alloc()函数可以指定像素数据的对齐方式。通过设置AVFrame的width、height和format字段,可以指定视频帧的宽度、高度和像素格式。总之,AVFrame是用于存储和处理视频帧数据的关键结构体。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [FFmpeg数据结构AVFrame](https://blog.csdn.net/irainsa/article/details/128977176)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [FFMPEG关键结构体——AVFrame](https://blog.csdn.net/PPPPPPPKD/article/details/125586279)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值