[FFmpeg-devel] [PATCH] lavfi: add aeval filter

Stefano Sabatini stefasab at gmail.com
Mon Dec 2 17:59:05 CET 2013


---
 doc/filters.texi            |  60 ++++++++++++++++
 libavfilter/Makefile        |   1 +
 libavfilter/allfilters.c    |   1 +
 libavfilter/asrc_aevalsrc.c | 162 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 224 insertions(+)

diff --git a/doc/filters.texi b/doc/filters.texi
index 0804bb4..40dec9c 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -435,6 +435,66 @@ aecho=0.8:0.9:1000|1800:0.3|0.25
 @end example
 @end itemize
 
+ at section aeval
+
+Modify an audio signal according to the specified expression.
+
+This source accepts in input one or more expressions (one for each
+channel), which are evaluated and used to modify a corresponding audio
+signal.
+
+This filter accepts the following options:
+
+ at table @option
+ at item exprs
+Set the '|'-separated expressions list for each separate channel. If
+the number of input channels is greater than the number of
+expressions, the last specified expression is repeated for the other
+channels as well.
+
+Default value is "val".
+ at end table
+
+Each expression in @var{exprs} can contain the following constants:
+
+ at table @option
+ at item pts
+presentation timestamp of the evaluated sample expressed in seconds, starting from 0.
+This corresponds to the timestamp of the first frame sample.
+
+ at item n
+number of the evaluated sample, starting from 0
+
+ at item s
+sample rate
+
+ at item t
+time of the evaluated sample expressed in seconds, starting from 0.
+This corresponds to the time of the first frame sample.
+
+ at item val
+the value of the currently processed frame
+ at end table
+
+Note: this filter is slow. For faster processing you should use a
+dedicated filter.
+
+ at subsection Examples
+
+ at itemize
+ at item
+Half volume:
+ at example
+aeval=val/2
+ at end example
+
+ at item
+Invert phase of the second channel:
+ at example
+eval=val|-val
+ at end example
+ at end itemize
+
 @section afade
 
 Apply fade-in/out effect to input audio.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 3d8a763..a419a7b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -53,6 +53,7 @@ OBJS-$(CONFIG_AVCODEC)                       += avcodec.o
 OBJS-$(CONFIG_ACONVERT_FILTER)               += af_aconvert.o
 OBJS-$(CONFIG_ADELAY_FILTER)                 += af_adelay.o
 OBJS-$(CONFIG_AECHO_FILTER)                  += af_aecho.o
+OBJS-$(CONFIG_AEVAL_FILTER)                  += asrc_aevalsrc.o
 OBJS-$(CONFIG_AFADE_FILTER)                  += af_afade.o
 OBJS-$(CONFIG_AFORMAT_FILTER)                += af_aformat.o
 OBJS-$(CONFIG_AINTERLEAVE_FILTER)            += f_interleave.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 04b4973..bd23b0a 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -50,6 +50,7 @@ void avfilter_register_all(void)
 #endif
     REGISTER_FILTER(ADELAY,         adelay,         af);
     REGISTER_FILTER(AECHO,          aecho,          af);
+    REGISTER_FILTER(AEVAL,          aeval,          af);
     REGISTER_FILTER(AFADE,          afade,          af);
     REGISTER_FILTER(AFORMAT,        aformat,        af);
     REGISTER_FILTER(AINTERLEAVE,    ainterleave,    af);
diff --git a/libavfilter/asrc_aevalsrc.c b/libavfilter/asrc_aevalsrc.c
index 41ad632..674176e 100644
--- a/libavfilter/asrc_aevalsrc.c
+++ b/libavfilter/asrc_aevalsrc.c
@@ -34,16 +34,20 @@
 #include "internal.h"
 
 static const char * const var_names[] = {
+    "pts",          ///< presentation timestamp
     "n",            ///< number of frame
     "t",            ///< timestamp expressed in seconds
     "s",            ///< sample rate
+    "val",          ///< currently processed value
     NULL
 };
 
 enum var_name {
+    VAR_PTS,
     VAR_N,
     VAR_T,
     VAR_S,
+    VAR_VAL,
     VAR_VARS_NB
 };
 
@@ -220,6 +224,7 @@ static int request_frame(AVFilterLink *outlink)
     return ff_filter_frame(outlink, samplesref);
 }
 
