X-Git-Url: http://git.videolan.org/?p=ffmpeg.git;a=blobdiff_plain;f=libavfilter%2Faf_volume.c;h=b106ed8cf4aa82999d1c3eeaf12aeda2b9550dae;hp=269a2a527af5760aa5ac037732bfb89e1d36848e;hb=ea3a980a610e2517fcb2abe2708166a4b9f6e57d;hpb=baeda2bf92a5de97e497f13ecb04025e27f9be1a diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c index 269a2a527a..b106ed8cf4 100644 --- a/libavfilter/af_volume.c +++ b/libavfilter/af_volume.c @@ -27,15 +27,19 @@ #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,6 +346,46 @@ 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); @@ -332,9 +393,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) } 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); @@ -345,13 +406,21 @@ 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); - av_frame_copy_props(out_buf, buf); + } + ret = av_frame_copy_props(out_buf, buf); + if (ret < 0) { + av_frame_free(&out_buf); + av_frame_free(&buf); + return ret; + } } if (vol->precision != PRECISION_FIXED || vol->volume_i > 0) { @@ -370,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);