|
|
|
@ -181,6 +181,7 @@ typedef struct MixContext { |
|
|
|
|
#define OFFSET(x) offsetof(MixContext, x) |
|
|
|
|
#define A AV_OPT_FLAG_AUDIO_PARAM |
|
|
|
|
#define F AV_OPT_FLAG_FILTERING_PARAM |
|
|
|
|
#define T AV_OPT_FLAG_RUNTIME_PARAM |
|
|
|
|
static const AVOption amix_options[] = { |
|
|
|
|
{ "inputs", "Number of inputs.", |
|
|
|
|
OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT16_MAX, A|F }, |
|
|
|
@ -193,7 +194,7 @@ static const AVOption amix_options[] = { |
|
|
|
|
"renormalization when an input stream ends.", |
|
|
|
|
OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F }, |
|
|
|
|
{ "weights", "Set weight for each input.", |
|
|
|
|
OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, A|F }, |
|
|
|
|
OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, A|F|T }, |
|
|
|
|
{ NULL } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -504,12 +505,37 @@ static int activate(AVFilterContext *ctx) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
static void parse_weights(AVFilterContext *ctx) |
|
|
|
|
{ |
|
|
|
|
MixContext *s = ctx->priv; |
|
|
|
|
float last_weight = 1.f; |
|
|
|
|
int i, ret; |
|
|
|
|
char *p; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
s->weight_sum = 0.f; |
|
|
|
|
p = s->weights_str; |
|
|
|
|
for (i = 0; i < s->nb_inputs; i++) { |
|
|
|
|
last_weight = av_strtod(p, &p); |
|
|
|
|
s->weights[i] = last_weight; |
|
|
|
|
s->weight_sum += FFABS(last_weight); |
|
|
|
|
if (p && *p) { |
|
|
|
|
p++; |
|
|
|
|
} else { |
|
|
|
|
i++; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (; i < s->nb_inputs; i++) { |
|
|
|
|
s->weights[i] = last_weight; |
|
|
|
|
s->weight_sum += FFABS(last_weight); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
{ |
|
|
|
|
MixContext *s = ctx->priv; |
|
|
|
|
int i, ret; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->nb_inputs; i++) { |
|
|
|
|
AVFilterPad pad = { 0 }; |
|
|
|
@ -533,23 +559,7 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
if (!s->weights) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
|
|
p = s->weights_str; |
|
|
|
|
for (i = 0; i < s->nb_inputs; i++) { |
|
|
|
|
last_weight = av_strtod(p, &p); |
|
|
|
|
s->weights[i] = last_weight; |
|
|
|
|
s->weight_sum += FFABS(last_weight); |
|
|
|
|
if (p && *p) { |
|
|
|
|
p++; |
|
|
|
|
} else { |
|
|
|
|
i++; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (; i < s->nb_inputs; i++) { |
|
|
|
|
s->weights[i] = last_weight; |
|
|
|
|
s->weight_sum += FFABS(last_weight); |
|
|
|
|
} |
|
|
|
|
parse_weights(ctx); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
@ -604,6 +614,24 @@ fail: |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
|
|
|
|
char *res, int res_len, int flags) |
|
|
|
|
{ |
|
|
|
|
MixContext *s = ctx->priv; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); |
|
|
|
|
if (ret < 0) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
parse_weights(ctx); |
|
|
|
|
for (int i = 0; i < s->nb_inputs; i++) |
|
|
|
|
s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); |
|
|
|
|
calculate_scales(s, 0); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const AVFilterPad avfilter_af_amix_outputs[] = { |
|
|
|
|
{ |
|
|
|
|
.name = "default", |
|
|
|
@ -624,5 +652,6 @@ AVFilter ff_af_amix = { |
|
|
|
|
.query_formats = query_formats, |
|
|
|
|
.inputs = NULL, |
|
|
|
|
.outputs = avfilter_af_amix_outputs, |
|
|
|
|
.process_command = process_command, |
|
|
|
|
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS, |
|
|
|
|
}; |
|
|
|
|