用户可以自行查看av_find_input_format函数的具体逻辑,得知最新版本的ffmpeg支持情况。
注意:若进行音视频采集之前,则必须执行avdevice_register_all函数。
static void open_input_media_file(const char *filename)
{
AVFormatContext *format_context = NULL;
int ret = 0;
ret = avformat_open_input(&format_context, filename, NULL, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "avformat_open_input failed, error:%s", av_err2str(ret));
return;
}
av_dump_format(format_context, 0, filename, 0);
avformat_close_input(&format_context);
}
static void open_input_media_from_net(const char *url)
{
AVFormatContext *format_context = NULL;
int ret = 0;
ret = avformat_open_input(&format_context, url, NULL, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "avformat_open_input failed, error:%s", av_err2str(ret));
return;
}
av_dump_format(format_context, 0, url, 0);
avformat_close_input(&format_context);
}
从上述代码中可以看出,ffmpeg解析本地文件与解析网络媒体协议没有区别,这就是ffmpeg强大的地方。
ffmpeg将本地文件抽象成了一种特殊的网络协议,通过分析媒体格式,查找到对应的解封装器,并通过回调,将解封装器细节进行隔离,对用户屏蔽所有差别。
static void capture_media_data_from_input_device()
{
AVFormatContext *format_context = NULL;
AVInputFormat *input_format = NULL;
int ret = 0;
input_format = av_find_input_format("avfoundation");
if (!input_format) {
av_log(NULL, AV_LOG_ERROR, "av_find_input_format failed\n");
return;
}
ret = avformat_open_input(&format_context, ":0", input_format, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "avformat_open_input failed, error:%s", av_err2str(ret));
return;
}
avformat_close_input(&format_context);
}
if (!s && !(s = avformat_alloc_context())) return AVERROR(ENOMEM);
通过avformat_alloc_context函数进行内存分配并初始化,av_class值为av_format_context_class。
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); }
fmt参数可以由用户自己在调用avformat_open_input函数之前,通过调用av_find_input_format函数获取。
关于av_find_input_format函数的讲解,详见:https://blog.csdn.net/chaisy971124568/article/details/109781833
if (fmt) s->iformat = fmt;
此处逻辑的用意是避免污染options参数本身。
if (options) av_dict_copy(&tmp, *options, 0);
s->pb可以通过avio_open2函数进行设置,pb是IO上下文,ffmpeg自己实现了一套缓存,并对用户隔绝了文件和网络的区别。
AVFMT_FLAG_CUSTOM_IO指定pb是由用户自己设置的,这样在调用avformat_free_context函数时,就不用通过avio_close函数进行关闭了,需要用户自己手动关闭。
if (s->pb) // must be before any goto fail s->flags |= AVFMT_FLAG_CUSTOM_IO;
可以通过AVDictionary结果,设置AVFormatContext结构体成员的值。
tmp变量中的各个key-val对需要与avformat_options变量中的参数对应上,否则该函数调用失败,并将无法识别的参数通过tmp回传。
这就是为什么要在步骤4中进行copy的原因。
if ((ret = av_opt_set_dict(s, &tmp)) < 0) goto fail;
打开输入媒体并填充其AVInputFormat结构。
执行完此函数后,s->pb和s->iformat都已经指向了有效实例。
pb是用于读写数据的,它把媒体数据当做流来读写,不管是什么媒体格式。
iformat把pb读出来的流按某种媒体格式进行分析,也就是说pb在底层,iformat在上层。
if ((ret = init_input(s, filename, &tmp)) < 0) goto fail;
protocol_whitelist,protocol_blacklist,format_whitelist这三个均是通过options参数进行设置的。
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; }
skip_initial_bytes通过options进行设置,表示跳过文件开头的若干字节。
avio_skip(s->pb, s->skip_initial_bytes);
这个逻辑在通过ffmpeg将多个图片合成视频流的时候有用。
/* 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; } }
例如,当fmt值为ff_flv_demuxer时,av_opt_set_dict函数设置参数的模板为libavformat/flvdec.c文件中的options变量。
/* 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; } }
ID3,一般是位于一个mp3文件的开头或末尾的若干字节内,附加了关于该mp3的歌手,标题,专辑名称,年代,风格等信息,该信息就被称为ID3信息。
ID3信息分为两个版本,v1和v2版。
v1版的ID3在mp3文件的末尾128字节,以TAG三个字符开头,后面跟上歌曲信息。
v2版一般位于mp3的开头,可以存储歌词,该专辑的图片等大容量的信息。
/* 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);
在read_header函数中主要是做某种格式的初始化工作。
比如填充自己的私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等。
当s->iformat为ff_flv_demuxer时,read_header函数就是flv_read_header。
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header) if ((ret = s->iformat->read_header(s)) < 0) goto fail;
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset) s->internal->data_offset = avio_tell(s->pb);