Permalink
Browse files

lavfi/volume: support volume normalization.

  • Loading branch information...
1 parent 615a503 commit 95e889bcf8f615170bb4e411794ae0837e59c312 @ubitux committed Feb 21, 2013
Showing with 58 additions and 8 deletions.
  1. +26 −0 doc/filters.texi
  2. +28 −7 libavfilter/af_volume.c
  3. +3 −0 libavfilter/af_volume.h
  4. +1 −1 libavfilter/x86/af_volume_init.c
View
@@ -1249,6 +1249,7 @@ out
Convert the audio sample format, sample rate and channel layout. This filter is
not meant to be used directly.
+@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
+@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
+
+@item
+Normalize in real-time an audio stream with @command{ffmpeg} and the help of
+the @ref{ebur128} filter:
+@example
+ffmpeg -i input.mp3 -af ebur128=volnorm=I,volume=volnorm output.wav
+@end example
+
+@item
+Normalize the audio using @ref{ebur128} and observe its effect using
+@command{ffplay}:
+@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'
+@end example
@end itemize
@section volumedetect
@@ -6496,6 +6519,7 @@ setpts='(RTCTIME - RTCSTART) / (TB * 1000000)'
@end example
@end itemize
+@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
View
@@ -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);
View
@@ -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;
@@ -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;
}

0 comments on commit 95e889b

Please sign in to comment.