ffmpeg option的解析
aresample_swr_opts
是AVFilterGraph中的option。
static const AVOption filtergraph_options[] = {
{ "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS,
{ .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, F|V|A, "thread_type" },
{ "slice", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVFILTER_THREAD_SLICE }, .flags = F|V|A, .unit = "thread_type" },
{ "threads", "Maximum number of threads", OFFSET(nb_threads), AV_OPT_TYPE_INT,
{ .i64 = 0 }, 0, INT_MAX, F|V|A, "threads"},
{"auto", "autodetect a suitable number of threads to use", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, .flags = F|V|A, .unit = "threads"},
{"scale_sws_opts" , "default scale filter options" , OFFSET(scale_sws_opts) ,
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, F|V },
{"aresample_swr_opts" , "default aresample filter options" , OFFSET(aresample_swr_opts) ,
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, F|A },
{ NULL },
};
因为是option,所以就想能不能将这个option配置到graph里面,分析代码发现,AVFilterGraph::aresample_swr_opts
在graph解析的时候不能当做filter的option解析。
因为graph load的在解析graph
文本的过程,option来自filter的option,aresample_swr_opts
是AVFilterGraph的option,而AVFilterGraph不在filter的list中,所以graph解析并不能识别aresample_swr_opts
。
但是aresample_swr_opts
从名字来看,就是为了给swresample用的,所以,先看下命令行是怎么用的。
命令行支持,并不是直接用aresample_swr_opts
指定swresample的option,而是不用写成这样aresample=resampler=swr:filter_size=16
,即用filter带option的写法,直接写option也能解析。
如下:
ffmpeg -y -i test.wav -filter_size 16 -phase_shift 6 -ar 48000 out.wav
其中filter_size
和phase_shift
会在解析的时候读取swr class的option,匹配成功后,拼接成swr_opts字符串,最后将swr_opts设置到graph->aresample_swr_opts上。avfiltergraph中协商如果convert_needed
大于0,就会创建对应的swresample
,将aresample_swr_opts
中的option作为swresample
的option参数传入。
其中-filter_size 16
,-phase_shift 6
是被当做option解析的,会进入opt_default
函数,因为这两个参数是swresample的,所以对应代码是:
#if CONFIG_SWRESAMPLE
if (!consumed && (o=opt_find(&swr_class, opt, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
av_dict_set(&swr_opts, opt, arg, FLAGS);
consumed = 1;
}
#endif
解析后会把这两个option设置到swr_opts上。
然后进入configure_filtergraph
函数,会将这个option设置到aresample_swr_opts
:
while ((e = av_dict_get(ost->swr_opts, "", e,
AV_DICT_IGNORE_SUFFIX))) {
av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);
}
if (strlen(args))
args[strlen(args)-1] = 0;
av_opt_set(fg->graph, "aresample_swr_opts", args, 0);
然后在avfiltergraph的query_formats
函数中,创建convert filter的时候将option传进去:
snprintf(inst_name, sizeof(inst_name), "auto_%s_%d",
neg->conversion_filter, converter_count++);
opts = FF_FIELD_AT(char *, neg->conversion_opts_offset, *graph);
ret = avfilter_graph_create_filter(&convert, filter, inst_name, opts, NULL, graph);
其中,graph参数中,可以看到aresample_swr_opts
就是"filter_size=16:phase_shift=6"。
graph中解析swr option
跟踪程序运行过程,在不走ffmpeg命令行程序的时候,像前面,直接设置在avfilter里面类似cmdutils.c里面的逻辑,添加对swr的处理,就可以在自定义程序中load graph的时候如同命令行程序一样,解析swr的option。
最终实现如下:
+#include "libswresample/swresample.h"
#define FF_INTERNAL_FIELDS 1
#include "framequeue.h"
@@ -951,6 +952,10 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
char *av_uninit(parsed_key), *av_uninit(value);
const char *key;
int offset= -1;
+#if CONFIG_SWRESAMPLE
+ char swr_opts[256] = { 0 };
+ const AVClass *swr_class = swr_get_class();
+#endif
if (!args)
return 0;
@@ -995,24 +1000,31 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options,
av_free(parsed_key);
return ret;
}
- } else {
- o = av_opt_find(ctx->priv, key, NULL, 0,
- AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
- if (!o) {
- av_log(ctx, AV_LOG_ERROR, "Option '%s' not found\n", key);
- av_free(value);
- av_free(parsed_key);
- return AVERROR_OPTION_NOT_FOUND;
- }
+ } else if (o = av_opt_find(ctx->priv, key, NULL, 0,
+ AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ)) {
av_dict_set(options, key, value,
(o->type == AV_OPT_TYPE_FLAGS &&
(value[0] == '-' || value[0] == '+')) ? AV_DICT_APPEND : 0);
+#if CONFIG_SWRESAMPLE
+ } else if (o = av_opt_find(&swr_class, key, NULL, 0, 0)) {
+ av_strlcatf(swr_opts, sizeof(swr_opts), "%s=%s:", key, value);
+#endif
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Option '%s' not found\n", key);
+ av_free(value);
+ av_free(parsed_key);
+ return AVERROR_OPTION_NOT_FOUND;
}
av_free(value);
av_free(parsed_key);
}
+ if (strlen(swr_opts)) {
+ swr_opts[strlen(swr_opts) - 1] = 0;
+ av_opt_set(ctx->graph, "aresample_swr_opts", swr_opts, 0);
+ }
+
这样,在graph中配置上filter_size=16:phase_shift=6
,然后,load graph的时候并不会在avfiltergraph里面进行解析,播放的时候,调用avfilter_graph_reconfig
,调用栈如下:
query_formats(AVFilterGraph * graph, void * log_ctx) (ffmpeg/libavfilter/avfiltergraph.c:747)
graph_config_formats(AVFilterGraph * graph, void * log_ctx) (ffmpeg/libavfilter/avfiltergraph.c:1373)
avfilter_graph_reconfig(AVFilterGraph * graphctx, void * log_ctx) (ffmpeg/libavfilter/avfiltergraph.c:1519)
movie_async_activate(AVFilterContext * ctx) (ffmpeg/libavfilter/src_movie_async.c:1254)
ff_filter_activate(AVFilterContext * filter) (ffmpeg/libavfilter/avfilter.c:1565)
ff_filter_graph_run_all(AVFilterGraph * graph) (ffmpeg/libavfilter/avfiltergraph.c:1718)
进入query_formats之后,获取opts,就是前面在graph中配置的opts:
opts = FF_FIELD_AT(char *, neg->conversion_opts_offset, *graph);
if (link->type == AVMEDIA_TYPE_AUDIO) {
snprintf(inst_opts, sizeof(inst_opts), "converter=%d:%s", convert_needed, opts ? opts : "");
opts = inst_opts;
}
snprintf(inst_name, sizeof(inst_name), "auto_%s", neg->conversion_filter);
ret = avfilter_graph_create_filter(&convert, filter, inst_name, opts, NULL, graph);
if (ret < 0)
return ret;
if ((ret = avfilter_insert_filter(link, convert, 0, 0)) < 0)
return ret;
最后创建filter的时候,就会将这些options设置到fitler上。
附:ffmpeg help命令的解析
当用户在命令行中输入ffmpeg -h
或ffmpeg -help
时,show_help_default
函数会被调用,输出帮助信息。该函数还可以在其他情况下被调用,例如当用户输入无效的命令行选项时,或者当用户输入ffmpeg -h <选项>
时,显示特定选项的帮助信息。
ffmpeg --help full
如果是help full
,会有这样的调用层次,并且show_avoptions和show_advanced都被赋值为1:
show_help
- show_help_default
- show_help_options
在show_help_options中:
if (opt && *opt) {
if (!strcmp(opt, "long"))
show_advanced = 1;
else if (!strcmp(opt, "full"))
show_advanced = show_avoptions = 1;
else
av_log(NULL, AV_LOG_ERROR, "Unknown help option '%s'.\n", opt);
}
show_avoptions的分支中:
if (show_avoptions) {
int flags = AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM;
show_help_children(avcodec_get_class(), flags);
show_help_children(avformat_get_class(), flags);
#if CONFIG_SWSCALE
show_help_children(sws_get_class(), flags);
#endif
#if CONFIG_SWRESAMPLE
show_help_children(swr_get_class(), AV_OPT_FLAG_AUDIO_PARAM);
#endif
show_help_children(avfilter_get_class(), AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM);
show_help_children(av_bsf_get_class(), AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_BSF_PARAM);
}
这里,会遍历avcodec
,avformat
,sws
,swr
,avfilter
,av_bsf
的class,将其children class
对应的option全部显示出来。