[FFmpeg-devel] [PATCH 1/2] lavfi/ebur128: add metadata injection.

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


---
 doc/filters.texi        | 17 +++++++++++++++++
 libavfilter/f_ebur128.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/doc/filters.texi b/doc/filters.texi
index bcbe21a..0d7856a 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -6277,6 +6277,23 @@ Set the EBU scale meter. Default is @code{9}. Common values are @code{9} and
 @code{18}, respectively for EBU scale meter +9 and EBU scale meter +18. Any
 other integer value between this range is allowed.
 
+ at item volnorm
+Set volume normalization metadata injection. Disabled by default. It accepts the following values:
+
+ at table @option
+ at item M
+Volume adjustment calculated according to the momentary loudness (400ms)
+ at item S
+Volume adjustment calculated according to the short-term loudness (3 seconds)
+ at item I
+Volume adjustment calculated according to the integrated loudness (from the beginning)
+ at end table
+
+If set to one of these values, the input audio will be segmented into 100ms
+output frames, each of them containing a volume adjustment metadata
+ at var{lavfi.r128.volume}. Note: all the frames might not contain that a
+metadata.
+
 @end table
 
 Example of real-time graph using @command{ffplay}, with a EBU scale meter +18:
diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c
index 66cc133..902f89a 100644
--- a/libavfilter/f_ebur128.c
+++ b/libavfilter/f_ebur128.c
@@ -33,6 +33,7 @@
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/channel_layout.h"
+#include "libavutil/dict.h"
 #include "libavutil/xga_font_data.h"
 #include "libavutil/opt.h"
 #include "libavutil/timestamp.h"
@@ -90,6 +91,7 @@ struct rect { int x, y, w, h; };
 
 typedef struct {
     const AVClass *class;           ///< AVClass context for log and options purpose
+    char *volnorm;                  ///< "M", "S" or "I" volume adjustment identifier
 
     /* video  */
     int do_video;                   ///< 1 if video output enabled, 0 otherwise
@@ -133,6 +135,7 @@ static const AVOption ebur128_options[] = {
     { "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, V|F },
     { "size",  "set video size",   OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, V|F },
     { "meter", "set scale meter (+9 to +18)",  OFFSET(meter), AV_OPT_TYPE_INT, {.i64 = 9}, 9, 18, V|F },
+    { "volnorm", "inject volume adjustment metadata", OFFSET(volnorm), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = A|F },
     { NULL },
 };
 
@@ -311,6 +314,20 @@ static int config_video_output(AVFilterLink *outlink)
     return 0;
 }
 
+static int config_audio_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    EBUR128Context *ebur128 = ctx->priv;
+
+    /* force 100ms framing in case of metadata injection: the frames must have
+     * a granularity of the window overlap to be accurately exploited */
+    if (ebur128->volnorm)
+        inlink->min_samples =
+        inlink->max_samples =
+        inlink->partial_buf_size = inlink->sample_rate / 10;
+    return 0;
+}
+
 static int config_audio_output(AVFilterLink *outlink)
 {
     int i;
@@ -387,6 +404,13 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
     if ((ret = av_set_options_string(ebur128, args, "=", ":")) < 0)
         return ret;
 
+    if (ebur128->volnorm && (!strchr("MSI", ebur128->volnorm[0]) ||
+                             ebur128->volnorm[1])) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Invalid volnorm option, choose between 'M', 'S' or 'I'\n");
+        return AVERROR(EINVAL);
+    }
+
     // if meter is  +9 scale, scale range is from -18 LU to  +9 LU (or 3*9)
     // if meter is +18 scale, scale range is from -36 LU to +18 LU (or 3*18)
     ebur128->scale_range = 3 * ebur128->meter;
@@ -644,10 +668,35 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *insamples)
                     return ret;
             }
 
+            if (ebur128->volnorm) { /* happens only once per filter_frame call */
+                double loudness_value = ABS_THRES;
+                switch (*ebur128->volnorm) {
+                case 'M':
+                    if (ebur128->i400.filled)
+                        loudness_value = loudness_400;
+                    break;
+                case 'S':
+                    if (ebur128->i3000.filled)
+                        loudness_value = loudness_3000;
+                    break;
+                case 'I':
+                    if (ebur128->i400.filled)
+                        loudness_value = ebur128->integrated_loudness;
+                    break;
+                }
+                if (loudness_value > ABS_THRES) {
+                    char metabuf[128];
+
+                    snprintf(metabuf, sizeof(metabuf), "%f",
+                             pow(10, (-23 - loudness_value) / 20));
+                    av_dict_set(&insamples->metadata, "lavfi.r128.volume", metabuf, 0);
+                }
+            } else {
             av_log(ctx, ebur128->do_video ? AV_LOG_VERBOSE : AV_LOG_INFO,
                    "t: %-10s " LOG_FMT "\n", av_ts2timestr(pts, &outlink->time_base),
                    loudness_400, loudness_3000,
                    ebur128->integrated_loudness, ebur128->loudness_range);
+            }
         }
     }
 
@@ -739,6 +788,7 @@ static av_cold void uninit(AVFilterContext *ctx)
     for (i = 0; i < ctx->nb_outputs; i++)
         av_freep(&ctx->output_pads[i].name);
     avfilter_unref_bufferp(&ebur128->outpicref);
+    av_opt_free(ebur128);
 }
 
 static const AVFilterPad ebur128_inputs[] = {
@@ -747,6 +797,7 @@ static const AVFilterPad ebur128_inputs[] = {
         .type             = AVMEDIA_TYPE_AUDIO,
         .get_audio_buffer = ff_null_get_audio_buffer,
         .filter_frame     = filter_frame,
+        .config_props     = config_audio_input,
     },
     { NULL }
 };
-- 
1.8.1.4



More information about the ffmpeg-devel mailing list