avfilter/af_silenceremove: prefer outlink instead of inlink
[ffmpeg.git] / libavfilter / af_volume.c
index 8b5d4c4..b106ed8 100644 (file)
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
 #include "libavutil/eval.h"
+#include "libavutil/ffmath.h"
 #include "libavutil/float_dsp.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/opt.h"
+#include "libavutil/replaygain.h"
+
 #include "audio.h"
 #include "avfilter.h"
 #include "formats.h"
 #include "internal.h"
 #include "af_volume.h"
 
-static const char *precision_str[] = {
+static const char * const precision_str[] = {
     "fixed", "float", "double"
 };
 
@@ -70,6 +74,16 @@ static const AVOption volume_options[] = {
     { "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" },
+    { "replaygain", "Apply replaygain side data when present",
+            OFFSET(replaygain), AV_OPT_TYPE_INT, { .i64 = REPLAYGAIN_DROP }, REPLAYGAIN_DROP, REPLAYGAIN_ALBUM, A|F, "replaygain" },
+        { "drop",   "replaygain side data is dropped", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_DROP   }, 0, 0, A|F, "replaygain" },
+        { "ignore", "replaygain side data is ignored", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_IGNORE }, 0, 0, A|F, "replaygain" },
+        { "track",  "track gain is preferred",         0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_TRACK  }, 0, 0, A|F, "replaygain" },
+        { "album",  "album gain is preferred",         0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_ALBUM  }, 0, 0, A|F, "replaygain" },
+    { "replaygain_preamp", "Apply replaygain pre-amplification",
+            OFFSET(replaygain_preamp), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, -15.0, 15.0, A|F },
+    { "replaygain_noclip", "Apply replaygain clipping prevention",
+            OFFSET(replaygain_noclip), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, A|F },
     { NULL }
 };
 
@@ -98,6 +112,11 @@ static int set_expr(AVExpr **pexpr, const char *expr, void *log_ctx)
 static av_cold int init(AVFilterContext *ctx)
 {
     VolumeContext *vol = ctx->priv;
+
+    vol->fdsp = avpriv_float_dsp_alloc(0);
+    if (!vol->fdsp)
+        return AVERROR(ENOMEM);
+
     return set_expr(&vol->volume_pexpr, vol->volume_expr, ctx);
 }
 
@@ -106,6 +125,7 @@ static av_cold void uninit(AVFilterContext *ctx)
     VolumeContext *vol = ctx->priv;
     av_expr_free(vol->volume_pexpr);
     av_opt_free(vol);
+    av_freep(&vol->fdsp);
 }
 
 static int query_formats(AVFilterContext *ctx)
@@ -134,23 +154,26 @@ static int query_formats(AVFilterContext *ctx)
             AV_SAMPLE_FMT_NONE
         }
     };
+    int ret;
 
     layouts = ff_all_channel_counts();
     if (!layouts)
         return AVERROR(ENOMEM);
-    ff_set_common_channel_layouts(ctx, layouts);
+    ret = ff_set_common_channel_layouts(ctx, layouts);
+    if (ret < 0)
+        return ret;
 
     formats = ff_make_format_list(sample_fmts[vol->precision]);
     if (!formats)
         return AVERROR(ENOMEM);
-    ff_set_common_formats(ctx, formats);
+    ret = ff_set_common_formats(ctx, formats);
+    if (ret < 0)
+        return ret;
 
     formats = ff_all_samplerates();
     if (!formats)
         return AVERROR(ENOMEM);
-    ff_set_common_samplerates(ctx, formats);
-
-    return 0;
+    return ff_set_common_samplerates(ctx, formats);
 }
 
 static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
@@ -220,11 +243,9 @@ static av_cold void volume_init(VolumeContext *vol)
         vol->scale_samples = scale_samples_s32;
         break;
     case AV_SAMPLE_FMT_FLT:
-        avpriv_float_dsp_init(&vol->fdsp, 0);
         vol->samples_align = 4;
         break;
     case AV_SAMPLE_FMT_DBL:
