Merge commit '06c3cd3c0186803619bc6aad2d8f06c3e9015d15'
authorMichael Niedermayer <michaelni@gmx.at>
Mon, 24 Mar 2014 14:04:47 +0000 (15:04 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Mon, 24 Mar 2014 14:04:47 +0000 (15:04 +0100)
* commit '06c3cd3c0186803619bc6aad2d8f06c3e9015d15':
  af_volume: support using replaygain frame side data

Conflicts:
doc/filters.texi
libavfilter/af_volume.c
libavfilter/af_volume.h

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
doc/filters.texi
libavfilter/af_volume.c
libavfilter/af_volume.h

@@@ -1803,109 -760,75 +1803,126 @@@ The filter accepts the following option
  
  @table @option
  
 -@item amount
 -The percentage of the pixels that have to be below the threshold, defaults to
 -98.
 +@item volume
 +Set audio volume expression.
  
 -@item threshold
 -Threshold below which a pixel value is considered black, defaults to 32.
 +Output values are clipped to the maximum value.
  
 -@end table
 +The output audio volume is given by the relation:
 +@example
 +@var{output_volume} = @var{volume} * @var{input_volume}
 +@end example
  
 -@section boxblur
 +Default value for @var{volume} is "1.0".
  
 -Apply boxblur algorithm to the input video.
 +@item precision
 +Set the mathematical precision.
  
 -This filter accepts the following options:
 +This determines which input sample formats will be allowed, which affects the
 +precision of the volume scaling.
 +
 +@table @option
 +@item fixed
 +8-bit fixed-point; limits input sample format to U8, S16, and S32.
 +@item float
 +32-bit floating-point; limits input sample format to FLT. (default)
 +@item double
 +64-bit floating-point; limits input sample format to DBL.
 +@end table
 +
++@item replaygain
++Behaviour on encountering ReplayGain side data in input frames.
+ @table @option
++@item drop
++Remove ReplayGain side data, ignoring its contents (the default).
++
++@item ignore
++Ignore ReplayGain side data, but leave it in the frame.
 -@item luma_radius
 -@item luma_power
 -@item chroma_radius
 -@item chroma_power
 -@item alpha_radius
 -@item alpha_power
++@item track
++Prefer track gain, if present.
++@item album
++Prefer album gain, if present.
+ @end table
 -Chroma and alpha parameters are optional, if not specified they default
 -to the corresponding values set for @var{luma_radius} and
 -@var{luma_power}.
 +@item eval
 +Set when the volume expression is evaluated.
  
 -@var{luma_radius}, @var{chroma_radius}, and @var{alpha_radius} represent
 -the radius in pixels of the box used for blurring the corresponding
 -input plane. They are expressions, and can contain the following
 -constants:
 -@table @option
 -@item w, h
 -the input width and height in pixels
 +It accepts the following values:
 +@table @samp
 +@item once
 +only evaluate expression once during the filter initialization, or
 +when the @samp{volume} command is sent
  
 -@item cw, ch
 -the input chroma image width and height in pixels
 +@item frame
 +evaluate expression for each incoming frame
 +@end table
  
 -@item hsub, vsub
 -horizontal and vertical chroma subsample values. For example for the
 -pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
 +Default value is @samp{once}.
  @end table
  
 -The radius must be a non-negative number, and must not be greater than
 -the value of the expression @code{min(w,h)/2} for the luma and alpha planes,
 -and of @code{min(cw,ch)/2} for the chroma planes.
 +The volume expression can contain the following parameters.
  
 -@var{luma_power}, @var{chroma_power}, and @var{alpha_power} represent
 -how many times the boxblur filter is applied to the corresponding
 -plane.
 +@table @option
 +@item n
 +frame number (starting at zero)
 +@item nb_channels
 +number of channels
 +@item nb_consumed_samples
 +number of samples consumed by the filter
 +@item nb_samples
 +number of samples in the current frame
 +@item pos
 +original frame position in the file
 +@item pts
 +frame PTS
 +@item sample_rate
 +sample rate
 +@item startpts
 +PTS at start of stream
 +@item startt
 +time at start of stream
 +@item t
 +frame time
 +@item tb
 +timestamp timebase
 +@item volume
 +last set volume value
 +@end table
  
 -Some examples follow:
 +Note that when @option{eval} is set to @samp{once} only the
 +@var{sample_rate} and @var{tb} variables are available, all other
 +variables will evaluate to NAN.
  
 -@itemize
 +@subsection Commands
 +
 +This filter supports the following commands:
 +@table @option
 +@item volume
 +Modify the volume expression.
 +The command accepts the same syntax of the corresponding option.
 +
 +If the specified expression is not valid, it is kept at its current
 +value.
 +@end table
 +
 +@subsection Examples
  
 +@itemize
  @item
 -Apply a boxblur filter with luma, chroma, and alpha radius
 -set to 2:
 +Halve the input audio volume:
  @example
 -boxblur=luma_radius=2:luma_power=1
 +volume=volume=0.5
 +volume=volume=1/2
 +volume=volume=-6.0206dB
  @end example
  
 -@item
 -Set luma radius to 2, alpha and chroma radius to 0
 +In all the above example the named key for @option{volume} can be
 +omitted, for example like in:
  @example
 -boxblur=2:1:0:0:0:0
 +volume=0.5
  @end example
  
  @item
@@@ -39,59 -42,48 +42,65 @@@ static const char *precision_str[] = 
      "fixed", "float", "double"
  };
  
 +static const char *const var_names[] = {
 +    "n",                   ///< frame number (starting at zero)
 +    "nb_channels",         ///< number of channels
 +    "nb_consumed_samples", ///< number of samples consumed by the filter
 +    "nb_samples",          ///< number of samples in the current frame
 +    "pos",                 ///< position in the file of the frame
 +    "pts",                 ///< frame presentation timestamp
 +    "sample_rate",         ///< sample rate
 +    "startpts",            ///< PTS at start of stream
 +    "startt",              ///< time at start of stream
 +    "t",                   ///< time in the file of the frame
 +    "tb",                  ///< timebase
 +    "volume",              ///< last set value
 +    NULL
 +};
 +
  #define OFFSET(x) offsetof(VolumeContext, x)
  #define A AV_OPT_FLAG_AUDIO_PARAM
 -
 -static const AVOption options[] = {
 -    { "volume", "Volume adjustment.",
 -            OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0, 0x7fffff, A },
 -    { "precision", "Mathematical precision.",
 -            OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A, "precision" },
 -        { "fixed",  "8-bit fixed-point.",     0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED  }, INT_MIN, INT_MAX, A, "precision" },
 -        { "float",  "32-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT  }, INT_MIN, INT_MAX, A, "precision" },
 -        { "double", "64-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A, "precision" },
 +#define F AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption volume_options[] = {
 +    { "volume", "set volume adjustment expression",
 +            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" },
 +        { "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" },
 +    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_ONCE}, 0, EVAL_MODE_NB-1, .flags = A|F, "eval" },
 +         { "once",  "eval volume expression once", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_ONCE},  .flags = A|F, .unit = "eval" },
 +         { "frame", "eval volume expression per-frame",                  0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = A|F, .unit = "eval" },
