一、简单介绍
avformat_open_input()的主要作用是:打开一个媒体文件或RTSP链接并进行解析。
函数原型为:
int avformat_open_input(AVFormatContext **ps, const char *filename,
ff_const59 AVInputFormat *fmt, AVDictionary **options);
各参数含义如下:
- ps: 媒体相关的上下文结构信息,需要注意的是:若该函数调用失败,则其将被系统主动释放。
- filename: 媒体文件名或URL.
- fmt:将要打开的媒体格式的操作结构,因为是读,所以是AVInputFormat结构.对应ffmpeg命令行中的 -f xxx段,如果指定了它,在打开文件中就不会探测文件的实际格式了,以它为准了.
- options:一个AVFormatContext和 demuxer-private 选项的字典。该函数返回时,将被销毁,无需配置则设置为NULL。
二、源码走读
下面的源码已经添加注释,读者可以粗略走读一下,子函数详细内容会在下面继续解释:
int avformat_open_input(AVFormatContext **ps, const char *filename,
ff_const59 AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int i, ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
// 如果ps未指定,则在此处会进行默认分配
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
// 通过avformat_alloc_context分配则该参数必不为NULL
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;
// 是否指定了其操作参数信息,支持的参数在/libavformat/options_table.h声明
if (options)
av_dict_copy(&tmp, *options, 0);
// 如果要使用自定义IO,则需要人为设置pb值,否则为NULL,表示的是系统管理
if (s->pb)
s->flags |= AVFMT_FLAG_CUSTOM_IO;
// 设置参数
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
// 根据filename给url参数赋值
if (!(s->url = av_strdup(filename ? filename : ""))) {
ret = AVERROR(ENOMEM);
goto fail;
}
#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGS
av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endif
// 打开媒体文件或链接,探测其格式
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;
}
}
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->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}
/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
if (s->pb)
ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
#if FF_API_DEMUXER_OPEN
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
#else
if (s->iformat->read_header)
#endif
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
if (!s->metadata) {
s->metadata = s->internal->id3v2_meta;
s->internal->id3v2_meta = NULL;
} else if (s->internal->id3v2_meta) {
av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");
av_dict_free(&s->internal->id3v2_meta);
}
if (id3v2_extra_meta) {
if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
!strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
goto close;
if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
goto close;
if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)
goto close;
} else
av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
}
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto close;
#if FF_API_DEMUXER_OPEN
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
#else
if (s->pb && !s->internal->data_offset)
#endif
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
update_stream_avctx(s);
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;
close:
if (s->iformat->read_close)
s->iformat->read_close(s);
fail:
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_closep(&s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
avformat_open_input()函数的核心代码,其实是init_input() 函数,主要作用为探测媒体资源的类型。
在代码走读之前,先介绍一下AVFMT_NOFILE标志的的含义:大部分libformat或者libavdevice解复用器都有AVFMT_NOFILE标志,表示该解复用器有自己的I/O函数,将不需要调用avio_open和avio_close对I/O进行操作
/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
int score = AVPROBE_SCORE_RETRY;
// 指定了pb参数,使用自定义 IO
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
// 没有指定解复用器,则自行探测
if (!s->iformat)
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
// 如果指定了pb后,解复用器的flag还指定了AVFMT_NOFILE,则冲突,直接返回失败
else if (s->iformat->flags & AVFMT_NOFILE)
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.\n");
return 0;
}
// 1、如果指定了解复用器,且该解复用器的flag包含AVFMT_NOFILE,则直接返回AVPROBE_SCORE_RETRY分数
// 2、如果没有指定解复用器,则自行探测
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
//
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;
if (s->iformat)
return 0;
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
这个函数分三种情况,分别为:
1、指定了pb参数,使用自定义 IO
2、
本文介绍了FFmpeg中avformat_open_input函数的作用与实现原理,详细分析了其参数及内部核心函数init_input的工作流程。
3174

被折叠的 条评论
为什么被折叠?



