下面这样一条命令
ffmpeg -i /Users/user/video/mp4/output.wav -ac 1 /Users/user/video/mp4/output1.wav
我们会形成下面这样的图
图1
现在有个问题link4的channel怎么设置的?
static int pick_format(AVFilterLink *link, AVFilterLink *ref){link->channel_layout = link->incfg.channel_layouts->channel_layouts[0];if ((link->channels = FF_LAYOUT2COUNT(link->channel_layout)))link->channel_layout = 0;elselink->channels = av_get_channel_layout_nb_channels(link->channel_layout);}
我们通过上面代码可以知道link的channel来自于link->incfg.channel_layouts->channel_layouts[0]的转化。
那么下一个问题incfg是什么。我们知道link的输入是link->src对应filter,那么这个配置也应该是src对应的filter进行配置。
通过图1我们知道link4的src是afromat。
我们进入afromat的filter我们发现了下面的代码
static int query_formats(AVFilterContext *ctx){AFormatContext *s = ctx->priv;ret = ff_set_common_channel_layouts(ctx, s->channel_layouts ? s->channel_layouts :ff_all_channel_counts());}
我们知道了s->channel_layouts 从ctx->priv
也就是下面结构体重的channel_layouts
typedef struct AFormatContext {const AVClass *class;AVFilterFormats *formats;AVFilterFormats *sample_rates;AVFilterChannelLayouts *channel_layouts;char *formats_str;char *sample_rates_str;char *channel_layouts_str;
} AFormatContext;
那么aformat的参数又来自于下面的设置
static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
{sample_fmts = choose_sample_fmts(ofilter);sample_rates = choose_sample_rates(ofilter);channel_layouts = choose_channel_layouts(ofilter);args[0] = 0;if (sample_fmts)av_strlcatf(args, sizeof(args), "sample_fmts=%s:",sample_fmts);if (sample_rates)av_strlcatf(args, sizeof(args), "sample_rates=%s:",sample_rates);if (channel_layouts)av_strlcatf(args, sizeof(args), "channel_layouts=%s:",channel_layouts);ret = avfilter_graph_create_filter(&format,avfilter_get_by_name("aformat"),name, args, NULL, fg->graph); }
可以看到来自于choose_channel_layouts
choose_channel_layouts是个宏定义,理解起来比较费劲
DEF_CHOOSE_FORMAT(channel_layouts, uint64_t, channel_layout, channel_layouts, 0,GET_CH_LAYOUT_NAME)
#define DEF_CHOOSE_FORMAT(suffix, type, var, supported_list, none, get_name) \
static char *choose_ ## suffix (OutputFilter *ofilter) \
{ \if (ofilter->var != none) { \get_name(ofilter->var); \return av_strdup(name); \} else if (ofilter->supported_list) { \const type *p; \AVIOContext *s = NULL; \uint8_t *ret; \int len; \\if (avio_open_dyn_buf(&s) < 0) \exit_program(1); \\for (p = ofilter->supported_list; *p != none; p++) { \get_name(*p); \avio_printf(s, "%s|", name); \} \len = avio_close_dyn_buf(s, &ret); \ret[len - 1] = 0; \return ret; \} else \return NULL; \
}
将相应的占位符进行替换后会发现channel_layouts取的是OutputFilter的是channel_layout字段。
接下来我们开始寻找OutputFilter->channel_layout怎么设置的?
static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter,AVFilterInOut *out){case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);}int configure_filtergraph(FilterGraph *fg) {const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter :fg->graph_desc; if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0)goto fail;for (cur = outputs, i = 0; cur; cur = cur->next, i++)configure_output_filter(fg, fg->outputs[i], cur);
}
我们发现来自于FilterGraph的output
static int open_output_file(OptionsContext *o, const char *filename){OutputFilter *f = ost->filter;switch (ist->st->codecpar->codec_type) {case AVMEDIA_TYPE_VIDEO: ost = new_video_stream (o, oc, src_idx); break;case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream (o, oc, src_idx); break;}if (ost->enc_ctx->channels) {f->channel_layout = av_get_default_channel_layout(ost->enc_ctx->channels);}}
现在的问题就成了ost->enc_ctx->channels怎么来的?
static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
{audio_enc = ost->enc_ctx;if (!ost->stream_copy) {char *sample_fmt = NULL;MATCH_PER_STREAM_OPT(audio_channels, i, audio_enc->channels, oc, st);}
}
MATCH_PER_STREAM_OPT的定义如下
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\int i, ret, matches = 0;\SpecifierOpt *so;\for (i = 0; i < o->nb_ ## name; i++) {\char *spec = o->name[i].specifier;\if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\outvar = o->name[i].u.type;\so = &o->name[i];\matches++;\} else if (ret < 0)\exit_program(1);\}\if (matches > 1)\WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\
}
我们将相应占位符带入后发现其实找的是
OptionsContext->audio_channels
typedef struct OptionsContext {OptionGroup *g;/* input/output options */int64_t start_time;int64_t start_time_eof;int seek_timestamp;const char *format;SpecifierOpt *codec_names;int nb_codec_names;SpecifierOpt *audio_channels;
}
下面的问题来了audio_channels怎么设置的?
在ffmpeg_opt.c
#define OFFSET(x) offsetof(OptionsContext, x)
const OptionDef options[] = { { "ac", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC |OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_channels) },"set number of audio channels", "channels" },
}static int open_files(OptionGroupList *l, const char *inout,int (*open_file)(OptionsContext*, const char*))
{int i, ret;for (i = 0; i < l->nb_groups; i++) {OptionGroup *g = &l->groups[i];OptionsContext o;init_options(&o);o.g = g;ret = parse_optgroup(&o, g);
}
}int parse_optgroup(void *optctx, OptionGroup *g)
{for (i = 0; i < g->nb_opts; i++) {Option *o = &g->opts[i];ret = write_option(optctx, o->opt, o->key, o->val);if (ret < 0)return ret;}
}
这样就option_context进行了设置。