14关于FFmpeg九个常见结构体重要字段的总结

14关于FFmpeg九个常见结构体重要字段的总结

1 封装上下文AVFormatContext

typedef struct AVFormatContext {
	ff_const59 struct AVInputFormat *iformat;//输入文件的AVInputFormat结构体,指明码流数据用到封装格式。例如flv,mkv。 可认为是存放文件的头部信息。例如flv的头部。输入时由avformat_open_input()自行设置,我们只需要使用即可。
	ff_const59 struct AVOutputFormat *oformat;//输出文件的一些封装格式属性信息。输出时由avformat_alloc_output_context2(&m_outputContext, nullptr, "flv", outUrl.c_str())初始化设置,我们只需要使用即可。
	AVIOContext *pb;//网络流,解复用时不需要处理;复用时由我们调用avio_open2内部赋值。
	unsigned int nb_streams;//流数组元素个数,一般为2。
	AVStream **streams;//流数组,包含视频和音频。
	char *url;//解复用时由avformat_open_input()自动设置;复用输出时由avio_open2的参2赋值,实际上在此之前已经由avformat_alloc_output_context2的参4赋值。
	int64_t bit_rate;//输入输出文件的总流码流。
	int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);
    void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);//这两个函数实际上并不常用,常用的是调用avio_open2。
}

2 解复用器AVInputFormat

typedef struct AVInputFormat {
	const char *name;//输入时的封装格式名
	const char *long_name;//同上,但是更人性化
	const char *extensions;//输入封装格式的扩展名
	/*......*/
}AVInputFormat;

3 复用器AVOutputFormat

typedef struct AVOutputFormat{
	const char *name;//输出时的封装格式名
	const char *long_name;//同上,但是更人性化
	const char *extensions;//输出封装格式的扩展名
	/*......*/
}AVOutputFormat;

4 音视频流AVStream

