[FFmpeg-devel] [PATCH 3/3] lavfi/volume: support volume normalization.

Clément Bœsch ubitux at gmail.com
Thu Mar 14 15:13:04 CET 2013


---
 doc/filters.texi                 | 26 ++++++++++++++++++++++++++
 libavfilter/af_volume.c          | 35 ++++++++++++++++++++++++++++-------
 libavfilter/af_volume.h          |  3 +++
 libavfilter/x86/af_volume_init.c |  2 +-
 4 files changed, 58 insertions(+), 8 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 780dafe..02981e8 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -1249,6 +1249,7 @@ out
 Convert the audio sample format, sample rate and channel layout. This filter is
 not meant to be used directly.
 
+ at anchor{volume}
 @section volume
 
 Adjust the input audio volume.
@@ -1272,6 +1273,10 @@ The output audio volume is given by the relation:
 @var{output_volume} = @var{volume} * @var{input_volume}
 @end example
 
+ at var{volnorm} is special value for the volume, and can be used along with a
+filter such as @ref{ebur128} placed prior to the volume filter in the
+filtergraph, and configured to inject the appropriate metadata information.
+
 Default value for @var{volume} is 1.0.
 
 @item precision
@@ -1312,6 +1317,24 @@ Increase input audio power by 6 decibels using fixed-point precision:
 @example
 volume=volume=6dB:precision=fixed
 @end example
+
+ at item
+Normalize in real-time an audio stream with @command{ffmpeg} and the help of
+the @ref{ebur128} filter:
+ at example
+ffmpeg -i input.mp3 -af ebur128=volnorm=I,volume=volnorm output.wav
+ at end example
+
+ at item
+Normalize the audio using @ref{ebur128} and observe its effect using
+ at command{ffplay}:
+ at example
+ffplay -f lavfi -i '
+                    amovie=input.mp3, ebur128=video=1:volnorm=I [r128-0][a];
+  [a]               volume=volnorm,   ebur128=video=1           [r128-1][out1];
+  [r128-0]          pad=iw*2                                    [padded];
+  [padded][r128-1]  overlay=w'
+ at end example
 @end itemize
 
 @section volumedetect
@@ -6496,6 +6519,7 @@ setpts='(RTCTIME - RTCSTART) / (TB * 1000000)'
 @end example
 @end itemize
 
+ at anchor{ebur128}
 @section ebur128
 
 EBU R128 scanner filter. This filter takes an audio stream as input and outputs
@@ -6549,6 +6573,8 @@ output frames, each of them containing a volume adjustment metadata
 
 Note: all the frames might not contain that a metadata.
 
+The main purpose of this option is to be used along with the @ref{volume} audio
+filter (refer to the filter documentation and examples for details).
 @end table
 
 @subsection Examples
diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
index 226ef93..540dfa0 100644
--- a/libavfilter/af_volume.c
+++ b/libavfilter/af_volume.c
@@ -45,7 +45,7 @@ static const char *precision_str[] = {
 
 static const AVOption volume_options[] = {
     { "volume", "set volume adjustment",
-            OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0, 0x7fffff, A|F },
+            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" },
@@ -56,6 +56,12 @@ static const AVOption volume_options[] = {
 
 AVFILTER_DEFINE_CLASS(volume);
 
+static void set_fixed_volume(VolumeContext *vol, double volume)
+{
+    vol->volume_i = (int)(volume * 256 + 0.5);
+    vol->volume   = vol->volume_i / 256.0;
+}
+
 static av_cold int init(AVFilterContext *ctx, const char *args)
 {
     VolumeContext *vol = ctx->priv;
@@ -68,9 +74,17 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
     if ((ret = av_opt_set_from_string(vol, args, shorthand, "=", ":")) < 0)
         return ret;
 
+    /* TODO: integrate "volnorm" as an expression constant */
+    if (!strcmp(vol->volume_expr, "volnorm")) {
+        vol->volume = 1.0;
+        vol->evalonce = 0;
+    } else {
+        vol->volume = av_strtod(vol->volume_expr, NULL);
+        vol->evalonce = 1;
+    }
+
     if (vol->precision == PRECISION_FIXED) {
-        vol->volume_i = (int)(vol->volume * 256 + 0.5);
-        vol->volume   = vol->volume_i / 256.0;
+        set_fixed_volume(vol, vol->volume);
         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 {
@@ -183,13 +197,13 @@ static void volume_init(VolumeContext *vol)
 
     switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
     case AV_SAMPLE_FMT_U8:
-        if (vol->volume_i < 0x1000000)
+        if (vol->volume_i < 0x1000000 && vol->evalonce)
             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)
+        if (vol->volume_i < 0x10000 && vol->evalonce)
             vol->scale_samples = scale_samples_s16_small;
         else
             vol->scale_samples = scale_samples_s16;
@@ -228,11 +242,18 @@ static int config_output(AVFilterLink *outlink)
 
 static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 {
-    VolumeContext *vol    = inlink->dst->priv;
-    AVFilterLink *outlink = inlink->dst->outputs[0];
+    AVFilterContext *ctx  = inlink->dst;
+    VolumeContext *vol    = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
     int nb_samples        = buf->nb_samples;
     AVFrame *out_buf;
 
+    if (!vol->evalonce) {
+        AVDictionaryEntry *e = av_dict_get(buf->metadata, "lavfi.volnorm", NULL, 0);
+        if (e)
+            set_fixed_volume(vol, av_strtod(e->value, NULL));
+    }
+
     if (vol->volume == 1.0 || vol->volume_i == 256)
         return ff_filter_frame(outlink, buf);
 
diff --git a/libavfilter/af_volume.h b/libavfilter/af_volume.h
index bd7932e..1e3a42b 100644
--- a/libavfilter/af_volume.h
+++ b/libavfilter/af_volume.h
@@ -39,6 +39,9 @@ typedef struct VolumeContext {
     const AVClass *class;
     AVFloatDSPContext fdsp;
     enum PrecisionType precision;
+    const char *volume_expr;    // TODO: make it a real expression
+    int evalonce;               // XXX: current means "do volume normalization"
+
     double volume;
     int    volume_i;
     int    channels;
diff --git a/libavfilter/x86/af_volume_init.c b/libavfilter/x86/af_volume_init.c
index beee8ca..29515bb 100644
--- a/libavfilter/x86/af_volume_init.c
+++ b/libavfilter/x86/af_volume_init.c
@@ -38,7 +38,7 @@ void ff_volume_init_x86(VolumeContext *vol)
     enum AVSampleFormat sample_fmt = av_get_packed_sample_fmt(vol->sample_fmt);
 
     if (sample_fmt == AV_SAMPLE_FMT_S16) {
-        if (EXTERNAL_SSE2(mm_flags) && vol->volume_i < 32768) {
+        if (EXTERNAL_SSE2(mm_flags) && vol->volume_i < 32768 && vol->evalonce) {
             vol->scale_samples = ff_scale_samples_s16_sse2;
             vol->samples_align = 8;
         }
-- 
1.8.1.5



More information about the ffmpeg-devel mailing list