在AVCodecContext结构体存储有AVStream数组,包含所有视频流、音频流、字幕流的信息。每个码流包含有时间基、时长、索引数组、编解码器参数、dts、元数据。其中,索引数组用于保存每帧数据包offset、size、timestamp、flag,用于seek定位某个时间戳对应的帧。
AVStream结构体如下:
typedef struct AVStream {
// 数组索引
int index;
// stream id
int id;
// 私有数据
void *priv_data;
// 时间基
AVRational time_base;
// 开始时间
int64_t start_time;
// 码流时长
int64_t duration;
// 帧数
int64_t nb_frames;
int disposition; /**< AV_DISPOSITION_* bit field */
// 选择丢弃哪些packets
enum AVDiscard discard;
// 宽高比
AVRational sample_aspect_ratio;
// 码流的元数据信息
AVDictionary *metadata;
// 平均帧率
AVRational avg_frame_rate;
// 封面信息
AVPacket attached_pic;
// 附加数据
AVPacketSideData *side_data;
int nb_side_data;
int event_flags;
AVRational r_frame_rate;
// 编解码器参数
AVCodecParameters *codecpar;
int pts_wrap_bits;
int64_t first_dts;
int64_t cur_dts;
int64_t last_IP_pts;
int last_IP_duration;
int probe_packets;
int codec_info_nb_frames;
/* av_read_frame() support */
enum AVStreamParseType need_parsing;
struct AVCodecParserContext *parser;
// 索引数组
AVIndexEntry *index_entries;
// 索引数组长度
int nb_index_entries;
unsigned int index_entries_allocated_size;
int stream_identifier;
AVStreamInternal *internal;
} AVStream;
我们来看下ffprobe打印的码流信息,该mp4文件有5个码流,属于双音轨双字幕。第一个是video,编码器为h264,帧率为23.98fps,分辨率为720x306,像素格式为yuv420p;第二个是audio,编码器为aac,采样率为44100,立体声,语言为印地语;第三个也是audio,参数与第二个相同,除了语言是英语;第四个是subtitle,语言为英语,编码器为mov_text;第五个也是subtitle,参数与第四个相同。具体的码流信息如下:
接下来是调试的实时数据,stream数组信息:
然后是AVCodecContext的编解码器参数,包括codec_type(媒体类型)、codec_id、bit_rate、profile、level、width、height、sample_rate、channels等。具体参数如下:
另外,AVStream内部有nb_index_entries(索引数组长度)、index_entries(索引数组)。而索引数组包括:offset、size、timestamp、flags、min_distance。在seek操作时,根据给定时间戳二分查找timestamp数组,seek有三种模式:previous、next、nearest,一般使用previous前向查找。具体信息如下:
最后,要讨论的是time_base时间基。FFmpeg中的时间戳是用time_base作为单位,位于rational.h的AVRational结构体定义如下:
typedef struct AVRational{
int num; ///< Numerator
int den; ///< Denominator
} AVRational;
换算为真实时间,需要使用num分子与den分母相除,公式如下:
static inline double av_q2d(AVRational a){
return a.num / (double) a.den;
}