typedef struct AVStream {
#if FF_API_LAVF_AVCTX
    /**
     * @deprecated use the codecpar struct instead
     */
    attribute_deprecated
    AVCodecContext *codec;//将被废弃的编解码上下文。
#endif

	/**
     * 这是表示帧时间戳的基本时间单位(秒)。
     * 即(解)封装上下文中的流的时基,该时基字段非常重要,是我们转换时基时的重要字段。例如输入文件为flv,输出文件为ts,那么flv时基为{1,1000},ts为{1,90000},我们转换时基就是将时间戳从输入流的时基单位转换成输出流的时基单位。
     * 例如此时输入(视频)流的某个画面的pts=80,那么经转时基函数转换后:80/1000=x/90000,x=7200即转换成输出流的时基单位的时间戳pts=7200,这就是我们时基的作用。
     *
     * decoding解码: 由libavformat自行设置。
     * 
     * encoding编码: 调用者可以在avformat_write_header()之前设置,以向muxer提供所需时间基的提示。在avformat_write_header()中,
     * muxer将用实际用于写入输出文件的时间戳的时基覆盖该字段(它可能与用户提供的时间戳相关,也可能与用户提供的时间戳无关,这取决于格式)。
     * 即编码时,muxer复用器会根据写入输出文件的时间戳大概求出对应的时基,然后再判断是否覆盖该字段,所以说明即使你赋值了该时基也可能在写头函数中被改变(一般时基低于10000时有可能被改变)。
     * 
     * 我们常用时基的转换就是将单位为输入流的时基的时间戳转成单位为输出流的时基的时间戳,这一点理解非常重要。
     */
	AVRational time_base;//视频流的时基。
	
	/**
     * Decoding解码:流中首帧的显示时间(即流首帧的pts),单位为上面的时基time_base。
     * 
     * 只有当你绝对100%确定你设置的值确实是第一帧的pts时才设置这个。
     * 
     * 该值可能是未定义的(AV_NOPTS_VALUE)。
     * 
     * 注意:ASF报头不包含正确的start_time, ASFdemuxer解复用时不能设置这个。
     * 封装上下文AVFormatContext也有一个相同的字段start_time,AVFormatContext中的该字段是由该AVStream的start_time值经过推导后,然后赋值给它的。
     */
	int64_t start_time;
	
	/**
     * Decoding解码: 流的duration,单位为流中的time_base。
     * 如果一个源文件没有指定持续时间,但是指定了比特率,这个值将根据比特率和文件大小来估计。
     * 码率:一帧的信息量(字节大小)。所以猜想它大概算法为:文件大小除以码率=共有多少帧。然后再根据两帧的时间差乘以多少帧=估计的视频流时长。
     *
     * Encoding编码:可以由调用者在avformat_write_header()之前设置,以向muxer提供关于估计持续时间的提示。
     * 
     * 注意:该时长为视频流的总时长,一般不用我们设置,注意区别pkt.duration,该时长为两帧的时间差,并非视频流的总时长。
     */
	int64_t duration;
	
    int64_t nb_frames;//流中帧的个数。

	/**
     * 平均帧速率。
     *
     * - demuxing解复用: 可以在创建流时由libavformat库设置,或者在调用avformat_find_stream_info()时设置。
     * - muxing复用: 调用者可以在avformat_write_header()之前设置。
     */
    AVRational avg_frame_rate;//平均帧率。

	/**
     * 真实基本的流的帧率。
     * 
     * 这是所有时间戳可以被准确表示的最低帧率(流中所有帧率的最小公倍数)。注意,这个值只是一个猜测!
     * 
     * 例如,如果时基是1/90000,并且所有帧大约有3600或者1800个计时器滴答声,那么r_frame_rate将是50/1。
     * 
     * 解析上面例子(它这里应该是以1800作运算结果了):
     * 帧率的计算方法,先以平时时基即1s为例,由于两帧时间差(x)=1/帧率(y),例flv的,x=1/25=0.04,所以我们根据该式子转换成求帧率的:y=1/x。
     * 由于1代表我们的时基单位,x代表两帧时间差,对应上面1就是90000(以分母运算),两帧时间差为流的总计算器滴答。古90000/1800=50的帧率。
     * 所以当我们求帧率时都用上面的公式,别用求pts的式子,防止混乱。
     * 
     * 
     * 注意:由于上面的时基time_base有可能会在写头时被FFmpeg内部重置(time_base小于10000时会不断被乘以2,但不一定真的重置,只是有可能,因为我试过低于10000也帧率也是正常的),导致的结果是帧率变得非常大画面变卡,这一点是需要我们注意的,实际上我们可以通过设置宏让FFmpeg内部必须采用我们的帧率。
     */
    AVRational r_frame_rate;//实时帧率。
    
    /**
     * 与此流关联的编解码器参数。分别由libavformat库中的avformat_new_stream()和avformat_free_context()函数分配和释放。
     *
     * - demuxing解复用: 在创建流时由libavformat库填充或在avformat_find_stream_info()中填充(反正就是解复用时它们不需要我们处理,它们自动处理,下面的链接解复用处理感觉就多余了)。
     * - muxing复用: 由调用者在avformat_write_header()之前填充。
     * 
     * 
     * 注意(个人编码经验,并非原文译码):该字段是新的编解码上下文,代替上面的旧的codec编解码上下文(虽然现在仍可以用,但未来版本将被舍弃,最好用这个)。解复用时不需要我们处理,复用时由我们自定义初始化旧的codec后,
     * 再通过avcodec_parameters_from_context给新的编解码上下文赋值。
     * 不过我觉得应该是可以直接给新的codecpar自定义初始化赋值的,我暂时还没试过,不过我猜可能会有点问题,因为其他库也还在使用旧的编解码上下文,可能还需要经过几个版本才能完全舍弃旧的吧。
     */
    AVCodecParameters *codecpar;//新版本的编解码上下文。
    
	/*其余剩下没有的在libavformat库内部的API,非公共API,不能在其它地方使用*/
	
}AVStream;

