从学龄前开始解读FFMPEG代码 之 avformat_find_stream_info函数 一
开始学习前想说的话
avformat_find_stream_info是一个很长很复杂的函数,在开始学习前也查了很多相关资料来帮助阅读学习这个函数的源代码,在整个函数体中中,包含多次的循环操作,用于对读入的视频数据进行检查并获取到视频文件中的stream数据。总函数长度有五百多行,所以可能会分成多个文章部分来学习
函数声明
avformat_find_stream_info函数定义在libavformat/avformat.h文件中,如下所示:
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
* The logical file position is not changed by this function;
* examined packets may be buffered for later processing.
*
* @param ic media file handle
* @param options If non-NULL, an ic.nb_streams long array of pointers to
* dictionaries, where i-th member contains options for
* codec corresponding to i-th stream.
* On return each dictionary will be filled with options that were not found.
* @return >=0 if OK, AVERROR_xxx on error
*
* @note this function isn't guaranteed to open all the codecs, so
* options being non-empty at return is a perfectly normal behavior.
*
* @todo Let the user decide somehow what information is needed so that
* we do not waste time getting stuff the user does not need.
*/
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
函数的注释中对功能进行了大体上的描述,这个函数会从媒体文件中读取视频packet(packet也就是未解码之前的视频数据)来获取到stream的信息,对于没有文件头的视频格式非常好用,比如MPEG文件(因为视频文件经常使用文件头header来标注这个视频文件的各项信息,比如muxing格式等等,在上一篇的read_head讲解中我们也看得出来),在一些特殊情况这个函数还会用于计算帧率,比如MPEG-2重复帧模式下。
函数参数有两个,一个是AVFormatContext的上下文句柄,即这个函数实际的操作对象,还有一个是AVDictionary数组,如果第二个参数传入时不为空的话,那么它应该是一个长度等于ic中包含的nb_stream的AVDictionary数组,第i个数组对应视频文件中第i个stream,函数返回时,每一个dictionary将填充没有找到的选项。
返回值为大于等于0的数字,返回>=0代表没有其他情况,其他数字代表不同的错误
在该函数中不能保证stream中的编解码器会被打开,所以在返回之后如果options不为空也是正常的。
从注释中就能看出,这个函数功能复杂而且还非常重要,所以也难怪它有五六百行了。在五六百行中包含对视频文件中总共nb_streams个视频stream的多次循环,所以下面也按照每次循环所做的事情来学习好了,由于函数实现整体代码太长,也按照一小部分一小部分来贴
函数实现0-第一次循环前做的准备
函数的具体实现在libavformat/utils.c文件中,在进入第一次循环前,先要做好一些准备。
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
int i, count = 0, ret = 0, j;
int64_t read_size;
AVStream *st;
AVCodecContext *avctx;
AVPacket pkt1, *pkt;
int64_t old_offset = avio_tell(ic->pb);
// new streams might appear, no options for those
int orig_nb_streams = ic->nb_streams;
int flush_codecs;
int64_t max_analyze_duration = ic->max_analyze_duration;
int64_t max_stream_analyze_duration;
int64_t max_subtitle_analyze_duration;
int64_t probesize = ic->probesize;
int eof_reached = 0;
int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams"