ffmpeg-aresample_swr_opts的解析

ffmpeg option的解析

ffmpeg -y -i /home/hui/2ch-16k.wav -filter_size 16 -phase_shift 6 -ar 48000 out.wav

其中-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"。

ffmpeg-aresample_swr_opts的解析_第1张图片

aresample_swr_opts为什么不能通过命令行设置?

ffmpeg -i chengdu-44100.wav "aresample_swr_opts=filter_size=16:phase_shift=6" -ar 48000 chengdu-48000-ff.wav

ffmpeg命令行不支持参数aresample_swr_opts。

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如前面所述,在ffmpeg命令行,支持如下写法:

    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,将swr_opts中的option作为swresample的option参数传入。

ffmpeg help命令的解析

当用户在命令行中输入ffmpeg -hffmpeg -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全部显示出来。

最后的实现

在avfilter里面类似cmdutils.c里面的逻辑,添加对swr的处理:

+#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)