10.AVFrame结构体

4.0 AVFrame 结构体

typedef struct AVFrame {
	#define AV_NUM_DATA_POINTERS 8
	uint8_t *data[AV_NUM_DATA_POINTERS];
	int linesize[AV_NUM_DATA_POINTERS];
	
	uint8_t **extended_data;
	
	/**宽高 */
	int width, height;
	
	int nb_samples;
	int format;
	
	/**是否是关键帧*/
	int key_frame;
	
	/**帧类型(I,B,P)*/
	enum AVPictureType pict_type;
	
	uint8_t *base[AV_NUM_DATA_POINTERS];
	AVRational sample_aspect_ratio;
	
	int64_t pts;
	int64_t pkt_pts;
	int64_t pkt_dts;
	
	int coded_picture_number;
	int display_picture_number;
	int quality;
	int reference;
	
	/**QP 表*/
	int8_t *qscale_table;
	
	int qstride;
	int qscale_type;
	
	/**跳过宏块表 */
	uint8_t *mbskip_table;
	
	/**运动矢量表*/
	int16_t (*motion_val[2])[2];
	
	/**宏块类型表 */
	uint32_t *mb_type;
	
	/**DCT 系数 */
	short *dct_coeff;
	
	/**参考帧列表 */
	int8_t *ref_index[2];
	
	void *opaque;
	uint64_t error[AV_NUM_DATA_POINTERS];
	
	int type;
	int repeat_pict;
	int interlaced_frame;
	int top_field_first;
	int palette_has_changed;
	int buffer_hints;
	
	AVPanScan *pan_scan;
	int64_t reordered_opaque;
	void *hwaccel_picture_private;
	struct AVCodecContext *owner;
	void *thread_opaque;
	
	/**
	* log2 of the size of the block which a single vector in motion_val represents:
	* (4->16x16, 3->8x8, 2-> 4x4, 1-> 2x2)
	* - encoding: unused
	* - decoding: Set by libavcodec.
	*/
	uint8_t motion_subsample_log2;
	
	/**(音频)采样率 */
	int sample_rate;
	
	uint64_t channel_layout;
	int64_t best_effort_timestamp;
	int64_t pkt_pos;
	int64_t pkt_duration;
	
	AVDictionary *metadata;
	
	int decode_error_flags;
	
	#define FF_DECODE_ERROR_INVALID_BITSTREAM 1
	#define FF_DECODE_ERROR_MISSING_REFERENCE 2
	
	int64_t channels;
	
	...
	
} AVFrame;

1.AVFrame 结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是 YUV,RGB,对音频来说是 PCM),
此外还包含了一些相关的信息。比如说,解码的时候存储了宏块类型表,QP 表,运动矢量表等数据。
编码的时候也存储了相关的数据。因此在使用 FFMPEG 进行码流分析的时候,AVFrame 是一个很重要的结构体。

2.下面看几个主要变量的作用(在这里考虑解码的情况):
uint8_t *data[AV_NUM_DATA_POINTERS]:解码后原始数据(对视频来说是 YUV,RGB,对音频来说是 PCM)

int linesize[AV_NUM_DATA_POINTERS]:data 的大小

int width, height:视频帧宽和高(1920x1080,1280x720…)

int nb_samples:音频的一个 AVFrame 中可能包含多个音频帧,在此标记包含了几个

int format:解码后原始数据类型(YUV420,YUV422,RGB24…)

int key_frame:是否是关键帧

enum AVPictureType pict_type:帧类型(I,B,P…)

AVRational sample_aspect_ratio:宽高比(16:9,4:3…)

int64_t pts:显示时间戳

int coded_picture_number:编码帧序号

int display_picture_number:显示帧序号

int8_t *qscale_table:QP 表

uint8_t *mbskip_table:跳过宏块表

int16_t (*motion_val[2])[2]:运动矢量表

uint32_t *mb_type:宏块类型表

short *dct_coeff:DCT 系数,这个没有提取过

int8_t *ref_index[2]:运动估计参考帧列表(貌似 H.264 这种比较新的标准才会涉及到多参考帧)

int interlaced_frame:是否是隔行扫描

uint8_t motion_subsample_log2:一个宏块中的运动矢量采样个数,取 log 的其他的变量不再一一列举,源代码中都有详细的说明。

3.在这里重点分析一下几个需要一定的理解的变量:
1)data[]
对于 packed 格式的数据(例如 RGB24),会存到 data[0]里面。
对于 planar 格式的数据(例如 YUV420P),则会分开成 data[0],data[1],data[2]…
(YUV420P 中 data[0]存 Y,data[1]存 U,data[2]存 V)
具体参见:FFMPEG 实现 YUV,RGB 各种图像原始数据之间的转换(swscale)

2)pict_type
包含以下类型:

enum AVPictureType {
	AV_PICTURE_TYPE_NONE = 0, ///< Undefined
	AV_PICTURE_TYPE_I, ///< Intra
	AV_PICTURE_TYPE_P, ///< Predicted
	AV_PICTURE_TYPE_B, ///< Bi-dir predicted
	AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG4
	AV_PICTURE_TYPE_SI, ///< Switching Intra
	AV_PICTURE_TYPE_SP, ///< Switching Predicted
	AV_PICTURE_TYPE_BI, ///< BI type
};

3)sample_aspect_ratio
宽高比是一个分数,FFMPEG 中用 AVRational 表达分数:

