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

Stefano Sabatini stefasab at gmail.com
Sat Feb 23 01:00:40 CET 2013


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;
+    }
 
     /* 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



More information about the ffmpeg-devel mailing list