-        avpriv_float_dsp_init(&vol->fdsp, 0);
         vol->samples_align = 8;
         break;
     }
@@ -259,7 +280,7 @@ static int set_volume(AVFilterContext *ctx)
         av_log(ctx, AV_LOG_VERBOSE, "volume_i:%d/255 ", vol->volume_i);
     }
     av_log(ctx, AV_LOG_VERBOSE, "volume:%f volume_dB:%f\n",
-           vol->volume, 20.0*log(vol->volume)/M_LN10);
+           vol->volume, 20.0*log10(vol->volume));
 
     volume_init(vol);
     return 0;
@@ -325,17 +346,56 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     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 (sd && vol->replaygain != REPLAYGAIN_IGNORE) {
+        if (vol->replaygain != REPLAYGAIN_DROP) {
+            AVReplayGain *replaygain = (AVReplayGain*)sd->data;
+            int32_t gain  = 100000;
+            uint32_t peak = 100000;
+            float g, p;
+
+            if (vol->replaygain == REPLAYGAIN_TRACK &&
+                replaygain->track_gain != INT32_MIN) {
+                gain = replaygain->track_gain;
+
+                if (replaygain->track_peak != 0)
+                    peak = replaygain->track_peak;
+            } else if (replaygain->album_gain != INT32_MIN) {
+                gain = replaygain->album_gain;
+
+                if (replaygain->album_peak != 0)
+                    peak = replaygain->album_peak;
+            } else {
+                av_log(inlink->dst, AV_LOG_WARNING, "Both ReplayGain gain "
+                       "values are unknown.\n");
+            }
+            g = gain / 100000.0f;
+            p = peak / 100000.0f;
+
+            av_log(inlink->dst, AV_LOG_VERBOSE,
+                   "Using gain %f dB from replaygain side data.\n", g);
+
+            vol->volume   = ff_exp10((g + vol->replaygain_preamp) / 20);
+            if (vol->replaygain_noclip)
+                vol->volume = FFMIN(vol->volume, 1.0 / p);
+            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;
+    vol->var_values[VAR_N  ] = inlink->frame_count_out;
 
-    pos = av_frame_get_pkt_pos(buf);
+    pos = buf->pkt_pos;
     vol->var_values[VAR_POS] = pos == -1 ? NAN : pos;
     if (vol->eval_mode == EVAL_MODE_FRAME)
         set_volume(ctx);
@@ -346,12 +406,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     }
 
     /* do volume scaling in-place if input buffer is writable */
-    if (av_frame_is_writable(buf)) {
+    if (av_frame_is_writable(buf)
+            && (vol->precision != PRECISION_FIXED || vol->volume_i > 0)) {
         out_buf = buf;
     } else {
-        out_buf = ff_get_audio_buffer(inlink, nb_samples);
-        if (!out_buf)
+        out_buf = ff_get_audio_buffer(outlink, nb_samples);
+        if (!out_buf) {
+            av_frame_free(&buf);
             return AVERROR(ENOMEM);
+        }
         ret = av_frame_copy_props(out_buf, buf);
         if (ret < 0) {
             av_frame_free(&out_buf);
@@ -376,19 +439,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
             }
         } else if (av_get_packed_sample_fmt(vol->sample_fmt) == AV_SAMPLE_FMT_FLT) {
             for (p = 0; p < vol->planes; p++) {
-                vol->fdsp.vector_fmul_scalar((float *)out_buf->extended_data[p],
+                vol->fdsp->vector_fmul_scalar((float *)out_buf->extended_data[p],
                                              (const float *)buf->extended_data[p],
                                              vol->volume, plane_samples);
             }
         } else {
             for (p = 0; p < vol->planes; p++) {
-                vol->fdsp.vector_dmul_scalar((double *)out_buf->extended_data[p],
+                vol->fdsp->vector_dmul_scalar((double *)out_buf->extended_data[p],
                                              (const double *)buf->extended_data[p],
                                              vol->volume, plane_samples);
             }
         }
     }
 
+    emms_c();
+
     if (buf != out_buf)
         av_frame_free(&buf);