[FFmpeg-devel] [PATCH] avfilter/f_ebur128: add dualmono measurement option

Kyle Swanson k at ylo.ph
Fri Sep 25 23:26:00 CEST 2015


On Fri, Sep 25, 2015 at 4:04 PM, Kyle Swanson <k at ylo.ph> wrote:
> Signed-off-by: Kyle Swanson <k at ylo.ph>
> ---
>  doc/filters.texi        |  9 +++++++++
>  libavfilter/f_ebur128.c | 22 ++++++++++++++++++++++
>  2 files changed, 31 insertions(+)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 044876c..e63311a 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -12642,6 +12642,15 @@ stream for better peak accuracy. It logs a message for true-peak.
>  This mode requires a build with @code{libswresample}.
>  @end table
>
> + at item dualmono
> +Treat mono input files as 'dual mono.' If a mono file is intended for playback
> +on a stereo system, its EBU R128 measurement will be perceptually incorrect.
> +If set to @code{true}, this option will compensate for this effect.
> +Multi-channel input files are not effected by this option.
> +
> + at item panlaw
> +Set a specific pan law to be used for the measurement of dual mono files.
> +This parameter is optional, and has a default value of -3.01dB.
>  @end table
>
>  @subsection Examples
> diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c
> index 1d5c8fd..906c9d7 100644
> --- a/libavfilter/f_ebur128.c
> +++ b/libavfilter/f_ebur128.c
> @@ -139,6 +139,8 @@ typedef struct {
>      /* misc */
>      int loglevel;                   ///< log level for frame logging
>      int metadata;                   ///< whether or not to inject loudness results in frames
> +    int dual_mono;                  ///< whether or not to treat single channel input files as dual-mono
> +    double pan_law;                 ///< pan law value used to calulate dual-mono measurements
>  } EBUR128Context;
>
>  enum {
> @@ -163,6 +165,8 @@ static const AVOption ebur128_options[] = {
>          { "none",   "disable any peak mode",   0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_NONE},          INT_MIN, INT_MAX, A|F, "mode" },
>          { "sample", "enable peak-sample mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_SAMPLES_PEAKS}, INT_MIN, INT_MAX, A|F, "mode" },
>          { "true",   "enable true-peak mode",   0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_TRUE_PEAKS},    INT_MIN, INT_MAX, A|F, "mode" },
> +    { "dualmono", "treat mono input files as dual-mono", OFFSET(dual_mono), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A|V|F },
> +    { "panlaw", "set a specific pan law for dual-mono files", OFFSET(pan_law), AV_OPT_TYPE_DOUBLE, {.dbl = -3.01029995663978}, -10.0, 0.0, A|V|F },
>      { NULL },
>  };
>
> @@ -661,6 +665,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
>                  }
>                  if (nb_integrated)
>                      ebur128->integrated_loudness = LOUDNESS(integrated_sum / nb_integrated);
> +                    /* dual-mono correction */
> +                    if (nb_channels == 1 && ebur128->dual_mono) {
> +                        ebur128->integrated_loudness -= ebur128->pan_law;
> +                    }
>              }
>
>              /* LRA */
> @@ -707,6 +715,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
>                  }
>              }
>
> +            /* dual-mono correction */
> +            if (nb_channels == 1 && ebur128->dual_mono) {
> +                loudness_400 -= ebur128->pan_law;
> +                loudness_3000 -= ebur128->pan_law;
> +            }
> +
>  #define LOG_FMT "M:%6.1f S:%6.1f     I:%6.1f LUFS     LRA:%6.1f LU"
>
>              /* push one video frame */
> @@ -855,6 +869,14 @@ static av_cold void uninit(AVFilterContext *ctx)
>      int i;
>      EBUR128Context *ebur128 = ctx->priv;
>
> +    /* dual-mono correction */
> +    if (ebur128->nb_channels == 1 && ebur128->dual_mono) {
> +        ebur128->i400.rel_threshold -= ebur128->pan_law;
> +        ebur128->i3000.rel_threshold -= ebur128->pan_law;
> +        ebur128->lra_low -= ebur128->pan_law;
> +        ebur128->lra_high -= ebur128->pan_law;
> +    }
> +
>      av_log(ctx, AV_LOG_INFO, "Summary:\n\n"
>             "  Integrated loudness:\n"
>             "    I:         %5.1f LUFS\n"
> --
> 1.8.4
>

This is a feature supported by many other loudness scanners. In the
United States, this filter is used to measure loudness for much of our
distributed public radio content. Our current workaround is to first
determine channel count, and then alter our calls to FFmpeg
accordingly. This is a simple, but quite useful feature to have.
Compare the output of the following commands. The first is our
workaround, and the second is an equivalent command which also
provides an accurate measurement. The advantage to having this built
in is that the same call can be used for multi-channel inputs as well.

ffmpeg -nostats -i mono.wav -filter_complex
"[0:0][0:0]amerge=inputs=2[aout];[aout]ebur128=framelog=verbose:peak=sample+true"
-f null -
ffmpeg -nostats -i mono.wav -filter_complex ebur128=dualmono=true
ebur128 -f null -

See also: https://ffmpeg.org/pipermail/ffmpeg-devel/2013-March/139910.html
Also see this blog post:
http://www.producenewmedia.com/podcast-loudness-mono-vs-stereo-perception/


More information about the ffmpeg-devel mailing list