一、源码方法参数分析
下面是avformat_open_input的方法及参数:
/**
* Open an input stream and read the header. The codecs are not opened.
* The stream must be closed with avformat_close_input().
*
* @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
* May be a pointer to NULL, in which case an AVFormatContext is allocated by this
* function and written into ps.
* Note that a user-supplied AVFormatContext will be freed on failure.
* @param url URL of the stream to open.
* @param fmt If non-NULL, this parameter forces a specific input format.
* Otherwise the format is autodetected.
* @param options A dictionary filled with AVFormatContext and demuxer-private options.
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
*
* @return 0 on success, a negative AVERROR on failure.
*
* @note If you want to use custom IO, preallocate the format context and set its pb field.
*/
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
这里我们提供的是英文的参数注释,主要是翻译不好,对此方法的理解也没有什么帮助。下面针对此方法的这几个参数进行一下说明:
- AVFormatContext **ps : 参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,会返回一个AVFormatContext的实例。此参数可以为avformat_alloc_context创建的,也可以是NULL。
- 参数filename是媒体文件名或URL.
- 参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以传入一个调用者定义的inputFormat,对应命令行中的 -f xxx段,如果指定了它,在打开文件中就不会探测文件的实际格式了,以它为准了.
- 参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入,特殊的操作参数而建的, 可以无视。
二、方法的作用及流程
1. 输入输出结构体AVIOContext的初始化;
2. 输入数据的协议(例如RTMP,或者File)的识别(通过一套评分机制)
-
判断文件名的后缀
-
读取文件头的数据进行比对;
3. 使用获得最高分的文件协议对应的URLProtocol,通过函数指针的方式,与FFMPEG连接,剩下的就是调用该URLProtocol的函数进行open,read等操作。
下图用来了解一下和avformat_open_input有关系的函数调用流程图:
可见最终都调用了URLProtocol结构体中的函数指针。
下面是URLProtocol结构,可以看出来URLProtocol是一大堆函数指针的集合(avio.h文件)
typedef struct URLProtocol {const char *name;int (*url_open)(URLContext *h, const char *url, int flags);int (*url_read)(URLContext *h, unsigned char *buf, int size);int (*url_write)(URLContext *h, const unsigned char *buf, int size);int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);int (*url_close)(URLContext *h);struct URLProtocol *next;int (*url_read_pause)(URLContext *h, int pause);int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags);int (*url_get_file_handle)(URLContext *h);int priv_data_size;const AVClass *priv_data_class;int flags;int (*url_check)(URLContext *h, int mask);
} URLProtocol;
URLProtocol功能就是完成各种输入协议的读写等操作
但输入协议种类繁多,它是怎样做到“大一统”的呢?
原来,每个具体的输入协议都有自己对应的URLProtocol。
比如file协议(FFMPEG把文件也当做一种特殊的协议)(*file.c文件)
URLProtocol ff_pipe_protocol = {.name = "pipe",.url_open = pipe_open,.url_read = file_read,.url_write = file_write,.url_get_file_handle = file_get_handle,.url_check = file_check,
};
或者rtmp协议(此处使用了librtmp)(librtmp.c文件)
URLProtocol ff_rtmp_protocol = {.name = "rtmp",.url_open = rtmp_open,.url_read = rtmp_read,.url_write = rtmp_write,.url_close = rtmp_close,.url_read_pause = rtmp_read_pause,.url_read_seek = rtmp_read_seek,.url_get_file_handle = rtmp_get_file_handle,.priv_data_size = sizeof(RTMP),.flags = URL_PROTOCOL_FLAG_NETWORK,
};
可见它们把各自的函数指针都赋值给了URLProtocol结构体的函数指针
因此avformat_open_input只需调用url_open,url_read这些函数就可以完成各种具体输入协议的open,read等操作了
三、方法的使用技巧
合理的使用 avformat_open_input 方法能够加快视频的首屏播放。
首先我们知道avformat_open_input
这个函数的作用是打开文件的链接,如果是网络连接,还会发起网络请求,并一直等待网络数据的返回,然后读取视频流的数据。
那如何加快首屏播放呢?
我们可以利用方法中的第三个参数——AVInputFormat。AVInputFormat的结构体比较复杂,主要是封装媒体数据封装类型的结构体,比如flv, mpegts, mp4等,在这里可以传入空(或者0),如果为空(或者0),那么FFmpeg就会自行去检测获取。
当然如果我们知道文件的类型,先用av_find_input_format("flv")初始化出对应的结构体,这里我们用的是flv,先初始化好这个结构体,就能够节约时间加快首屏播放了。