音视频播放的原理主要分为:解协议->解封装->解码->音视频同步->播放。
那 FFMPEG 的解码流程结构体也可以由以上流程进行分类:
- 解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;
每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;
每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。
每种解码器都对应一个AVCodec结构。
存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
解码后数据:AVFrame
涉及的主要的结构体和函数
C 音视频开发学习资料:点击领取→音视频开发(资料文档 视频教程 面试题)(FFmpeg WebRTC RTMP RTSP HLS RTP)
AVUtil:核心工具库,下面的许多其他模块都会依赖该库做一些基本的音视频处理操作。
AVFormat:文件格式和协议库,该模块是最重要的模块之一,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。
AVCodec:编解码库,封装了Codec层,但是有一些Codec是具备自己的License的,FFmpeg是不会默认添加像libx264、FDK-AAC等库的,但是FFmpeg就像一个平台一样,可以将其他的第三方的Codec以插件的方式添加进来,然后为开发者提供统一的接口。
AVFilter:音视频滤镜库,该模块提供了包括音频特效和视频特效的处理,在使用FFmpeg的API进行编解码的过程中,直接使用该模块为音视频数据做特效处理是非常方便同时也非常高效的一种方式。
AVDevice:输入输出设备库,比如,需要编译出播放声音或者视频的工具ffplay,就需要确保该模块是打开的,同时也需要SDL的预先编译,因为该设备模块播放声音与播放视频使用的都是SDL库。
SwrRessample:该模块可用于音频重采样,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换。
SWScale:该模块是将图像进行格式转换的模块,比如,可以将YUV的数据转换为RGB的数据,缩放尺寸由1280_720变为800_480。
PostProc:该模块可用于进行后期处理,当我们使用AVFilter的时候需要打开该模块的开关,因为Filter中会使用到该模块的一些基础函数。
1. 解协议结构体1)AVIOContext
在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。
其中opaque指向了URLContext。在avformat_open_input()中进行初始化。
AVIOContext中有以下几个变量比较重要:
unsigned char *buffer: 缓存开始位置
int buffer_size: 缓存大小(默认32768)
unsigned char *buf_ptr: 当前指针读取到的位置
unsigned char *buf_end: 缓存结束的位置
void *opaque: URLContext 结构体
1) AVFormatContext
AVFormatContext是一个贯穿始终的数据结构,它是解封装(flv、mp4、rmvb、avi)功能的结构体。该结构体在avformat.h中,AVFormatContext主要存储视音频封装格式中包含的信息,包含编解码码流丰富的信息,统领全局的基本结构体,主要用于处理封装格式(FLV/MKV/RMVB等),由avformat_alloc_context()初始化,由avformat_free_context()销毁。
在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况):
struct AVInputFormat *iformat: 输入数据的封装格式
AVIOContext *pb: 输入数据的缓存
unsigned int nb_streams: 音视频流的个数
AVStream **streams: 音视频流
char filename[1024]: 文件名
int64_t duration: 时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate: 比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata: 元数据
视频的原数据(metadata)信息可以通过AVDictionary获取。元数据存储在AVDictionaryEntry结构体中,如下所示:
typedef struct AVDictionaryEntry {
char *key;
char *value;
} AVDictionaryEntry;
每一条元数据分为key和value两个属性。
在ffmpeg中通过av_dict_get()函数获得视频的原数据。
下列代码显示了获取元数据并存入meta字符串变量的过程,注意每一条key和value之间有一个"\t:",value之后有一个"\r\n"
//MetaData------------------------------------------------------------
//从AVDictionary获得
//需要用到AVDictionaryEntry对象
//CString author,copyright,description;
CString meta=NULL,key,value;
AVDictionaryEntry *m = NULL;
//不用一个一个找出来
/* m=av_dict_get(pFormatCtx->metadata,"author",m,0);
author.Format("作者:%s",m->value);
m=av_dict_get(pFormatCtx->metadata,"copyright",m,0);
copyright.Format("版权:%s",m->value);
m=av_dict_get(pFormatCtx->metadata,"description",m,0);
description.Format("描述:%s",m->value);
*/
//使用循环读出
//(需要读取的数据,字段名称,前一条字段(循环时使用),参数)
while(m=av_dict_get(pFormatCtx->metadata,"",m,AV_DICT_IGNORE_SUFFIX)){
key.Format(m->key);
value.Format(m->value);
meta =key "\t:" value "\r\n" ;
}
AVFormatContext 结构体具体定义源码如下:
typedef struct AVFormatContext {
const AVClass *av_class;
struct AVInputFormat *iformat; //输入数据的封装格式
struct AVOutputFormat *oformat;
void *priv_data;
AVIOContext *pb; //输入数据的缓存
int ctx_flags;
unsigned int nb_streams;
AVStream **streams; //视音频流
char filename[1024]; //文件名
int64_t start_time;
int64_t duration; //时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate; //比特率(单位bps,转换为kbps需要除以1000)
unsigned int packet_size;
int max_delay;
int flags;
#if FF_API_PROBESIZE_32
unsigned int probesize;
attribute_deprecated
int max_analyze_duration;
#endif
const uint8_t *key;
int keylen;
unsigned int nb_programs;
AVProgram **programs;
enum AVCodecID video_codec_id;
enum AVCodecID audio_codec_id;
enum AVCodecID subtitle_codec_id;
unsigned int max_index_size;
unsigned int max_picture_buffer;
unsigned int nb_chapters;
AVChapter **chapters;
AVDictionary *metadata; //元数据
int64_t start_time_realtime;
int fps_probe_size;
int error_recognition;
AVIOInterruptCB interrupt_callback;
int debug;
#define FF_FDEBUG_TS 0x0001
int64_t max_interleave_delta;
int strict_std_compliance;
int event_flags;
int max_ts_probe;
int avoid_negative_ts;
int ts_id;
int audio_preload;
int max_chunk_duration;
int max_chunk_size;
int use_wallclock_as_timestamps;
int avio_flags;
enum AVDurationEstimationMethod duration_estimation_method;
int64_t skip_initial_bytes;
unsigned int correct_ts_overflow;
int seek2any;
int flush_packets;
int probe_score;
int format_probesize;
char *codec_whitelist;
char *format_whitelist;
AVFormatInternal *internal;
int io_repositioned;
AVCodec *video_codec;
AVCodec *audio_codec;
AVCodec *subtitle_codec;
AVCodec *data_codec;
int metadata_header_padding;
void *opaque;
av_format_control_message control_message_cb;
int64_t output_ts_offset;
#if FF_API_PROBESIZE_32
int64_t max_analyze_duration2;
#else
int64_t max_analyze_duration;
#endif
#if FF_API_PROBESIZE_32
int64_t probesize2;
#else
int64_t probesize;
#endif
uint8_t *dump_separator;
enum AVCodecID data_codec_id;
int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
} AVFormatContext;
用于暂存解复用之后、解码之前的媒体数据(比如一个视频帧)及附加信息(dts、pts)。在AVPacket结构体中,这个结构体虽然比较简单,但是非常的常用。重要的变量有以下几个:
int size: data的大小
int64_t pts: 显示时间戳
int64_t dts: 解码时间戳
int stream_index: 标识该AVPacket所属的视频/音频流。
uint8_t *data: 压缩编码的数据。
例如对于H.264来说。1个AVPacket的data通常对应一个NAL。注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。
因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到音视频的码流文件。
AVPacket 结构体的定义在 packet.h 头文件中:
typedef struct AVPacket {
AVBufferRef *buf;
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
AVPacketSideData *side_data;
int side_data_elems;
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
void *opaque;
AVBufferRef *opaque_ref;
// Time base of the packet's timestamps.
AVRational time_base;
} AVPacket;
AVStream是存储每一个视频/音频流信息的结构体,重要的变量如下所示:
int index:标识该视频/音频流
AVCodecContext codec:指向该视频/音频流的AVCodecContext(它们是一一对应的关系)
AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。FFMPEG其他结构体中也有这个字段,但是根据我的经验,只有AVStream中的time_base是可用的。PTStime_base:真正的时间
int64_t duration:该视频/音频流长度
AVDictionary *metadata:元数据信息
AVRational avg_frame_rate:帧率(注:对视频来说,这个挺重要的)
AVPacket attached_pic:附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
AVCodecParameters *codecpar:编解码器的参数(音频、视频、字幕解码器)
2)AVCodecContext挑一些关键的变量来看看(这里只考虑解码)。
enum AVMediaType codec_type: 编解码器的类型(视频,音频…)
struct AVCodec *codec: 采用的解码器AVCodec(H.264,MPEG2…)
int bit_rate: 平均比特率
uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
AVRational time_base: 根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height: 如果是视频的话,代表宽和高
int refs: 运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate: 采样率(音频)
int channels: 声道数(音频)
enum AVSampleFormat sample_fmt:采样格式
int profile: 型(H.264里面就有,其他编码标准应该也有)
int level: 级(和profile差不太多)
在这里需要注意:AVCodecContext中很多的参数是编码的时候使用的,而不是解码的时候使用的。
enum AVMediaType codec_type; // 编码器类型
编解码器类型有以下几种:
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
// 字幕
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
在FFMPEG中音频采样格式有以下几种:
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
在FFMPEG中型有以下几种,可以看出AAC,MPEG2,H.264,VC-1,MPEG4都有型的概念。
#define FF_PROFILE_UNKNOWN -99
#define FF_PROFILE_RESERVED -100
#define FF_PROFILE_AAC_MAIN 0
#define FF_PROFILE_AAC_LOW 1
#define FF_PROFILE_AAC_SSR 2
#define FF_PROFILE_AAC_LTP 3
#define FF_PROFILE_AAC_HE 4
#define FF_PROFILE_AAC_HE_V2 28
#define FF_PROFILE_AAC_LD 22
#define FF_PROFILE_AAC_ELD 38
#define FF_PROFILE_DTS 20
#define FF_PROFILE_DTS_ES 30
#define FF_PROFILE_DTS_96_24 40
#define FF_PROFILE_DTS_HD_HRA 50
#define FF_PROFILE_DTS_HD_MA 60
#define FF_PROFILE_MPEG2_422 0
#define FF_PROFILE_MPEG2_HIGH 1
#define FF_PROFILE_MPEG2_SS 2
#define FF_PROFILE_MPEG2_SNR_SCALABLE 3
#define FF_PROFILE_MPEG2_MAIN 4
#define FF_PROFILE_MPEG2_SIMPLE 5
#define FF_PROFILE_H264_CONSTRAINED (1<<9) // 8 1; constraint_set1_flag
#define FF_PROFILE_H264_INTRA (1<<11) // 8 3; constraint_set3_flag
#define FF_PROFILE_H264_BASELINE 66
#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)
#define FF_PROFILE_H264_MAIN 77
#define FF_PROFILE_H264_EXTENDED 88
#define FF_PROFILE_H264_HIGH 100
#define FF_PROFILE_H264_HIGH_10 110
#define FF_PROFILE_H264_HIGH_10_INTRA (110|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_422 122
#define FF_PROFILE_H264_HIGH_422_INTRA (122|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_444 144
#define FF_PROFILE_H264_HIGH_444_PREDICTIVE 244
#define FF_PROFILE_H264_HIGH_444_INTRA (244|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_CAVLC_444 44
#define FF_PROFILE_VC1_SIMPLE 0
#define FF_PROFILE_VC1_MAIN 1
#define FF_PROFILE_VC1_COMPLEX 2
#define FF_PROFILE_VC1_ADVANCED 3
#define FF_PROFILE_MPEG4_SIMPLE 0
#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE 1
#define FF_PROFILE_MPEG4_CORE 2
#define FF_PROFILE_MPEG4_MAIN 3
#define FF_PROFILE_MPEG4_N_BIT 4
#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE 5
#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6
#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7
#define FF_PROFILE_MPEG4_HYBRID 8
#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME 9
#define FF_PROFILE_MPEG4_CORE_SCALABLE 10
#define FF_PROFILE_MPEG4_ADVANCED_CODING 11
#define FF_PROFILE_MPEG4_ADVANCED_CORE 12
#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13
#define FF_PROFILE_MPEG4_SIMPLE_STUDIO 14
#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE 15
AVCodec是存储编解码器信息的结构体。
结构体AVCodec的定义的结构体源码(位于libavcodec/codec.h):
typedef struct AVCodec {
/**
* Name of the codec implementation.
* The name is globally unique among encoders and among decoders (but an
* encoder and a decoder can share the same name).
* This is the primary way to find a codec from the user perspective.
*/
const char *name;
/**
* Descriptive name for the codec, meant to be more human readable than name.
* You should use the NULL_IF_CONFIG_SMALL() macro to define it.
*/
const char *long_name;
enum AVMediaType type;
enum AVCodecID id;
/**
* Codec capabilities.
* see AV_CODEC_CAP_*
*/
int capabilities;
uint8_t max_lowres; ///< maximum value for lowres supported by the decoder
const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
#if FF_API_OLD_CHANNEL_LAYOUT
/**
* @deprecated use ch_layouts instead
*/
attribute_deprecated
const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
#endif
const AVClass *priv_class; ///< AVClass for the private context
const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
/**
* Group name of the codec implementation.
* This is a short symbolic name of the wrapper backing this codec. A
* wrapper uses some kind of external implementation for the codec, such
* as an external library, or a codec implementation provided by the OS or
* the hardware.
* If this field is NULL, this is a builtin, libavcodec native codec.
* If non-NULL, this will be the suffix in AVCodec.name in most cases
* (usually AVCodec.name will be of the form "<codec_name>_<wrapper_name>").
*/
const char *wrapper_name;
/**
* Array of supported channel layouts, terminated with a zeroed layout.
*/
const AVChannelLayout *ch_layouts;
} AVCodec;
下面说一下最主要的几个变量:
const char *name: 编解码器的名字,比较短
const char *long_name: 编解码器的名字,全称,比较长
enum AVMediaType type: 指明了类型,是视频,音频,还是字幕
enum AVCodecID id: ID,不重复
const AVRational *supported_framerates: 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts: 支持的像素格式(仅视频)
const int *supported_samplerates: 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts: 支持的采样格式(仅音频)
const uint64_t *channel_layouts: 支持的声道数(仅音频)
int priv_data_size: 私有数据的大小
每一个编解码器对应一个该结构体,查看一下ffmpeg的源代码,我们可以看一下H.264解码器的结构体如下所示(h264dec.c):
const FFCodec ff_h264_decoder = {
.p.name = "h264",
.p.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = h264_decode_init,
.close = h264_decode_end,
FF_CODEC_DECODE_CB(h264_decode_frame),
.p.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
.hw_configs = (const AVCodecHWConfigInternal *const []) {
#if CONFIG_H264_DXVA2_HWACCEL
HWACCEL_DXVA2(h264),
#endif
#if CONFIG_H264_D3D11VA_HWACCEL
HWACCEL_D3D11VA(h264),
#endif
#if CONFIG_H264_D3D11VA2_HWACCEL
HWACCEL_D3D11VA2(h264),
#endif
#if CONFIG_H264_NVDEC_HWACCEL
HWACCEL_NVDEC(h264),
#endif
#if CONFIG_H264_VAAPI_HWACCEL
HWACCEL_VAAPI(h264),
#endif
#if CONFIG_H264_VDPAU_HWACCEL
HWACCEL_VDPAU(h264),
#endif
#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL
HWACCEL_VIDEOTOOLBOX(h264),
#endif
NULL
},
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING |
FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP,
.flush = h264_decode_flush,
.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
.update_thread_context_for_user = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context_for_user),
.p.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.p.priv_class = &h264_class,
};
下面简单介绍一下遍历ffmpeg中的解码器信息的方法(这些解码器以一个链表的形式存储):
1.注册所有编解码器:av_register_all();
2.声明一个AVCodec类型的指针,比如说AVCodec* first_c;
3.调用av_codec_next()函数,即可获得指向链表下一个解码器的指针,循环往复可以获得所有解码器的信息。注意,如果想要获得指向第一个解码器的指针,则需要将该函数的参数设置为NULL。
C 音视频开发学习资料:点击领取→音视频开发(资料文档 视频教程 面试题)(FFmpeg WebRTC RTMP RTSP HLS RTP)
4)AVFrame
存储一帧解码后像素(采样)数据。该结构体定义位于libavutil/frame.h,
AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。比如说,解码的时候存储了宏块类型表,QP表,运动矢量表等数据。编码的时候也存储了相关的数据。
因此在使用FFMPEG进行码流分析的时候,AVFrame是一个很重要的结构体。由av_frame_alloc()或av_image_fill_arrays()初始化,由av_frame_free()销毁。
typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
uint8_t *data[AV_NUM_DATA_POINTERS]; //解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)
int linesize[AV_NUM_DATA_POINTERS]; //data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。
uint8_t **extended_data; //视频帧宽和高(1920x1080,1280x720...)
int width, height;
int nb_samples; //音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个
int format; //解码后原始数据类型(YUV420,YUV422,RGB24...)
int key_frame; //是否是关键帧
enum AVPictureType pict_type; //帧类型(I,B,P...)
#if FF_API_AVFRAME_LAVC
attribute_deprecated
uint8_t *base[AV_NUM_DATA_POINTERS];
#endif
AVRational sample_aspect_ratio; //宽高比(16:9,4:3...)
int64_t pts; //显示时间戳
int64_t pkt_pts;
int64_t pkt_dts;
int coded_picture_number; //编码帧序号
int display_picture_number; //显示帧序号
int quality;
#if FF_API_AVFRAME_LAVC
attribute_deprecated
int reference;
attribute_deprecated
int8_t *qscale_table; //QP表
attribute_deprecated
int qstride;
attribute_deprecated
int qscale_type;
attribute_deprecated
uint8_t *mbskip_table; //跳过宏块表
int16_t (*motion_val[2])[2]; //运动矢量表
attribute_deprecated
uint32_t *mb_type; //宏块类型表
attribute_deprecated
short *dct_coeff; //DCT系数
attribute_deprecated
int8_t *ref_index[2]; //运动估计参考帧列表(貌似H.264这种比较新的标准才会涉及到多参考帧)
#endif
void *opaque;
uint64_t error[AV_NUM_DATA_POINTERS];
#if FF_API_AVFRAME_LAVC
attribute_deprecated
int type;
#endif
int repeat_pict;
int interlaced_frame; //是否是隔行扫描
int top_field_first;
int palette_has_changed;
#if FF_API_AVFRAME_LAVC
attribute_deprecated
int buffer_hints;
attribute_deprecated
struct AVPanScan *pan_scan;
#endif
int64_t reordered_opaque;
#if FF_API_AVFRAME_LAVC
attribute_deprecated void *hwaccel_picture_private;
attribute_deprecated
struct AVCodecContext *owner;
attribute_deprecated
void *thread_opaque;
uint8_t motion_subsample_log2; //一个宏块中的运动矢量采样个数,取log的
#endif
int sample_rate;
uint64_t channel_layout;
AVBufferRef *buf[AV_NUM_DATA_POINTERS];
AVBufferRef **extended_buf;
int nb_extended_buf;
AVFrameSideData **side_data;
int nb_side_data;
#define AV_FRAME_FLAG_CORRUPT (1 << 0)
int flags;
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
enum AVColorSpace colorspace;
enum AVChromaLocation chroma_location;
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
int channels;
int pkt_size;
AVBufferRef *qp_table_buf;
} AVFrame;
一些重要的变量
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的
1.data[]
对于packed格式的数据(例如RGB24),会存到data[0]里面。
对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)
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)