当前位置:网站首页>FFMPEG关键结构体——AVFrame
FFMPEG关键结构体——AVFrame
2022-07-05 23:56:00 【陈小帅hh】
一、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的数组。
边栏推荐
- Fiddler Everywhere 3.2.1 Crack
- Spreadjs 15.1 CN and spreadjs 15.1 en
- 如何获取localStorage中存储的所有值
- 有什么不起眼却挣钱的副业?
- 【LeetCode】5. Valid palindrome
- Doppler effect (Doppler shift)
- Effet Doppler (déplacement de fréquence Doppler)
- Bao Yan notebook IV software engineering and calculation volume II (Chapter 8-12)
- XML configuration file (DTD detailed explanation)
- Russian Foreign Ministry: Japan and South Korea's participation in the NATO summit affects security and stability in Asia
猜你喜欢

GFS distributed file system

跟着CTF-wiki学pwn——ret2libc1

保研笔记四 软件工程与计算卷二(8-12章)

如何让同步/刷新的图标(el-icon-refresh)旋转起来

After summarizing more than 800 kubectl aliases, I'm no longer afraid that I can't remember commands!

Part III Verilog enterprise real topic of "Niuke brush Verilog"

There is no network after configuring the agent by capturing packets with Fiddler mobile phones

Senparc.Weixin.Sample.MP源码剖析

XML配置文件(DTD详细讲解)

【NOI模拟赛】Anaid 的树(莫比乌斯反演,指数型生成函数,埃氏筛,虚树)
随机推荐
Biased sample variance, unbiased sample variance
如何提升口才
Laser slam learning record
单商户V4.4,初心未变,实力依旧!
FFT 学习笔记(自认为详细)
China Jinmao online electronic signature, accelerating the digitization of real estate business
用列表初始化你的vector&&initializer_list简介
TS type declaration
mysql-全局锁和表锁
保研笔记四 软件工程与计算卷二(8-12章)
QT -- thread
Rsync remote synchronization
时区的区别及go语言的time库
【luogu CF487E】Tourists(圆方树)(树链剖分)(线段树)
SpreadJS 15.1 CN 与 SpreadJS 15.1 EN
"14th five year plan": emphasis on the promotion of electronic contracts, electronic signatures and other applications
保研笔记二 软件工程与计算卷二(13-16章)
How much do you know about the bank deposit business that software test engineers must know?
QCombox(重写)+QCompleter(自动补全,自动加载qcombox的下拉选项,设置背景颜色)
【EF Core】EF Core与C# 数据类型映射关系