-     { NULL }
+     { "replaygain", "Apply replaygain side data when present",
+             OFFSET(replaygain), AV_OPT_TYPE_INT, { .i64 = REPLAYGAIN_DROP }, REPLAYGAIN_DROP, REPLAYGAIN_ALBUM, A, "replaygain" },
+         { "drop",   "replaygain side data is dropped", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_DROP   }, 0, 0, A, "replaygain" },
+         { "ignore", "replaygain side data is ignored", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_IGNORE }, 0, 0, A, "replaygain" },
+         { "track",  "track gain is preferred",         0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_TRACK  }, 0, 0, A, "replaygain" },
+         { "album",  "album gain is preferred",         0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_ALBUM  }, 0, 0, A, "replaygain" },
+     { NULL },
  };
  
 -static const AVClass volume_class = {
 -    .class_name = "volume filter",
 -    .item_name  = av_default_item_name,
 -    .option     = options,
 -    .version    = LIBAVUTIL_VERSION_INT,
 -};
 +AVFILTER_DEFINE_CLASS(volume);
  
 -static av_cold int init(AVFilterContext *ctx)
 +static int set_expr(AVExpr **pexpr, const char *expr, void *log_ctx)
  {
 -    VolumeContext *vol = ctx->priv;
 -
 -    if (vol->precision == PRECISION_FIXED) {
 -        vol->volume_i = (int)(vol->volume * 256 + 0.5);
 -        vol->volume   = vol->volume_i / 256.0;
 -        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 {
 -        av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
 -               vol->volume, 20.0*log(vol->volume)/M_LN10,
 -               precision_str[vol->precision]);
 +    int ret;
 +    AVExpr *old = NULL;
 +
 +    if (*pexpr)
 +        old = *pexpr;
 +    ret = av_expr_parse(pexpr, expr, var_names,
 +                        NULL, NULL, NULL, NULL, 0, log_ctx);
 +    if (ret < 0) {
 +        av_log(log_ctx, AV_LOG_ERROR,
 +               "Error when evaluating the volume expression '%s'\n", expr);
 +        *pexpr = old;
 +        return ret;
      }
  
 +    av_expr_free(old);
      return 0;
  }
  
@@@ -324,26 -238,40 +333,56 @@@ static int filter_frame(AVFilterLink *i
      AVFilterLink *outlink = inlink->dst->outputs[0];
      int nb_samples        = buf->nb_samples;
      AVFrame *out_buf;
 +    int64_t pos;
+     AVFrameSideData *sd = av_frame_get_side_data(buf, AV_FRAME_DATA_REPLAYGAIN);
      int ret;
  
 -    if (vol->volume == 1.0 || vol->volume_i == 256)
 -        return ff_filter_frame(outlink, buf);
+     if (sd && vol->replaygain != REPLAYGAIN_IGNORE) {
+         if (vol->replaygain != REPLAYGAIN_DROP) {
+             AVReplayGain *replaygain = (AVReplayGain*)sd->data;
+             int32_t gain;
+             float g;
+             if (vol->replaygain == REPLAYGAIN_TRACK &&
+                 replaygain->track_gain != INT32_MIN)
+                 gain = replaygain->track_gain;
+             else if (replaygain->album_gain != INT32_MIN)
+                 gain = replaygain->album_gain;
+             else {
+                 av_log(inlink->dst, AV_LOG_WARNING, "Both ReplayGain gain "
+                        "values are unknown.\n");
+                 gain = 100000;
+             }
+             g = gain / 100000.0f;
+             av_log(inlink->dst, AV_LOG_VERBOSE,
+                    "Using gain %f dB from replaygain side data.\n", g);
+             vol->volume   = pow(10, g / 20);
+             vol->volume_i = (int)(vol->volume * 256 + 0.5);
+             volume_init(vol);
+         }
+         av_frame_remove_side_data(buf, AV_FRAME_DATA_REPLAYGAIN);
+     }
 +    if (isnan(vol->var_values[VAR_STARTPTS])) {
 +        vol->var_values[VAR_STARTPTS] = TS2D(buf->pts);
 +        vol->var_values[VAR_STARTT  ] = TS2T(buf->pts, inlink->time_base);
 +    }
 +    vol->var_values[VAR_PTS] = TS2D(buf->pts);
 +    vol->var_values[VAR_T  ] = TS2T(buf->pts, inlink->time_base);
 +    vol->var_values[VAR_N  ] = inlink->frame_count;
 +
 +    pos = av_frame_get_pkt_pos(buf);
 +    vol->var_values[VAR_POS] = pos == -1 ? NAN : pos;
 +    if (vol->eval_mode == EVAL_MODE_FRAME)
 +        set_volume(ctx);
 +
 +    if (vol->volume == 1.0 || vol->volume_i == 256) {
 +        out_buf = buf;
 +        goto end;
 +    }
  
      /* do volume scaling in-place if input buffer is writable */
      if (av_frame_is_writable(buf)) {
@@@ -36,37 -35,18 +36,45 @@@ enum PrecisionType 
      PRECISION_DOUBLE,
  };
  
 +enum EvalMode {
 +    EVAL_MODE_ONCE,
 +    EVAL_MODE_FRAME,
 +    EVAL_MODE_NB
 +};
 +
 +enum VolumeVarName {
 +    VAR_N,
 +    VAR_NB_CHANNELS,
 +    VAR_NB_CONSUMED_SAMPLES,
 +    VAR_NB_SAMPLES,
 +    VAR_POS,
 +    VAR_PTS,
 +    VAR_SAMPLE_RATE,
 +    VAR_STARTPTS,
 +    VAR_STARTT,
 +    VAR_T,
 +    VAR_TB,
 +    VAR_VOLUME,
 +    VAR_VARS_NB
 +};
 +
+ enum ReplayGainType {
+     REPLAYGAIN_DROP,
+     REPLAYGAIN_IGNORE,
+     REPLAYGAIN_TRACK,
+     REPLAYGAIN_ALBUM,
+ };
  typedef struct VolumeContext {
      const AVClass *class;
      AVFloatDSPContext fdsp;
      enum PrecisionType precision;
 +    enum EvalMode eval_mode;
 +    const char *volume_expr;
 +    AVExpr *volume_pexpr;
 +    double var_values[VAR_VARS_NB];
 +
+     enum ReplayGainType replaygain;
      double volume;
      int    volume_i;
      int    channels;