本文主要来分析demuxer的流程。
1.结构流程图
从上面的结构图中我们可以看到AVFormatContext的iformat指向AVInputFormat。
2.实现流程图
3.avformat_open_input函数作用
首先看函数的声明
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
这个函数的作用是打开文件的链接,如果是网络连接,还会发起网络请求,并一直等待网络数据的返回,然后读取视频流的数据(协议操作)。
AVFormatContext **ps
该函数的主要作用是填充好AVFormatContext **ps这个结构体。AVFormatContext这个结构体里面的参数比较多,这里就不一一列举了,详细可以参考avformat.h这个头文件,具体用到到时再详细说明。
const char *filename
文件的全部路径,比如
http://videocdn.eebbk.net/7abbdb39fd2aa71efa64f9897f4f5616.mp4
AVInputFormat *fmt
AVInputFormat 的结构体也比较复杂,主要是封装媒体数据封装类型的结构体,比如flv,mpegts,mp4等。在这里可以传入空,如果为空,后面就会从网络数据中读出。当然如果我们知道文件的类型,先用av_find_input_format(“mp4”)初始化出对应的结构体,这里我们用的是mp4,先初始化好这个结构体,比如对于直播来说,会比较节约时间。
AVDictionary **options
struct AVDictionary {
int count;
AVDictionaryEntry *elems;
};
typedef struct AVDictionaryEntry {
char *key;
char *value;
} AVDictionaryEntry;
字典类型的可选参数,可以向ffmpeg中传入指定的参数的值。比如我们这里传入了
//number of frames used to probe fps
av_dict_set_int(&ffp->format_opts, "fpsprobesize", 0, 0);
表示fpsprobesize对应的参数值为0,当然还可以传入更多值,具体可以参考options_table.h/libavformat这个头文件。
文章最后扫码进qun,可免费领取音视频学习资料,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等
函数使用sample:
AVFormatContext *ic = NULL;//初始化ic变量
//申请AVFormatContext内存
ic = avformat_alloc_context();
if (!ic) {//内存申请失败
av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
ret = AVERROR(ENOMEM);
goto fail;
}
ic->interrupt_callback.callback = decode_interrupt_cb;//IO层错误回调
ic->interrupt_callback.opaque = is;
if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
scan_all_pmts_set = 1;
}
err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
if (err < 0) {
print_error(is->filename, err);
ret = -1;
goto fail;
}
4.流程实现分析
讲解解复用流程,我们还是需要从avformat_open_input/utils.c/libavformat函数开始分析整个解析流程。
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int i, ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
//如果s为空,且没有申请内存的话,申请AVFormatContext内存
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
if (!s->av_class) {
av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
return AVERROR(EINVAL);
}
if (fmt)
s->iformat = fmt;//AVInputFormat赋值给AVFormatContext的AVInputFormat
if (options)//获取option,拷贝给tmp
av_dict_copy(&tmp, *options, 0);
if (s->pb) // must be before any goto fail
s->flags |= AVFMT_FLAG_CUSTOM_IO;
//把获取的option设置给AVFormatContext
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
//filename赋值给s->filename
av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
//初始化协议与初始化解封装(文件探测等)
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
s->probe_score = ret;//获取文件探测回来的分数
//协议白名单
if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
if (!s->protocol_whitelist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
//协议黑名单
if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
if (!s->protocol_blacklist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
ret = AVERROR(EINVAL);
goto fail;
}
//打开流的时候跳过最初的字节
avio_skip(s->pb, s->skip_initial_bytes);
/* Check filename in case an image number is expected. */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}
//把AVFormatContext的duration和start_time置为NOPTS
s->duration = s->start_time = AV_NOPTS_VALUE;
/* Allocate private data. */
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->iformat->priv_class) {
*(const AVClass **) s->priv_data = s->iforma