[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