ffprobe是如何处理命令行参数的.
author: hjjdebug
date: 2025年 04月 14日 星期一 13:04:30 CST
description: ffprobe是如何处理命令行参数的.
文章目录
- 1.ffprobe, 功能:
- 2.ffprobe 对命令行选项的处理,
- 3.分析一个实例:
- 3.1 命令行如下:$ffprobe audio.data -f s16le -ac 2 -ar 44100
- 3.2 问1: -f s16le 最终被放到了哪里,怎样被使用的?
- 3.3 问2: -ac 2, -ar 44100 这2个参数最终放到了哪里? 怎样被使用的?
- 3.4 全局变量iformat 在哪里使用?
- 3.5 全局变量 codec_opts 在哪里使用?
- 3.6 AVOption 选项信息,最终去了哪里?
- 3.6.1: iformat, 最终进入了s->iformat
- 3.6.2: codec_opts字典,传送值到其ctx中对应的成员变量的位置
1.ffprobe, 功能:
1.解复用,解包等功能
2.极尽所能显示包信息,frame信息.
可以用命令行设置显示哪些条目. 想显示谁,不想显示谁.精准控制. 不是grep, 是control
2.ffprobe 对命令行选项的处理,
可以理解为,对每一个选项key,value对, 先从它定义的option表中找对应的option项,
根据option项的信息指引,或者直接存储到一个全局变量,或者进行一次函数调用,由函数调用再进一步处理这个key,value参数
总架构: parse_options -> parse_option -> write_option
集体->单个->写值
ffprobe 就没有使用OptionGroup, OptionGroupList, OptionParseContext 这些概念,
而是简化了命令行分析过程,因为它只针对一个文件
3.分析一个实例:
3.1 命令行如下:$ffprobe audio.data -f s16le -ac 2 -ar 44100
其中audio.data 是一个原始码流音频数据文件
3.2 问1: -f s16le 最终被放到了哪里,怎样被使用的?
答1: 因为其key 是 -f, write_option 时调用了 opt_format -> av_find_input_format()
iformat = av_find_input_format(arg); //对参数的进一步处理把arg转变成了iformat
其中 arg 就是 s16le,
其返回值存储到全局变量 iformat 中
3.3 问2: -ac 2, -ar 44100 这2个参数最终放到了哪里? 怎样被使用的?
答2: 因为其key 是 -ac, 在avprobe定义的option 表中找不到该选项,所以就让它指向一个项opt_default
write_options时,执行定义的opt_default 函数继续处理
-ac在avcodec_class 选项表中找到了它,所以把-ac 2放到 codec_opts 字典中, codec_opts是一个全局变量
av_dict_set(&codec_opts, opt, arg, 0); //codec_opts 是全局变量
同样,对于-ar 44100, 其处理过程相同. write_option->opt_default->av_dict_set
最终也放到了全局变量codec_opts 字典中储存.
由此可见, parse 的过程也是数据存储的过程, 是命令行参数向计算机内存传数的过程. 可以简单的理解为就是一个scan操作.
实际上是通过parse_options -> parse_option -> write_option 根据选项表来完成数据存储操作.
人机交互的接口除了命令行方式,其实也可以用文件, 例如json 格式的文件就很简洁清晰, 不过ffprobe 没有这么做.
这些存到内存的变量下一步如何使用,下次分解.
继续:
3.4 全局变量iformat 在哪里使用?
iformat 变量, 在打开文件时使用. 包括format_opts 选项表
if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0)
3.5 全局变量 codec_opts 在哪里使用?
codec_opts 选项字典, 在打开codec_ctx时使用
AVDictionary *opts = filter_codec_opts(codec_opts, stream->codecpar->codec_id, fmt_ctx, stream, codec);
if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0)
3.6 AVOption 选项信息,最终去了哪里?
这些AVOpton 选项,会直接进一步通过接口调用保存到ctx 对应的成员变量中.
这部分具体的存储过程, 不在应用层,而有库函数来执行了, 不过ffmpeg 是开源的,我们还是可以看见它的存储过程.
3.6.1: iformat, 最终进入了s->iformat
iformat, input format是通过 format-name 查format 表对象查到的, 直接通过参数传给avformat_open_input
这样该函数就不用探测文件的 format了
avformat_open_input() 在 libavformat/utils.c 文件中,有如下语句:
if (fmt)
s->iformat = fmt;
如果fmt 为空的话,就需要读取一段文件数据,调用每一个format 对象,让它们检查这段数据是否是它们的格式,
觉得是,返回高分,觉得不是,返回低分,最后根据得分情况,把format 对象地址赋值给s->iformat
至于s->iformat 以后怎样用, 那就是调用它执行format 对象的函数了. 读header, 读包等等.
3.6.2: codec_opts字典,传送值到其ctx中对应的成员变量的位置
codec_opts 字典, 当然 format_opts 字典也是一样,反正就像fmt一样最终送到ctx对象的成员变量上.
avcodec_open2(avctx,codec,&codec_opts) 在 libavcodec/codec.c 中
codec_opts 字典用法还比较曲折,首先它copy到tmp字典,以后用tmp字典操作
if (options)
av_dict_copy(&tmp, *options, 0);
如果codec 有私有类,先让私有类消费. av_opt_set_dict, 就是从源tmp 向目的传送,并删除源项
if (codec->priv_class && (ret = av_opt_set_dict(avctx->priv_data, &tmp)) < 0)
然后向本对象传递字典
if ((ret = av_opt_set_dict(avctx, &tmp)) < 0)
然后把剩下字典项的传回,当然可能是空字典了,都消费完了.
*options = tmp;
av_dict_free(&tmp); //释放自己占用的内存,如果有的话
至于av_opt_set_dict()是怎样把字典项传递到目的,并删除源项,这个简单又最基础,可以把代码copy过来
while ((t = av_dict_get(*options, "", t, AV_DICT_IGNORE_SUFFIX))) //从源字典表中循环读取每一项
{ //向目标设置,目标不是一般的地址,而是AVClass对象,它们是有AVOption 选项表的,
//AVOption结构与命令行中的Option结构是类似的.
//其中最重要的属性是说明该key对应的value,应该存储再obj中的什么位置(offset)
//有个这个AVOpton表,就直到该类是否包含该选项,如果包含,应该把数据存储到哪里.
ret = av_opt_set(obj, t->key, t->value, search_flags);
// 设置不进去,就是选项表中找不到key,那说明这个key,value不属于本表,就放到tmp项保留.
if (ret == AVERROR_OPTION_NOT_FOUND)
ret = av_dict_set(&tmp, t->key, t->value, 0);
}
av_dict_free(options); //把源项全部删除
*options = tmp; //把填不进去的项赋值给源项,表示是剩余的项
好了,这样我们把ffprobe 的命令行选项数据的周游过程叙述了一遍.
ffmpeg的命令行选项的处理过程其下层与ffprobe是一样的.
其上层由于要分清楚选项属于哪一个文件, 所以添加了option_group 概念,在上层把选项划分为不同的文件来进行,
并且在上层 还引入了parse_context概念,option_context概念. 因为它要同时处理多个文件.
因为多个文件处理就等于多次处理一个文件.
它也离不了parse_options->parse_option->write_option, 再到库级别av_opt_set_dict设置成员变量.
ffmpeg的上层概念的具体流程,留待ffmpeg选项分析时再说明.
另补充一句ffplay 的命令行参数与ffprobe 完全一致,入口都是parse_options, 因为它们都是处理一个文件.