+#if CONFIG_AEVALSRC_FILTER
 static const AVFilterPad aevalsrc_outputs[] = {
     {
         .name          = "default",
@@ -241,3 +246,160 @@ AVFilter ff_asrc_aevalsrc = {
     .outputs       = aevalsrc_outputs,
     .priv_class    = &aevalsrc_class,
 };
+
+#endif /* CONFIG_AEVALSRC_FILTER */
+
+#define OFFSET(x) offsetof(EvalContext, x)
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption aeval_options[]= {
+    { "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = "val"}, .flags = FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(aeval);
+
+static int aeval_query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats = NULL;
+    AVFilterChannelLayouts *layouts;
+    static const enum AVSampleFormat sample_fmts[] = {
+        AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE
+    };
+
+    layouts = ff_all_channel_layouts();
+    if (!layouts)
+        return AVERROR(ENOMEM);
+    ff_set_common_channel_layouts(ctx, layouts);
+
+    formats = ff_make_format_list(sample_fmts);
+    if (!formats)
+        return AVERROR(ENOMEM);
+    ff_set_common_formats(ctx, formats);
+
+    formats = ff_all_samplerates();
+    if (!formats)
+        return AVERROR(ENOMEM);
+    ff_set_common_samplerates(ctx, formats);
+
+    return 0;
+}
+
+static int aeval_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    EvalContext *eval = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    char *args1 = av_strdup(eval->exprs);
+    char *expr, *buf;
+    int ret, i, nb_channels = 0;
+
+    /* parse expressions */
+    buf = args1;
+    while (expr = av_strtok(buf, "|", &buf)) {
+        if (!av_dynarray2_add((void **)&eval->expr, &nb_channels, sizeof(*eval->expr), NULL)) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
+        eval->expr[nb_channels-1] = NULL;
+        ret = av_expr_parse(&eval->expr[nb_channels - 1], expr, var_names,
+                            NULL, NULL, NULL, NULL, 0, ctx);
+        if (ret < 0)
+            goto end;
+    }
+
+    if (nb_channels != inlink->channels)
+        av_log(ctx, AV_LOG_WARNING, "Mismatch between number of processed channels (%d), "
+               "and number of parsed expressions (%d)\n", nb_channels, inlink->channels);
+
+    if (nb_channels < inlink->channels) {
+        for (i = nb_channels; i < inlink->channels; i++) {
+            ret = av_expr_parse(&eval->expr[i], expr, var_names,
+                                NULL, NULL, NULL, NULL, 0, ctx);
+            if (ret < 0)
+                goto end;
+        }
+    }
+
+    eval->n = 0;
+    eval->var_values[VAR_S] = inlink->sample_rate;
+    eval->var_values[VAR_PTS] = NAN;
+    eval->var_values[VAR_T] = NAN;
+end:
+    av_free(args1);
+    return ret;
+}
+
+#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
+#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    EvalContext *eval     = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    int nb_samples        = in->nb_samples;
+    AVFrame *out;
+    int i, j;
+
+    /* do volume scaling in-place if input buffer is writable */
+    if (av_frame_is_writable(in)) {
+        out = in;
+    } else {
+        out = ff_get_audio_buffer(inlink, nb_samples);
+        if (!out)
+            return AVERROR(ENOMEM);
+        av_frame_copy_props(out, in);
+    }
+
+    eval->var_values[VAR_PTS] = TS2D(in->pts);
+    eval->var_values[VAR_T]   = TS2T(in->pts, inlink->time_base);
+
+    /* evaluate expression for each single sample and for each channel */
+    for (i = 0; i < nb_samples; i++, eval->n++) {
+        eval->var_values[VAR_N] = eval->n;
+        for (j = 0; j < inlink->channels; j++) {
+            eval->var_values[VAR_VAL] = *((double *) in->extended_data[j] + i);
+            *((double *) out->extended_data[j] + i) =
+                av_expr_eval(eval->expr[j], eval->var_values, NULL);
+        }
+    }
+
+    if (in != out)
+        av_frame_free(&in);
+
+    return ff_filter_frame(outlink, out);
+}
+
+#if CONFIG_AEVAL_FILTER
+
+static const AVFilterPad aeval_inputs[] = {
+    {
+        .name           = "default",
+        .type           = AVMEDIA_TYPE_AUDIO,
+        .filter_frame   = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad aeval_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_AUDIO,
+        .config_props  = aeval_config_output,
+    },
+    { NULL }
+};
+
+AVFilter ff_af_aeval = {
+    .name          = "aeval",
+    .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
+    .query_formats = aeval_query_formats,
+    .uninit        = uninit,
+    .priv_size     = sizeof(EvalContext),
+    .inputs        = aeval_inputs,
+    .outputs       = aeval_outputs,
+    .priv_class    = &aeval_class,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+
+#endif /* CONFIG_AEVAL_FILTER */
-- 
1.8.1.2



More information about the ffmpeg-devel mailing list