下面这样一条命令
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;
else
link->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());
}
ff_set_common_channel_layouts为什么可以设置incfg.channel_layouts呢?
ret = ff_set_common_channel_layouts(ctx, s->channel_layouts ? s->channel_layouts :
ff_all_channel_counts());
int ff_set_common_channel_layouts(AVFilterContext *ctx,
AVFilterChannelLayouts *channel_layouts)
{
SET_COMMON_FORMATS(ctx, channel_layouts,
ff_channel_layouts_ref, ff_channel_layouts_unref);
}
#define SET_COMMON_FORMATS(ctx, fmts, ref_fn, unref_fn) \
int count = 0, i; \
\
if (!fmts) \
return AVERROR(ENOMEM); \
\
for (i = 0; i < ctx->nb_inputs; i++) { \
if (ctx->inputs[i] && !ctx->inputs[i]->outcfg.fmts) { \
int ret = ref_fn(fmts, &ctx->inputs[i]->outcfg.fmts); \
if (ret < 0) { \
return ret; \
} \
count++; \
} \
} \
for (i = 0; i < ctx->nb_outputs; i++) { \
if (ctx->outputs[i] && !ctx->outputs[i]->incfg.fmts) { \
int ret = ref_fn(fmts, &ctx->outputs[i]->incfg.fmts); \
if (ret < 0) { \
return ret; \
} \
count++; \
} \
} \
\
if (!count) { \
unref_fn(&fmts); \
} \
\
return 0;
ref_fn(fmts, &ctx->outputs[i]->incfg.fmts)这里实际上最后就转成了
ff_channel_layouts_ref(channel_layouts, &ctx->outputs[i]->incfg.channel_layouts)
ff_channel_layouts_ref做了什么事情?
int ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
{
FORMATS_REF(f, ref, ff_formats_unref);
}
#define FORMATS_REF(f, ref, unref_fn) \
void *tmp; \
\
if (!f) \
return AVERROR(ENOMEM); \
\
tmp = av_realloc_array(f->refs, sizeof(*f->refs), f->refcount + 1); \
if (!tmp) { \
unref_fn(&f); \
return AVERROR(ENOMEM); \
} \
f->refs = tmp; \
f->refs[f->refcount++] = ref; \
*ref = f; \
return 0
上面代码看的人头晕,就是对outputs[i]->incfg相应的字段赋值。
我们知道了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进行了设置。