From ee4a04654014e24f47f5a31265fb3bde6fad8ce6 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Thu, 5 Nov 2020 13:33:32 +0100 Subject: avfilter/af_asoftclip: add oversampling support --- libavfilter/af_asoftclip.c | 106 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 7 deletions(-) (limited to 'libavfilter/af_asoftclip.c') diff --git a/libavfilter/af_asoftclip.c b/libavfilter/af_asoftclip.c index ce1f7ea96a..aaae3c6d4b 100644 --- a/libavfilter/af_asoftclip.c +++ b/libavfilter/af_asoftclip.c @@ -21,6 +21,7 @@ #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/opt.h" +#include "libswresample/swresample.h" #include "avfilter.h" #include "audio.h" #include "formats.h" @@ -42,14 +43,22 @@ typedef struct ASoftClipContext { const AVClass *class; int type; + int oversample; + int64_t delay; double param; + SwrContext *up_ctx; + SwrContext *down_ctx; + + AVFrame *frame; + void (*filter)(struct ASoftClipContext *s, void **dst, const void **src, int nb_samples, int channels, int start, int end); } ASoftClipContext; #define OFFSET(x) offsetof(ASoftClipContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM +#define F AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption asoftclip_options[] = { { "type", "set softclip type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, -1, NB_TYPES-1, A, "types" }, @@ -63,6 +72,7 @@ static const AVOption asoftclip_options[] = { { "sin", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_SIN}, 0, 0, A, "types" }, { "erf", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ERF}, 0, 0, A, "types" }, { "param", "set softclip parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 3, A }, + { "oversample", "set oversample factor", OFFSET(oversample), AV_OPT_TYPE_INT, {.i64=1}, 1, 32, F }, { NULL } }; @@ -242,6 +252,7 @@ static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; ASoftClipContext *s = ctx->priv; + int ret; switch (inlink->format) { case AV_SAMPLE_FMT_FLT: @@ -251,6 +262,38 @@ static int config_input(AVFilterLink *inlink) default: av_assert0(0); } + if (s->oversample <= 1) + return 0; + + s->up_ctx = swr_alloc(); + s->down_ctx = swr_alloc(); + if (!s->up_ctx || !s->down_ctx) + return AVERROR(ENOMEM); + + av_opt_set_int(s->up_ctx, "in_channel_layout", inlink->channel_layout, 0); + av_opt_set_int(s->up_ctx, "in_sample_rate", inlink->sample_rate, 0); + av_opt_set_sample_fmt(s->up_ctx, "in_sample_fmt", inlink->format, 0); + + av_opt_set_int(s->up_ctx, "out_channel_layout", inlink->channel_layout, 0); + av_opt_set_int(s->up_ctx, "out_sample_rate", inlink->sample_rate * s->oversample, 0); + av_opt_set_sample_fmt(s->up_ctx, "out_sample_fmt", inlink->format, 0); + + av_opt_set_int(s->down_ctx, "in_channel_layout", inlink->channel_layout, 0); + av_opt_set_int(s->down_ctx, "in_sample_rate", inlink->sample_rate * s->oversample, 0); + av_opt_set_sample_fmt(s->down_ctx, "in_sample_fmt", inlink->format, 0); + + av_opt_set_int(s->down_ctx, "out_channel_layout", inlink->channel_layout, 0); + av_opt_set_int(s->down_ctx, "out_sample_rate", inlink->sample_rate, 0); + av_opt_set_sample_fmt(s->down_ctx, "out_sample_fmt", inlink->format, 0); + + ret = swr_init(s->up_ctx); + if (ret < 0) + return ret; + + ret = swr_init(s->down_ctx); + if (ret < 0) + return ret; + return 0; } @@ -280,8 +323,9 @@ static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; + ASoftClipContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - int nb_samples, channels; + int ret, nb_samples, channels; ThreadData td; AVFrame *out; @@ -304,17 +348,64 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) channels = 1; } - td.in = in; - td.out = out; - td.nb_samples = nb_samples; - td.channels = channels; - ctx->internal->execute(ctx, filter_channels, &td, NULL, FFMIN(channels, - ff_filter_get_nb_threads(ctx))); + if (s->oversample > 1) { + s->frame = ff_get_audio_buffer(outlink, in->nb_samples * s->oversample); + if (!s->frame) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = swr_convert(s->up_ctx, (uint8_t**)s->frame->extended_data, in->nb_samples * s->oversample, + (const uint8_t **)in->extended_data, in->nb_samples); + if (ret < 0) + goto fail; + + td.in = s->frame; + td.out = s->frame; + td.nb_samples = av_sample_fmt_is_planar(in->format) ? ret : ret * in->channels; + td.channels = channels; + ctx->internal->execute(ctx, filter_channels, &td, NULL, FFMIN(channels, + ff_filter_get_nb_threads(ctx))); + + ret = swr_convert(s->down_ctx, (uint8_t**)out->extended_data, out->nb_samples, + (const uint8_t **)s->frame->extended_data, ret); + if (ret < 0) + goto fail; + + if (out->pts) + out->pts -= s->delay; + s->delay += in->nb_samples - ret; + out->nb_samples = ret; + + av_frame_free(&s->frame); + } else { + td.in = in; + td.out = out; + td.nb_samples = nb_samples; + td.channels = channels; + ctx->internal->execute(ctx, filter_channels, &td, NULL, FFMIN(channels, + ff_filter_get_nb_threads(ctx))); + } if (out != in) av_frame_free(&in); return ff_filter_frame(outlink, out); +fail: + if (out != in) + av_frame_free(&out); + av_frame_free(&in); + av_frame_free(&s->frame); + + return ret; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + ASoftClipContext *s = ctx->priv; + + swr_free(&s->up_ctx); + swr_free(&s->down_ctx); } static const AVFilterPad inputs[] = { @@ -343,6 +434,7 @@ AVFilter ff_af_asoftclip = { .priv_class = &asoftclip_class, .inputs = inputs, .outputs = outputs, + .uninit = uninit, .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, -- cgit v1.2.3