Skip to content

Commit

Permalink
lavfi/ebur128: add metadata injection.
Browse files Browse the repository at this point in the history
  • Loading branch information
ubitux committed Mar 14, 2013
1 parent 6b971e3 commit 615a503
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
18 changes: 18 additions & 0 deletions doc/filters.texi
Expand Up @@ -6531,6 +6531,24 @@ 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.

@item volnorm
Set volume normalization metadata injection. Disabled by default. It accepts the following values:

@table @option
@item M
Volume adjustment calculated according to the momentary loudness (400ms)
@item S
Volume adjustment calculated according to the short-term loudness (3 seconds)
@item I
Volume adjustment calculated according to the integrated loudness (from the beginning)
@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
@var{lavfi.volnorm}.

Note: all the frames might not contain that a metadata.

@end table

@subsection Examples
Expand Down
51 changes: 51 additions & 0 deletions libavfilter/f_ebur128.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 },
};

Expand Down Expand Up @@ -310,6 +313,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;
Expand Down Expand Up @@ -386,6 +403,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;
Expand Down Expand Up @@ -643,10 +667,35 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *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.volnorm", 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);
}
}
}

Expand Down Expand Up @@ -727,6 +776,7 @@ static av_cold void uninit(AVFilterContext *ctx)
for (i = 0; i < ctx->nb_outputs; i++)
av_freep(&ctx->output_pads[i].name);
av_frame_free(&ebur128->outpicref);
av_opt_free(ebur128);
}

static const AVFilterPad ebur128_inputs[] = {
Expand All @@ -735,6 +785,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 }
};
Expand Down

0 comments on commit 615a503

Please sign in to comment.