前段时间在做直播的优化,主要是优化首屏时间,因为直播播放器大部分都会采用ffmpeg来处理,所以就会用到avformat_open_input这个函数,这也是首屏耗时比较多的一个地方,这里我主要跟踪一下http的请求以及rtmp的请求,源码都是开源的,这里主要是记录下来以备自己查询,本篇文章主要是是以ijkplayer源码为基础分析的。
avformat_open_input
这个函数的作用是打开文件的链接,如果是网络连接,还会发起网络请求,并一直等待网络数据的返回,然后读取视频流的数据。接下来进行详细的分析。
1.接口参数的解析
首先看函数的声明
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
AVFormatContext **ps
该函数的主要作用是填充好
AVFormatContext **ps
这个结构体。AVFormatContext
这个结构体里面的参数比较多,这里就不一一列举了,详细可以参考avformat.h
这个头文件,具体用到啥到时再详细说明。const char *filename
文件的全部路径,比如
http://flv-meipai.8686c.com/meipai-live/58e03ffd20a05d7a1410d08c.flv
AVInputFormat *fmt
AVInputFormat
的结构体也比较复杂,主要是封装媒体数据封装类型的结构体,比如flv, mpegts, mp4等。在这里可以传入空,如果为空,后面就会从网络数据中读出。当然如果我们知道文件的类型,先用av_find_input_format("flv")
初始化出对应的结构体,这里我们用的是flv,先初始化好这个结构体,对于直播来说,会比较节约时间。AVDictionary **options
struct AVDictionary { int count; AVDictionaryEntry *elems; }; typedef struct AVDictionaryEntry { char *key; char *value; } AVDictionaryEntry;
字典类型的可选参数,可以向ffmpeg中传入指定的参数的值。比如我们这里传入了
av_dict_set_int(&ffp->format_opts, "fpsprobesize", 0, 0);
表示fpsprobesize
对应的参数值为0,当然还可以传入更多值,具体可以参考options_table.h
这个头文件。
2.函数的关键函数实现
avformat_open_input
的具体实现在libavformat/utils.c
文件。
init_input函数
第一次调用avformat_open_input
函数时,传入的ps
是属于初始化状态,很多部分可以忽略,直接跳到以下部分
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
init_input
函数的声明如下
/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
函数的主要功能如注释一样,打开一个文件链接,并尽可能解析出该文件的格式。它里面关键的调用是
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ |
s->avio_flags, options)) < 0)
return ret;
io_open
函数是一个回调函数。一般情况下是采用的默认函数io_open_default
,具体的赋值是在libavformat/option.c
文件中,调用过程如下:
avformat_alloc_context->avformat_get_context_defaults->(s->io_open = io_open_default;)
其中io_open_default的函数实现
static int io_open_default(AVFormatContext *s, AVIOContext **pb,
const char *url, int flags, AVDictionary **options)
{
printf(