/**
* rational number numerator/denominator
*/
typedef struct AVRational{
	int num; ///< numerator
	int den; ///< denominator
} AVRational;

4)qscale_table
QP 表指向一块内存,里面存储的是每个宏块的 QP 值。
宏块的标号是从左往右,一行一行的来的。每个宏块对应 1个 QP。
qscale_table[0]就是第 1 行第 1 列宏块的 QP 值;
qscale_table[1]就是第 1 行第 2 列宏块的 QP 值;
qscale_table[2]就是第 1 行第 3 列宏块的 QP 值。
以此类推…

宏块的个数用下式计算:
注:宏块大小是 16x16 的。

每行宏块数:
int mb_stride = pCodecCtx->width/16+1

宏块的总数:
int mb_sum = ((pCodecCtx->height+15)>>4)*(pCodecCtx->width/16+1)

5)motion_subsample_log2
1个运动矢量所能代表的画面大小(用宽或者高表示,单位是像素),注意,这里取了 log2。
代码注释中给出以下数据:(24、23、22、21)
4->16x16, 3->8x8, 2-> 4x4, 1-> 2x2

即 1 个运动矢量代表 16x16 的画面的时候,该值取 4;
1 个运动矢量代表 8x8 的画面的时候,该值取 3
以此类推…

6)motion_val
运动矢量表存储了一帧视频中的所有运动矢量。该值的存储方式比较特别:

int16_t (*motion_val[2])[2];

注释中给了一段代码:

int mv_sample_log2= 4 - motion_subsample_log2;
int mb_width= (width+15)>>4;
int mv_stride= (mb_width << mv_sample_log2) + 1;
motion_val[direction][x + y*mv_stride][0->mv_x, 1->mv_y];

大概知道了该数据的结构:
1.首先分为两个列表 L0 和 L1
2.每个列表(L0 或 L1)存储了一系列的 MV(每个 MV 对应一个画面,大小由 motion_subsample_log2 决定)
3.每个 MV 分为横坐标和纵坐标(x,y)

注意,在 FFMPEG 中 MV 和 MB 在存储的结构上是没有什么关联的,第 1 个 MV 是屏幕上左上角画面的 MV(画
面的大小取决于 motion_subsample_log2),第 2 个 MV 是屏幕上第 1 行第 2 列的画面的 MV,以此类推。因此在一
个宏块(16x16)的运动矢量很有可能如下图所示(line 代表一行运动矢量的个数):
图10.1. 8x8划分的运动矢量与宏块的关系
8x8划分的运动矢量与宏块的关系
7)mb_type
宏块类型表存储了一帧视频中的所有宏块的类型。其存储方式和 QP 表差不多。
只不过其是 uint32 类型的,而 QP是 uint8 类型的。每个宏块对应一个宏块类型变量。

宏块类型如下定义所示:

//The following defines may change, don't expect compatibility if you use them.
#define MB_TYPE_INTRA4x4 0x0001
#define MB_TYPE_INTRA16x16 0x0002 //FIXME H.264-specific
#define MB_TYPE_INTRA_PCM 0x0004 //FIXME H.264-specific
#define MB_TYPE_16x16 0x0008
#define MB_TYPE_16x8 0x0010
#define MB_TYPE_8x16 0x0020
#define MB_TYPE_8x8 0x0040
#define MB_TYPE_INTERLACED 0x0080
#define MB_TYPE_DIRECT2 0x0100 //FIXME
#define MB_TYPE_ACPRED 0x0200
#define MB_TYPE_GMC 0x0400
#define MB_TYPE_SKIP 0x0800
#define MB_TYPE_P0L0 0x1000
#define MB_TYPE_P1L0 0x2000
#define MB_TYPE_P0L1 0x4000
#define MB_TYPE_P1L1 0x8000
#define MB_TYPE_L0 (MB_TYPE_P0L0 | MB_TYPE_P1L0)
#define MB_TYPE_L1 (MB_TYPE_P0L1 | MB_TYPE_P1L1)
#define MB_TYPE_L0L1 (MB_TYPE_L0 | MB_TYPE_L1)
#define MB_TYPE_QUANT 0x00010000
#define MB_TYPE_CBP 0x0002000
//Note bits 24-31 are reserved for codec specific use (h264 ref0, mpeg1 0mv, ...)

一个宏块如果包含上述定义中的一种或两种类型,则其对应的宏块变量的对应位会被置 1。
注:一个宏块可以包含好几种类型,但是有些类型是不能重复包含的,比如说一个宏块不可能既是 16x16 又是 8x8。

8)ref_index
运动估计参考帧列表存储了一帧视频中所有宏块的参考帧索引。
这个列表其实在比较早的压缩编码标准中是没有什么用的。
只有像 H.264 这样的编码标准才有多参考帧的概念。
每个宏块包含有 4 个该值,该值反映的是参考帧的索引。

图10.2.视频帧
在这里插入图片描述
图10.3.QP 参数提取的结果
在这里插入图片描述
图10.4.美化过的(加上了颜色)
在这里插入图片描述
图10.5.宏块类型参数提取的结果
在这里插入图片描述
图10.6.美化过的(加上了颜色,更清晰一些,s 代表 skip 宏块)
在这里插入图片描述
图10.7.运动矢量参数提取的结果(在这里是 List0)
在这里插入图片描述
图10.8.运动估计参考帧参数提取的结果
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值