5 与网络流相关的AVIOContext
暂无。

6 编解码器上下文AVCodecContext

enum AVMediaType codec_type;//媒体类型,具体可以看AVMEDIA_TYPE_xxx即编解码器的类型(视频,音频...)。
const struct AVCodec  *codec;/* 编解码器(H.264,MPEG2...) */
enum AVCodecID     codec_id; /* 编解码的ID,具体可以看AV_CODEC_ID_xxx */
unsigned int codec_tag;//这是用来解决一些encoder编码器的错误。一般设置为0。例如写头前初始化时stream->codec->codec_tag = 0;。
int64_t bit_rate;//平均比特率(码率)。encoding编码: 由用户设置; 不用于常量量化编码。decoding解码: 由用户设置, 如果这个码率信息是在流中可用的话,可能被libavcodec覆盖。
int bit_rate_tolerance;//允许比特流偏离参考的比特数。encoding编码: 由用户设置; 不用于常量量化编码。decoding解码: 未使用。
int flags;//AV_CODEC_FLAG_*.例如写头时满足m_formatContext->oformat->flags & AVFMT_GLOBALHEADER时,设置全局头AV_CODEC_FLAG_GLOBAL_HEADER;
uint8_t *extradata;//针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等),具体看上面解析。
int extradata_size;//extradata缓冲区大小。这两个字段非常重要。
AVRational time_base;//该字段与流中的time_base同名,但意思不一样,流中的是与时间相关的时基单位,这里的意思主要是帧率,一般设置为帧率的倒数,例如帧率fps={25,1},那么该字段一般设置为{1,25},但不一定是帧率的倒数。上面原文已经很清楚了。
int width, height;//图片的长和宽(分辨率),只有视频才有。
int coded_width, coded_height;//位流的宽度/高度,可能与宽度/高度不同,例如,当解码帧在输出之前被裁剪或低分辨率被启用。具体看上面。
int gop_size;//一组图片的数量。即多少帧后重新出现新的I帧。
enum AVPixelFormat pix_fmt;//像素格式,见AV_PIX_FMT_xxx。一般设置为AV_PIX_FMT_YUV420P。
float b_quant_factor;//IP和b帧之间的qscale因子,同样还有I帧,P帧的因子,这里了解一下。
AVRational sample_aspect_ratio;//样本比例 (0 if unknown)。也就是像素的宽度除以像素的高度。
int refs;//参考帧的数目。即运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)。
int sample_rate; // 每秒采样率。
int channels;    // 音频通道数量。也叫声道数(音频)。
enum AVSampleFormat sample_fmt;  //音频(采样)样本格式。

 /* 不应该初始化以下数据。 */
uint64_t channel_layout;//音频通道布局。
int profile;//型--配置文件。(H.264里面就有,其他编码标准应该也有),即该字段下的那些宏。AVRational framerate;
int level;//(和profile差不太多)。
AVRational framerate;//解码:对于在压缩比特流中存储帧速率值的编解码器,解码器可以在这里导出它。当未知时为{0,1}。编码:可用于将CFR内容的帧率信号发送给编码器。

7 编解码器AVCodec

const char *name;//编解码器实现的名称。
const char *long_name;//编解码器的描述性名称,意味着比名称更易于读懂。例如我获取的rtsp输入流的一个:"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"。
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;//支持的声道数(仅音频)。
uint8_t max_lowres; //低分辨率的最大值
int priv_data_size;//私有数据的大小。

8 存放解码前裸流的结构体AVPacket(一个或者多个NAL,一般是一帧,一帧包含多个NAL)

