[FFmpeg-devel] [PATCH] lavfi/volume: add dynamic expression evaluation

Stefano Sabatini stefasab at gmail.com
Mon Dec 23 20:15:25 CET 2013


On date Monday 2013-12-23 19:48:25 +0100, Nicolas George encoded:
> Le tridi 3 nivôse, an CCXXII, Stefano Sabatini a écrit :
> > Updated with a solution similar to that of overlay. I don't plan to
> > implement the av_expr_is_const() thing, and will probably push this
> > variant unless there are objections.
> > 
> > Comments are welcome.
> > -- 
> > FFmpeg = Free & Funny Magnificient Proud Elected Geisha
> 
> > >From 9afa55b01eb84e6615fff877dc718d192305abf3 Mon Sep 17 00:00:00 2001
> > From: Stefano Sabatini <stefasab at gmail.com>
> > Date: Sat, 23 Feb 2013 00:17:17 +0100
> > Subject: [PATCH] lavfi/volume: support volume expression and per-frame
> >  expression evaluation
> > 
> > The eval mode allows to evaluate the expression per-frame or just at
> > init.
> > 
> > In particular, address ticket #3234.
> > 
> > TODO: bump micro
> > ---
> >  doc/filters.texi        |  55 ++++++++++++++++++-
> >  libavfilter/af_volume.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++--
> >  libavfilter/af_volume.h |  27 +++++++++
> >  3 files changed, 217 insertions(+), 7 deletions(-)
> > 
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index 8bcfd82..d29bedc 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -1796,7 +1796,7 @@ The filter accepts the following options:
> >  @table @option
> >  
> >  @item volume
> > -Expresses how the audio volume will be increased or decreased.
> > +Set audio volume expression.
> >  
> >  Output values are clipped to the maximum value.
> >  
> > @@ -1805,7 +1805,7 @@ The output audio volume is given by the relation:
> >  @var{output_volume} = @var{volume} * @var{input_volume}
> >  @end example
> >  
> > -Default value for @var{volume} is 1.0.
> > +Default value for @var{volume} is "1.0".
> >  
> >  @item precision
> >  Set the mathematical precision.
> > @@ -1821,8 +1821,53 @@ precision of the volume scaling.
> >  @item double
> >  64-bit floating-point; limits input sample format to DBL.
> >  @end table
> > +
> > + at item eval
> > +Set when the volume expression is evaluated.
> > +
> > +It accepts the following values:
> > + at table @samp
> > + at item init
> > +only evaluate expression once during the filter initialization
> > +
> > + at item frame
> > +evaluate expression for each incoming frame
> > + at end table
> > +
> > +Default value is @samp{init}.
> > + at end table
> > +
> > +The volume expression can contain the following parameters.
> > +
> > + at table @option
> > + at item n
> > +frame number (starting at zero)
> > + at item nb_channels
> > +number of channels
> > + at item nb_consumed_samples
> > +number of samples consumed by the filter
> > + at item nb_samples
> > +number of samples in the current frame
> > + at item pos
> > +original frame position in the file
> > + at item pts
> > +frame PTS
> > + at item sample_rate
> > +sample rate
> > + at item startpts
> > +PTS at start of stream
> > + at item startt
> > +time at start of stream
> > + at item t
> > +frame time
> > + at item tb
> > +timestamp timebase
> >  @end table
> >  
> > +Note that when @option{eval} is set to @samp{init} only the
> > + at var{sample_rate} and @var{tb} variables are available, all other
> > +variables will evaluate to NAN.
> > +
> >  @subsection Examples
> >  
> >  @itemize
> > @@ -1845,6 +1890,12 @@ Increase input audio power by 6 decibels using fixed-point precision:
> >  @example
> >  volume=volume=6dB:precision=fixed
> >  @end example
> > +
> > + at item
> > +Fade volume after time 10 with an annihilation period of 5 seconds:
> > + at example
> > +volume='if(lt(t,10),1,max(1-(t-10)/5,0))':eval=frame
> > + at end example
> >  @end itemize
> >  
> >  @section volumedetect
> > diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
> > index 21fe9a1..9bfd840 100644
> > --- a/libavfilter/af_volume.c
> > +++ b/libavfilter/af_volume.c
> > @@ -29,6 +29,7 @@
> >  #include "libavutil/eval.h"
> >  #include "libavutil/float_dsp.h"
> >  #include "libavutil/opt.h"
> > +#include "libavutil/time.h"
> >  #include "audio.h"
> >  #include "avfilter.h"
> >  #include "formats.h"
> > @@ -39,24 +40,42 @@ static const char *precision_str[] = {
> >      "fixed", "float", "double"
> >  };
> >  
> > +static const char *const var_names[] = {
> > +    "n",                   ///< frame number (starting at zero)
> > +    "nb_channels",         ///< number of channels
> > +    "nb_consumed_samples", ///< number of samples consumed by the filter
> > +    "nb_samples",          ///< number of samples in the current frame
> > +    "pos",                 ///< position in the file of the frame
> > +    "pts",                 ///< frame presentation timestamp
> > +    "sample_rate",         ///< sample rate
> > +    "startpts",            ///< PTS at start of stream
> > +    "startt",              ///< time at start of stream
> > +    "t",                   ///< time in the file of the frame
> > +    "tb",                  ///< timebase
> > +    NULL
> > +};
> > +
> >  #define OFFSET(x) offsetof(VolumeContext, x)
> >  #define A AV_OPT_FLAG_AUDIO_PARAM
> >  #define F AV_OPT_FLAG_FILTERING_PARAM
> >  
> >  static const AVOption volume_options[] = {
> > -    { "volume", "set volume adjustment",
> > -            OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0, 0x7fffff, A|F },
> > +    { "volume", "set volume adjustment expression",
> > +            OFFSET(volume_expr), AV_OPT_TYPE_STRING, { .str = "1.0" }, .flags = A|F },
> >      { "precision", "select mathematical precision",
> >              OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A|F, "precision" },
> >          { "fixed",  "select 8-bit fixed-point",     0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED  }, INT_MIN, INT_MAX, A|F, "precision" },
> >          { "float",  "select 32-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT  }, INT_MIN, INT_MAX, A|F, "precision" },
> >          { "double", "select 64-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A|F, "precision" },
> > +    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, .flags = A|F, "eval" },
> > +         { "init",  "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = A|F, .unit = "eval" },
> > +         { "frame", "eval expressions per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = A|F, .unit = "eval" },
> >      { NULL }
> >  };
> >  
> >  AVFILTER_DEFINE_CLASS(volume);
> >  
> > -static av_cold int init(AVFilterContext *ctx)
> > +static void set_volume(AVFilterContext *ctx)
> >  {
> >      VolumeContext *vol = ctx->priv;
> >  
> > @@ -70,10 +89,41 @@ static av_cold int init(AVFilterContext *ctx)
> >                 vol->volume, 20.0*log(vol->volume)/M_LN10,
> >                 precision_str[vol->precision]);
> >      }
> > +}
> > +
> > +static int set_expr(AVExpr **pexpr, const char *expr, void *log_ctx)
> > +{
> > +    int ret;
> > +    AVExpr *old = NULL;
> > +
> > +    if (*pexpr)
> > +        old = *pexpr;
> > +    ret = av_expr_parse(pexpr, expr, var_names,
> > +                        NULL, NULL, NULL, NULL, 0, log_ctx);
> > +    if (ret < 0) {
> > +        av_log(log_ctx, AV_LOG_ERROR,
> > +               "Error when evaluating the volume expression '%s'\n", expr);
> > +        *pexpr = old;
> > +        return ret;
> > +    }
> >  
> > +    av_expr_free(old);
> >      return 0;
> >  }
> >  
> > +static av_cold int init(AVFilterContext *ctx)
> > +{
> > +    VolumeContext *vol = ctx->priv;
> > +    return set_expr(&vol->volume_pexpr, vol->volume_expr, ctx);
> > +}
> > +
> > +static av_cold void uninit(AVFilterContext *ctx)
> > +{
> > +    VolumeContext *vol = ctx->priv;
> > +    av_expr_free(vol->volume_pexpr);
> > +    av_opt_free(vol);
> > +}
> > +
> >  static int query_formats(AVFilterContext *ctx)
> >  {
> >      VolumeContext *vol = ctx->priv;
> > @@ -204,25 +254,102 @@ static int config_output(AVFilterLink *outlink)
> >      AVFilterContext *ctx = outlink->src;
> >      VolumeContext *vol   = ctx->priv;
> >      AVFilterLink *inlink = ctx->inputs[0];
> > +    int ret;
> >  
> >      vol->sample_fmt = inlink->format;
> >      vol->channels   = inlink->channels;
> >      vol->planes     = av_sample_fmt_is_planar(inlink->format) ? vol->channels : 1;
> >  
> > +    vol->var_values[VAR_N] =
> > +    vol->var_values[VAR_NB_CONSUMED_SAMPLES] =
> > +    vol->var_values[VAR_NB_SAMPLES] =
> > +    vol->var_values[VAR_POS] =
> > +    vol->var_values[VAR_PTS] =
> > +    vol->var_values[VAR_STARTPTS] =
> > +    vol->var_values[VAR_STARTT] =
> > +    vol->var_values[VAR_T] = NAN;
> > +
> > +    vol->var_values[VAR_NB_CHANNELS] = inlink->channels;
> > +    vol->var_values[VAR_TB]          = av_q2d(inlink->time_base);
> > +    vol->var_values[VAR_SAMPLE_RATE] = inlink->sample_rate;
> > +
> > +    av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f SAMPLE_RATE:%f\n",
> > +           vol->var_values[VAR_TB], vol->var_values[VAR_SAMPLE_RATE]);
> > +
> >      volume_init(vol);
> >  
> > +    if (vol->eval_mode == EVAL_MODE_INIT) {
> 
> > +        if ((ret = av_expr_parse(&vol->volume_pexpr, vol->volume_expr,
> > +                                 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
> > +            av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n",
> > +                   vol->volume_expr);
> > +            return ret;
> > +        }
> 
> It looks like duplicated code with set_expr().
> 
> > +
> > +        vol->volume = av_expr_eval(vol->volume_pexpr, vol->var_values, NULL);
> > +        if (isnan(vol->volume)) {
> > +            av_log(ctx, AV_LOG_ERROR, "Invalid value NaN for volume\n");
> > +            return AVERROR(EINVAL);
> > +        }
> > +
> > +        set_volume(ctx);
> 

> This bit could probably be factored out, that would make the patch with
> command processing slightly shorter.

Correct. I also added the vol variable, which may be useful for
example for doing interactive volume = vol/2.
-- 
FFmpeg = Friendly and Fabulous Magical Pacific Emblematic Geek
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-lavfi-volume-support-volume-expression-and-per-frame.patch
Type: text/x-diff
Size: 13390 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20131223/38b73160/attachment.bin>


More information about the ffmpeg-devel mailing list