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

Paul B Mahol onemda at gmail.com
Sat Feb 23 12:12:21 CET 2013


On 2/23/13, Stefano Sabatini <stefasab at gmail.com> wrote:
> TODO: update docs, bump micro
> ---
>  libavfilter/af_volume.c |  120
> ++++++++++++++++++++++++++++++++++++-----------
>  libavfilter/af_volume.h |   20 ++++++++
>  2 files changed, 113 insertions(+), 27 deletions(-)
>
> diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
> index 5ffa1fe..87898c8 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,13 +40,28 @@ static const char *precision_str[] = {
>      "fixed", "float", "double"
>  };
>
> +static const char *const var_names[] = {
> +    "N",           ///< frame number (starting at zero)
> +    "NB_CONSUMED_SAMPLES", ///< number of samples consumed by the filter
> (only audio)
> +    "NB_SAMPLES",  ///< number of samples in the current frame (only
> audio)
> +    "POS",         ///< original position in the file of the frame
> +    "PTS",         ///< original pts in the file of the frame
> +    "RTC_STARTT",  ///< start wallclock (RTC) time in micro seconds
> +    "SAMPLE_RATE", ///< sample rate (only audio)
> +    "STARTPTS",    ///< PTS at start of stream
> +    "STARTT",      ///< time at start of stream
> +    "T",           ///< original 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" },
> @@ -68,21 +84,25 @@ static av_cold int init(AVFilterContext *ctx, const char
> *args)
>      if ((ret = av_opt_set_from_string(vol, args, shorthand, "=", ":")) <
> 0)
>          return ret;
>
> -    if (vol->precision == PRECISION_FIXED) {
> -        vol->volume_i = (int)(vol->volume * 256 + 0.5);
> -        vol->volume   = vol->volume_i / 256.0;
> -        av_log(ctx, AV_LOG_VERBOSE, "volume:(%d/256)(%f)(%1.2fdB)
> precision:fixed\n",
> -               vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
> -    } else {
> -        av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
> -               vol->volume, 20.0*log(vol->volume)/M_LN10,
> -               precision_str[vol->precision]);
> +    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;
>      }
>
> -    av_opt_free(vol);
> +    vol->var_values[VAR_N       ] = 0.0;
> +    vol->var_values[VAR_STARTPTS] = vol->var_values[VAR_STARTT] = NAN;
> +
>      return ret;
>  }
>
> +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;
> @@ -182,18 +202,6 @@ static void volume_init(VolumeContext *vol)
>      vol->samples_align = 1;
>
>      switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
> -    case AV_SAMPLE_FMT_U8:
> -        if (vol->volume_i < 0x1000000)
> -            vol->scale_samples = scale_samples_u8_small;
> -        else
> -            vol->scale_samples = scale_samples_u8;
> -        break;
> -    case AV_SAMPLE_FMT_S16:
> -        if (vol->volume_i < 0x10000)
> -            vol->scale_samples = scale_samples_s16_small;
> -        else
> -            vol->scale_samples = scale_samples_s16;
> -        break;
>      case AV_SAMPLE_FMT_S32:
>          vol->scale_samples = scale_samples_s32;
>          break;
> @@ -221,20 +229,73 @@ static int config_output(AVFilterLink *outlink)
>      vol->channels   =
> av_get_channel_layout_nb_channels(inlink->channel_layout);
>      vol->planes     = av_sample_fmt_is_planar(inlink->format) ?
> vol->channels : 1;
>
> -    volume_init(vol);
> +    vol->var_values[VAR_TB]          = av_q2d(inlink->time_base);
> +    vol->var_values[VAR_RTC_STARTT]  = av_gettime();
> +    vol->var_values[VAR_SAMPLE_RATE] = inlink->sample_rate;
> +
> +    av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f SAMPLE_RATE:%f
> RTC_STARTT:%f\n",
> +           vol->var_values[VAR_TB],
> +           vol->var_values[VAR_SAMPLE_RATE],
> +           vol->var_values[VAR_RTC_STARTT]);
>
> +    volume_init(vol);
>      return 0;
>  }
>
> +#define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
> +#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, AVFilterBufferRef *buf)
>  {
> +    AVFilterContext *ctx = inlink->dst;
>      VolumeContext *vol    = inlink->dst->priv;
>      AVFilterLink *outlink = inlink->dst->outputs[0];
>      int nb_samples        = buf->audio->nb_samples;
>      AVFilterBufferRef *out_buf;
>
> -    if (vol->volume == 1.0 || vol->volume_i == 256)
> -        return ff_filter_frame(outlink, buf);
> +    if (isnan(vol->var_values[VAR_STARTPTS])) {
> +        vol->var_values[VAR_STARTPTS] = TS2D(buf->pts);
> +        vol->var_values[VAR_STARTT  ] = TS2T(buf->pts, inlink->time_base);
> +    }
> +    vol->var_values[VAR_PTS] = TS2D(buf->pts);
> +    vol->var_values[VAR_T  ] = TS2T(buf->pts, inlink->time_base);
> +    vol->var_values[VAR_POS] = buf->pos == -1 ? NAN : buf->pos;
> +    vol->volume = av_expr_eval(vol->volume_pexpr, vol->var_values, NULL);
> +    if (isnan(vol->volume)) {
> +        av_log(ctx, AV_LOG_WARNING, "Invalid value NaN for volume, setting
> to 0\n");
> +        vol->volume = 0;
> +    }
> +
> +    if (vol->precision == PRECISION_FIXED) {
> +        vol->volume_i = (int)(vol->volume * 256 + 0.5);
> +        vol->volume   = vol->volume_i / 256.0;
> +        av_log(ctx, AV_LOG_DEBUG, "volume_i:%d/256 volume:%f
> volume_db:%1.2f\n",
> +               vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
> +    } else {
> +        av_log(ctx, AV_LOG_DEBUG, "volume:%f volume_db:%1.2f\n",
> +               vol->volume, 20.0*log(vol->volume)/M_LN10);
> +    }
> +
> +    if (vol->volume == 1.0 || vol->volume_i == 256) {
> +        out_buf = buf;
> +        goto end;
> +    }
> +
> +    switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
> +    case AV_SAMPLE_FMT_U8:
> +        if (vol->volume_i < 0x1000000)
> +            vol->scale_samples = scale_samples_u8_small;
> +        else
> +            vol->scale_samples = scale_samples_u8;
> +        break;
> +    case AV_SAMPLE_FMT_S16:
> +        if (vol->volume_i < 0x10000)
> +            vol->scale_samples = scale_samples_s16_small;
> +        else
> +            vol->scale_samples = scale_samples_s16;
> +        break;
> +    }

What about other sample formats?

>
>      /* do volume scaling in-place if input buffer is writable */
>      if (buf->perms & AV_PERM_WRITE) {
> @@ -278,6 +339,10 @@ static int filter_frame(AVFilterLink *inlink,
> AVFilterBufferRef *buf)
>      if (buf != out_buf)
>          avfilter_unref_buffer(buf);
>
> +end:
> +    vol->var_values[VAR_N] += 1.0;
> +    vol->var_values[VAR_NB_CONSUMED_SAMPLES] += buf->audio->nb_samples;
> +
>      return ff_filter_frame(outlink, out_buf);
>  }
>
> @@ -305,6 +370,7 @@ AVFilter avfilter_af_volume = {
>      .query_formats  = query_formats,
>      .priv_size      = sizeof(VolumeContext),
>      .init           = init,
> +    .uninit         = uninit,
>      .inputs         = avfilter_af_volume_inputs,
>      .outputs        = avfilter_af_volume_outputs,
>      .priv_class     = &volume_class,
> diff --git a/libavfilter/af_volume.h b/libavfilter/af_volume.h
> index bd7932e..38e43a8 100644
> --- a/libavfilter/af_volume.h
> +++ b/libavfilter/af_volume.h
> @@ -25,6 +25,7 @@
>  #define AVFILTER_AF_VOLUME_H
>
>  #include "libavutil/common.h"
> +#include "libavutil/eval.h"
>  #include "libavutil/float_dsp.h"
>  #include "libavutil/opt.h"
>  #include "libavutil/samplefmt.h"
> @@ -35,10 +36,29 @@ enum PrecisionType {
>      PRECISION_DOUBLE,
>  };
>
> +enum VolumeVarName {
> +    VAR_N,
> +    VAR_NB_CONSUMED_SAMPLES,
> +    VAR_NB_SAMPLES,
> +    VAR_POS,
> +    VAR_PTS,
> +    VAR_RTC_STARTT,
> +    VAR_SAMPLE_RATE,
> +    VAR_STARTPTS,
> +    VAR_STARTT,
> +    VAR_T,
> +    VAR_TB,
> +    VAR_VARS_NB
> +};
> +
>  typedef struct VolumeContext {
>      const AVClass *class;
>      AVFloatDSPContext fdsp;
>      enum PrecisionType precision;
> +    const char *volume_expr;
> +    AVExpr *volume_pexpr;
> +    double var_values[VAR_VARS_NB];
> +
>      double volume;
>      int    volume_i;
>      int    channels;
> --
> 1.7.9.5
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


More information about the ffmpeg-devel mailing list