From patchwork Sun Nov 28 13:21:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 31786 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:d206:0:0:0:0:0 with SMTP id q6csp4820923iob; Sun, 28 Nov 2021 05:21:18 -0800 (PST) X-Google-Smtp-Source: ABdhPJyyZrmpFYW5+wEwOOlunKmu2WGPd032u0hkLv82kaFDvopGqNdgF3gy1mbvgYRZWgoJlQSm X-Received: by 2002:a17:906:5811:: with SMTP id m17mr52654795ejq.289.1638105678657; Sun, 28 Nov 2021 05:21:18 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1638105678; cv=none; d=google.com; s=arc-20160816; b=Q3q/g46UXhpL8S5Aq9uRVew/BLypvZ6YtpGJdtxIkzmgg0+oRo9rHFBRVLFEjgXng4 lz8yp9KKeUIqCr/KZnfSe1xk9hxwFa0M32+aDtdqzZWnssj8UP9V/Q3l9T6ddQMEPM4K bKV/OnkOcXbA7Fpv1FvZcFtsDJMu5YGsimEoNhrGsOyGOahZBH8Q6DR8bW4sTLvoAHZr 4h16acc6fQI6hjUMyrbytbnvwAboDQZ8EWpgnIZHB5sREKq85wFcjO/wD5nzPHbWQAgC Xb2ng2yEoxny3GMAaBIl6/MhAx0NhvBcH9mrqDyzYWfLttp0EemdN3SnAzOejsVNTTlc lRCQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=MZ55V09p+3V5jmKObjPYYoah9LU3eWv5aDnjEGeDQR0=; b=LGHEm2cCfovEQ7QPsg06QZa3R/jzJQEayzcGD0xKrcAN82BbCtQjVRRJbO1JIRxJ1C ryHx+6zBgWAhcQDss4sRDKD9iFv7ghR4q138m7BGLIvC6YrOHKusS60Iyic0bScja1gA dALSBNygSfUbkyGsfnPrd2U7F0Keo7gorRHS+lpOj42Y7B6jejWfzwe3K1Lc9RR7bz4E bPhYxFlIAXTokvFzLkzQjDbaBk5rhGAqszRw7rKBgOqeuj7HJ7NpFSzGS2CRIlYmBfTH FXDVetVSgD4IzBkE1F11Yqw1GGb/Au43Y9E0ZWuOhpYBIH/dQLwve2kOei0ComGRyrsT uAIw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=NzajR0DX; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id kg26si16341393ejc.463.2021.11.28.05.21.17; Sun, 28 Nov 2021 05:21:18 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=NzajR0DX; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 586E268A2F4; Sun, 28 Nov 2021 15:21:13 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DAA12680610 for ; Sun, 28 Nov 2021 15:21:06 +0200 (EET) Received: by mail-wr1-f42.google.com with SMTP id i5so30457001wrb.2 for ; Sun, 28 Nov 2021 05:21:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=PBFkjEbWlXHGYjB3gWGsgTpNmVxwew2tVbct/EXV2w0=; b=NzajR0DXE6cRGpXLt+FvrQoQ0eZuGngquVH9aVpQpwWIh7kbG+L9i+U3b63UkLx9FP OkVL2f2fWay00hrKry+b4h7TeT8zndPWHYn49lZkRCTM+7MK6OnlBFXHHjVa8n2Tpcwz QQqzGZxai4vKQ/Aykc2hKbvZ4dhvnlCyYeejFw3uhUSDNit4VPMRpOAfo36If+USn//i 8ZNst6v+uExmpfT0R+aMfofcjLJvW39cTKYlPh7zG0utebtjLZfm2YlbkmmSTXNF+KQZ WaQii5avubjYx1aNRqsNMBkEP9nPW2CLvkFIhcWXZ0mhloh4ss34ScGeH0u9EzTgXSNn hA7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=PBFkjEbWlXHGYjB3gWGsgTpNmVxwew2tVbct/EXV2w0=; b=6YVRcqcSVhSK6mwsJGxDSEQUYLUVsDc0AKC6l4XbjtQVCGQlGOFfTp+PcSLEYcDNX1 CzOOTYHD32qsoYeNPlTVLx5YwXs5stoItj15f/Ch1lOxHwVsRo0+ZPeW3TX9qkmiX2Qm oTk8O3v7k4rYz+jAiYKopLFzymZcWji6kgn7SJZKUVTm0MzOrh9Ve2Lhh7J6t9BHA2KQ ZIr+jJzDOUISSb8CmdkxuItLynDwMYiGIn/3DLL/5LbjzA9AOQ0deaU2zs2WbgyOQrlQ FAZSc3eZfbJR+kxiwPsvmd9zCYOpakEirXYkskhAtBugQ6S2uDGpY4cOsJ33w6peFMpa yWtA== X-Gm-Message-State: AOAM5306UP1xjdzZNzhUXa3t1KjoNduS2COLPNBjqSGAQCnJMRuBBUGS haLGq1/JmBel7bACDbfok6Qi21n3EhE= X-Received: by 2002:a05:6000:1541:: with SMTP id 1mr26728314wry.347.1638105666089; Sun, 28 Nov 2021 05:21:06 -0800 (PST) Received: from localhost.localdomain ([95.168.121.19]) by smtp.gmail.com with ESMTPSA id h204sm12621636wmh.33.2021.11.28.05.21.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 28 Nov 2021 05:21:05 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sun, 28 Nov 2021 14:21:08 +0100 Message-Id: <20211128132108.674370-1-onemda@gmail.com> X-Mailer: git-send-email 2.33.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] avfilter: add audio dynamic equalizer filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: sweUBL1pJ3mh Signed-off-by: Paul B Mahol --- doc/filters.texi | 75 +++++++ libavfilter/Makefile | 1 + libavfilter/af_adynamicequalizer.c | 310 +++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 4 files changed, 387 insertions(+) create mode 100644 libavfilter/af_adynamicequalizer.c diff --git a/doc/filters.texi b/doc/filters.texi index 7852948d2f..cfddefff30 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -843,6 +843,81 @@ Compute derivative/integral of audio stream. Applying both filters one after another produces original audio. +@section adynamicequalizer + +Apply dynamic equalization to input audio stream. + +A description of the accepted options follows. + +@table @option +@item threshold +Set the detection threshold used to trigger equalization. +Threshold detection is using bandpass filter. +Default value is 0. Allowed range is from 0 to 50. + +@item dfrequency +Set the detection frequency in Hz used for bandpass filter used to trigger equalization. +Default value is 1000 Hz. Allowed range is between 2 and 1000000 Hz. + +@item dqfactor +Set the detection resonance factor for bandpass filter used to trigger equalization. +Default value is 1. Allowed range is from 0.001 to 1000. + +@item sfrequency +Set the frequency of lowpass filter to smooth equalization filter gains. +Default value is 100 Hz. Allowed range is between 2 and 1000000 Hz. + +@item sqfactor +Set the resonance factor of lowpass filter to smooth equalization filter gains. +Default value is 1. Allowed range is from 0.001 to 1000. + +@item tfrequency +Set the target frequency of equalization filter. +Default value is 1000 Hz. Allowed range is between 2 and 1000000 Hz. + +@item tqfactor +Set the target resonance factor for target equalization filter. +Default value is 1. Allowed range is from 0.001 to 1000. + +@item attack +Set the amount of milliseconds the signal from detection has to rise above +the detection threshold before equalization starts. +Default is 10. Allowed range is between 1 and 2000. + +@item release +Set the amount of milliseconds the signal from detection has to fall below the +detection threshold before equalization ends. +Default is 80. Allowed range is between 1 and 2000. + +@item knee +Curve the sharp knee around the detection threshold to calculate +equalization gain more softly. +Default is 2. Allowed range is between 1 and 8. + +@item ratio +Set the ratio by which the equalization gain is raised. +Default is 1. Allowed range is between 1 and 20. + +@item range +Set the max allowed cut/boost amount. Default is 0.06125. +Allowed range is from 0.00000001 to 1. + +@item mode +Set the mode of filter operation, can be one of the following: + +@table @samp +@item cut +Cut frequencies above detection threshold. +@item boost +Boost frequencies bellow detection threshold. +@end table +Default mode is @samp{cut}. +@end table + +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section adynamicsmooth Apply dynamic smoothing to input audio stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index c8082c4a2f..d40be4b252 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -44,6 +44,7 @@ OBJS-$(CONFIG_ADECORRELATE_FILTER) += af_adecorrelate.o OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o OBJS-$(CONFIG_ADENORM_FILTER) += af_adenorm.o OBJS-$(CONFIG_ADERIVATIVE_FILTER) += af_aderivative.o +OBJS-$(CONFIG_ADYNAMICEQUALIZER_FILTER) += af_adynamicequalizer.o OBJS-$(CONFIG_ADYNAMICSMOOTH_FILTER) += af_adynamicsmooth.o OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o OBJS-$(CONFIG_AEMPHASIS_FILTER) += af_aemphasis.o diff --git a/libavfilter/af_adynamicequalizer.c b/libavfilter/af_adynamicequalizer.c new file mode 100644 index 0000000000..da3d96d7cf --- /dev/null +++ b/libavfilter/af_adynamicequalizer.c @@ -0,0 +1,310 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/ffmath.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "audio.h" +#include "formats.h" +#include "hermite.h" + +typedef struct AudioDynamicEqualizerContext { + const AVClass *class; + + double threshold; + double lin_threshold; + double dfrequency; + double dqfactor; + double tfrequency; + double tqfactor; + double sfrequency; + double sqfactor; + double attack; + double release; + double knee; + double ratio; + double range; + double knee_sqrt; + double attack_coeff; + double release_coeff; + double lin_knee_start; + double lin_knee_stop; + double knee_start; + double knee_stop; + double compressed_knee_start; + int mode; + + AVFrame *state; +} AudioDynamicEqualizerContext; + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + AudioDynamicEqualizerContext *s = ctx->priv; + + s->state = ff_get_audio_buffer(inlink, 7); + if (!s->state) + return AVERROR(ENOMEM); + + return 0; +} + +static double get_gain(AVFilterContext *ctx, double in, + double sample_rate, int ch) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + double *state = (double *)s->state->extended_data[ch]; + const double ratio = s->ratio; + const double knee = s->knee; + const double attack_coeff = s->attack_coeff; + const double release_coeff = s->release_coeff; + const double lin_knee_start = s->lin_knee_start; + const double threshold = s->threshold; + const double knee_start = s->knee_start; + const double knee_stop = s->knee_stop; + const double compressed_knee_start = s->compressed_knee_start; + const double abs_sample = fabs(in); + const int mode = s->mode; + double lin_slope = state[2]; + double range = s->range; + double gain = 0.; + double slope; + int detected; + + lin_slope += (abs_sample - lin_slope) * (abs_sample > lin_slope ? attack_coeff : release_coeff); + + detected = lin_slope > lin_knee_start; + + state[2] = lin_slope; + if (lin_slope <= 0. || !detected) + return 1.; + + slope = log(lin_slope); + gain = (slope - threshold) * ratio + threshold; + + if (knee >= 1.) + gain = hermite_interpolation(slope, knee_stop, knee_start, + knee_stop, compressed_knee_start, + 1., ratio); + if (!mode) + gain = -gain; + + gain = exp(gain - slope); + gain = mode ? av_clipd(gain, 1., 1. / range) : av_clipd(gain, range, 1.); + + return gain; +} + +static double get_svf(double in, double *m, double *a, double *b) +{ + const double v0 = in; + const double v3 = v0 - b[1]; + const double v1 = a[0] * b[0] + a[1] * v3; + const double v2 = b[1] + a[1] * b[0] + a[2] * v3; + + b[0] = 2. * v1 - b[0]; + b[1] = 2. * v2 - b[1]; + + return m[0] * v0 + m[1] * v1 + m[2] * v2; +} + +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const double sample_rate = in->sample_rate; + const double range = s->range; + const double irange = 1. / range; + const double dfrequency = fmin(s->dfrequency, sample_rate * 0.5); + const double tfrequency = fmin(s->tfrequency, sample_rate * 0.5); + const double sfrequency = fmin(s->sfrequency, sample_rate * 0.5); + const double dqfactor = s->dqfactor; + const double tqfactor = s->tqfactor; + const double sqfactor = s->tqfactor; + const double fg = tan(M_PI * tfrequency / sample_rate); + const double dg = tan(M_PI * dfrequency / sample_rate); + const double sg = tan(M_PI * sfrequency / sample_rate); + const int start = (in->channels * jobnr) / nb_jobs; + const int end = (in->channels * (jobnr+1)) / nb_jobs; + const int mode = s->mode; + double da[3], dm[3]; + + { + double k = 1. / dqfactor; + + da[0] = 1. / (1. + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = 0.; + dm[1] = 1.; + dm[2] = 0.; + } + + for (int ch = start; ch < end; ch++) { + const double *src = (const double *)in->extended_data[ch]; + double *dst = (double *)out->extended_data[ch]; + double *state = (double *)s->state->extended_data[ch]; + + for (int n = 0; n < out->nb_samples; n++) { + double fa[3], fm[3], sa[3], sm[3]; + double detect, gain, v; + + detect = get_svf(src[n], dm, da, state); + gain = get_gain(ctx, detect, sample_rate, ch); + + { + double k = 1. / sqfactor; + + sa[0] = 1. / (1. + sg * (sg + k)); + sa[1] = sg * sa[0]; + sa[2] = sg * sa[1]; + + sm[0] = 0.; + sm[1] = 0.; + sm[2] = 1.; + } + + gain = get_svf(gain, sm, sa, &state[5]); + gain = mode ? av_clipd(gain, 1., irange) : av_clipd(gain, range, 1.); + + { + double k = 1. / (tqfactor * gain); + + fa[0] = 1. / (1. + fg * (fg + k)); + fa[1] = fg * fa[0]; + fa[2] = fg * fa[1]; + + fm[0] = 1.; + fm[1] = k * (gain * gain - 1.); + fm[2] = 0.; + } + + v = get_svf(src[n], fm, fa, &state[3]); + dst[n] = ctx->is_disabled ? src[n] : v; + } + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AudioDynamicEqualizerContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + ThreadData td; + AVFrame *out; + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_audio_buffer(outlink, in->nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + s->attack_coeff = FFMIN(1., 1. / (s->attack * in->sample_rate / 4000.)); + s->release_coeff = FFMIN(1., 1. / (s->release * in->sample_rate / 4000.)); + s->knee_sqrt = sqrt(s->knee); + s->lin_knee_stop = s->lin_threshold * s->knee_sqrt; + s->lin_knee_start = s->lin_threshold / s->knee_sqrt; + s->knee_start = log(s->lin_knee_start); + s->knee_stop = log(s->lin_knee_stop); + s->threshold = log(s->lin_threshold); + s->compressed_knee_start = (s->knee_start - s->threshold) / s->ratio + s->threshold; + + td.in = in; + td.out = out; + ff_filter_execute(ctx, filter_channels, &td, NULL, + FFMIN(outlink->channels, ff_filter_get_nb_threads(ctx))); + + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + + av_frame_free(&s->state); +} + +#define OFFSET(x) offsetof(AudioDynamicEqualizerContext, x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption adynamicequalizer_options[] = { + { "threshold", "set detection threshold", OFFSET(lin_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 50, FLAGS }, + { "dfrequency", "set detection frequency", OFFSET(dfrequency), AV_OPT_TYPE_DOUBLE, {.dbl=1000}, 2, 1000000, FLAGS }, + { "dqfactor", "set detection Q factor", OFFSET(dqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, + { "sfrequency", "set smooth frequency", OFFSET(sfrequency), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 2, 1000000, FLAGS }, + { "sqfactor", "set smooth Q factor", OFFSET(sqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, + { "tfrequency", "set target frequency", OFFSET(tfrequency), AV_OPT_TYPE_DOUBLE, {.dbl=1000}, 2, 1000000, FLAGS }, + { "tqfactor", "set target Q factor", OFFSET(tqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, + { "attack", "set attack", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=10}, 1, 2000, FLAGS }, + { "release", "set release", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=80}, 1, 2000, FLAGS }, + { "knee", "set knee factor", OFFSET(knee), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 8, FLAGS }, + { "ratio", "set ratio factor", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 20, FLAGS }, + { "range", "set max gain", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=0.06125},0.00000001, 1,FLAGS }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, + { "cut", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, + { "boost", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(adynamicequalizer); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, +}; + +const AVFilter ff_af_adynamicequalizer = { + .name = "adynamicequalizer", + .description = NULL_IF_CONFIG_SMALL("Apply Dynamic Equalization of input audio."), + .priv_size = sizeof(AudioDynamicEqualizerContext), + .priv_class = &adynamicequalizer_class, + .uninit = uninit, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index c5c0e9b28b..7018337c85 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -37,6 +37,7 @@ extern const AVFilter ff_af_adecorrelate; extern const AVFilter ff_af_adelay; extern const AVFilter ff_af_adenorm; extern const AVFilter ff_af_aderivative; +extern const AVFilter ff_af_adynamicequalizer; extern const AVFilter ff_af_adynamicsmooth; extern const AVFilter ff_af_aecho; extern const AVFilter ff_af_aemphasis;