AVBufferRef *buf;//存储包数据(packet data)缓冲区的引用
int64_t pts;//显示时间戳
int64_t dts;//解码时间戳
uint8_t *data;//压缩编码的数据。对于H264压缩码流来说,data存放的是一个NAL,但往往编码时存放的是多个NAL即完整的一帧数据。例如PS流解码为H264,需要将0 0 0 1 67的NAL,0 0 0 1 68,...直至遇到新的PS头中的NAL。所以更准确的应该说data存放的是多个NAL即完整一帧的数据。但不能说前者错误,因为AVPacket中的data数据是由你自己存放的,并非固定存储哪一个数据。
int size;//data的大小
int stream_index;//流下标,标识该AVPacket所属的视频/音频流。
int64_t duration;//一帧的显示时长。例如你上面的pts时间戳为0,duration为1s,那么你这幅画面是显示时长就是0-1秒。
int64_t pos; //在流中的字节位置,如果未知则为-1。

9 存放解码后的原始数据结构体AVFrame(RGB,YUV12等)

uint8_t *data[AV_NUM_DATA_POINTERS];//解码后原始数据。(对视频来说是YUV,RGB,对音频来说是PCM)
int linesize[AV_NUM_DATA_POINTERS];//对于视频,以字节为单位的每个图片行的大小。每个平面的字节大小(整个音频帧的大小)。注意:未必等于图像的宽,一般大于图像的宽。
uint8_t **extended_data;//对于视频,这应该简单地指向data[]。对于音频,FFmpeg音频解码后的数据是存放在AVFrame结构中的。 Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音频数据中。 Planar格式,frame.data[i]或者frame.extended_data[i]表示第i个声道的数据(假设声道0是第一个), AVFrame.data数组大小固定为8,如果声道数超过8,需要从frame.extended_data获取声道数据。
int width, height;//视频帧宽和高即分辨率。(1920x1080,1280x720...)
int nb_samples;//此帧的音频样本数(每个通道),即一个AVFrame中可能包含多个音频帧,在此标记包含了几个。注:一个AVFrame代表解压后的一帧。
int format;// 帧的格式,对于视频帧为enum AVPixelFormat,对于音频帧为enum AVSampleFormat。例如YUV420,YUV422,RGB24...
int key_frame;//是否为关键帧。
enum AVPictureType pict_type;//图片的帧类型。例如I,P,B帧。
AVRational sample_aspect_ratio;//视频帧的样本宽高比。例如16:9,4:3...
int64_t pts;//显示时间戳,单位为time_base(帧被显示给用户时的时间)。
int64_t pkt_dts;//从AVPacket复制的DTS(如果没有使用帧线程)。这也是这个帧的显示时间(只从AVPacket.dts计算没有pts的话)。该字段不是特别重要,因为它的兄弟pkt_pts,看上面,已经被弃用了,所以这个也差不多了。
int coded_picture_number;//在位流中排序的图片数。(即编码帧序号)
int coded_picture_number;//图片数的显示序列(即显示帧序号)。
int quality;//质量(between 1 (good) and FF_LAMBDA_MAX (bad))。
int interlaced_frame;//这幅图片的内容是交错(隔行interlaced)的。通过赋值使它是否是隔行扫描。
int sample_rate;//音频数据采样率。
uint64_t channel_layout;//音频数据的通道布局。
AVBufferRef *buf[AV_NUM_DATA_POINTERS];
AVBufferRef **extended_buf;//这两个缓冲区暂时不知道具体的意思,但是猜他大概是用于缓冲上面的data的内容。
int nb_extended_buf;//extended_buf中的元素数量。
int64_t pkt_duration;//对应数据包的duration,用AVStream->time_base单位表示,如果未知,为0。这个字段稍微了解一下即可,没有AVPacket中的duration重要。
AVDictionary *metadata;//metadata元数据。
int channels;//音频的通道数,仅用于音频。一般和上面的通道布局一起使用。
int pkt_size;//包含压缩帧的相应数据包的大小。

//新版本的AVFrame已经将QP表的相应字段弃用了。所以这里没有列出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值