[FFmpeg-devel] [PATCH 2/2] lavfi/volume: support volume normalization through metadata.

Clément Bœsch ubitux at gmail.com
Fri Mar 1 18:07:26 CET 2013


---
 doc/filters.texi                 | 27 +++++++++++++++++++++++++++
 libavfilter/af_volume.c          | 33 ++++++++++++++++++++++++++-------
 libavfilter/af_volume.h          |  1 +
 libavfilter/x86/af_volume_init.c |  2 +-
 4 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 0d7856a..aa77a47 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -1224,6 +1224,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.
@@ -1263,6 +1264,11 @@ precision of the volume scaling.
 @item double
 64-bit floating-point; limits input sample format to DBL.
 @end table
+
+ at item metadata
+Set the frame metadata key to read volume adjustment value from. The main
+purpose of this option is to be used along with filters injecting volume
+metadata information such as @ref{ebur128}. See examples for more details.
 @end table
 
 @subsection Examples
@@ -1287,6 +1293,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=metadata=lavfi.r128.volume 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=metadata=lavfi.r128.volume,ebur128=video=1  [r128-1][out1];
+  [r128-0]          pad=iw*2                                           [padded];
+  [padded][r128-1]  overlay=w'
+ at end example
 @end itemize
 
 @section volumedetect
@@ -6242,6 +6266,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
@@ -6294,6 +6319,8 @@ output frames, each of them containing a volume adjustment metadata
 @var{lavfi.r128.volume}. 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
 
 Example of real-time graph using @command{ffplay}, with a EBU scale meter +18:
diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
index 5ffa1fe..717497c 100644
--- a/libavfilter/af_volume.c
+++ b/libavfilter/af_volume.c
@@ -51,11 +51,18 @@ static const AVOption volume_options[] = {
         { "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" },
+    { "metadata", "set the metadata key for volume adjustment", OFFSET(metadata), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = A|F },
     { NULL },
 };
 
 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;
@@ -69,8 +76,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
         return ret;
 
     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 {
@@ -79,7 +85,6 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
                precision_str[vol->precision]);
     }
 
-    av_opt_free(vol);
     return ret;
 }
 
@@ -183,13 +188,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->metadata)
             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->metadata)
             vol->scale_samples = scale_samples_s16_small;
         else
             vol->scale_samples = scale_samples_s16;
@@ -228,11 +233,18 @@ static int config_output(AVFilterLink *outlink)
 
 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *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->audio->nb_samples;
     AVFilterBufferRef *out_buf;
 
+    if (vol->metadata) {
+        AVDictionaryEntry *e = av_dict_get(buf->metadata, vol->metadata, 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);
 
@@ -281,6 +293,12 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
     return ff_filter_frame(outlink, out_buf);
 }
 
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    VolumeContext *vol = ctx->priv;
+    av_opt_free(vol);
+}
+
 static const AVFilterPad avfilter_af_volume_inputs[] = {
     {
         .name           = "default",
@@ -305,6 +323,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..4deca9c 100644
--- a/libavfilter/af_volume.h
+++ b/libavfilter/af_volume.h
@@ -48,6 +48,7 @@ typedef struct VolumeContext {
     void (*scale_samples)(uint8_t *dst, const uint8_t *src, int nb_samples,
                           int volume);
     int samples_align;
+    char *metadata;
 } VolumeContext;
 
 void ff_volume_init_x86(VolumeContext *vol);
diff --git a/libavfilter/x86/af_volume_init.c b/libavfilter/x86/af_volume_init.c
index beee8ca..c018ce5 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->metadata) {
             vol->scale_samples = ff_scale_samples_s16_sse2;
             vol->samples_align = 8;
         }
-- 
1.8.1.4



More information about the ffmpeg-devel mailing list