[FFmpeg-devel] [PATCH] avfilter: add afwtdn filter

Paul B Mahol onemda at gmail.com
Sun May 31 23:29:35 EEST 2020


Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
 libavfilter/Makefile     |   1 +
 libavfilter/af_afwtdn.c  | 619 +++++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c |   1 +
 3 files changed, 621 insertions(+)
 create mode 100644 libavfilter/af_afwtdn.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 5123540653..191826a622 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -50,6 +50,7 @@ OBJS-$(CONFIG_AFFTDN_FILTER)                 += af_afftdn.o
 OBJS-$(CONFIG_AFFTFILT_FILTER)               += af_afftfilt.o
 OBJS-$(CONFIG_AFIR_FILTER)                   += af_afir.o
 OBJS-$(CONFIG_AFORMAT_FILTER)                += af_aformat.o
+OBJS-$(CONFIG_AFWTDN_FILTER)                 += af_afwtdn.o
 OBJS-$(CONFIG_AGATE_FILTER)                  += af_agate.o
 OBJS-$(CONFIG_AIIR_FILTER)                   += af_aiir.o
 OBJS-$(CONFIG_AINTEGRAL_FILTER)              += af_aderivative.o
diff --git a/libavfilter/af_afwtdn.c b/libavfilter/af_afwtdn.c
new file mode 100644
index 0000000000..00b93c29af
--- /dev/null
+++ b/libavfilter/af_afwtdn.c
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2020 Paul B Mahol
+ *
+ * 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 <float.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/audio_fifo.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "audio.h"
+#include "filters.h"
+#include "formats.h"
+
+static const double coif5_lp[30] = {
+    -9.517657273819165e-08, -1.6744288576823017e-07,
+    2.0637618513646814e-06, 3.7346551751414047e-06,
+    -2.1315026809955787e-05, -4.134043227251251e-05,
+    0.00014054114970203437, 0.00030225958181306315,
+    -0.0006381313430451114, -0.0016628637020130838,
+    0.0024333732126576722, 0.006764185448053083,
+    -0.009164231162481846, -0.01976177894257264,
+    0.03268357426711183, 0.0412892087501817,
+    -0.10557420870333893, -0.06203596396290357,
+    0.4379916261718371, 0.7742896036529562,
+    0.4215662066908515, -0.05204316317624377,
+    -0.09192001055969624, 0.02816802897093635,
+    0.023408156785839195, -0.010131117519849788,
+    -0.004159358781386048, 0.0021782363581090178,
+    0.00035858968789573785, -0.00021208083980379827,
+};
+
+static const double coif5_hp[30] = {
+    0.00021208083980379827, 0.00035858968789573785,
+    -0.0021782363581090178, -0.004159358781386048,
+    0.010131117519849788, 0.023408156785839195,
+    -0.02816802897093635, -0.09192001055969624,
+    0.05204316317624377, 0.4215662066908515,
+    -0.7742896036529562, 0.4379916261718371,
+    0.06203596396290357, -0.10557420870333893,
+    -0.0412892087501817, 0.03268357426711183,
+    0.01976177894257264, -0.009164231162481846,
+    -0.006764185448053083, 0.0024333732126576722,
+    0.0016628637020130838, -0.0006381313430451114,
+    -0.00030225958181306315, 0.00014054114970203437,
+    4.134043227251251e-05, -2.1315026809955787e-05,
+    -3.7346551751414047e-06, 2.0637618513646814e-06,
+    1.6744288576823017e-07, -9.517657273819165e-08,
+};
+
+static const double coif5_ilp[30] = {
+    -0.00021208083980379827, 0.00035858968789573785,
+    0.0021782363581090178, -0.004159358781386048,
+    -0.010131117519849788, 0.023408156785839195,
+    0.02816802897093635, -0.09192001055969624,
+    -0.05204316317624377, 0.4215662066908515,
+    0.7742896036529562, 0.4379916261718371,
+    -0.06203596396290357, -0.10557420870333893,
+    0.0412892087501817, 0.03268357426711183,
+    -0.01976177894257264, -0.009164231162481846,
+    0.006764185448053083, 0.0024333732126576722,
+    -0.0016628637020130838, -0.0006381313430451114,
+    0.00030225958181306315, 0.00014054114970203437,
+    -4.134043227251251e-05, -2.1315026809955787e-05,
+    3.7346551751414047e-06, 2.0637618513646814e-06,
+    -1.6744288576823017e-07, -9.517657273819165e-08,
+};
+
+static const double coif5_ihp[30] = {
+    -9.517657273819165e-08, 1.6744288576823017e-07,
+    2.0637618513646814e-06, -3.7346551751414047e-06,
+    -2.1315026809955787e-05, 4.134043227251251e-05,
+    0.00014054114970203437, -0.00030225958181306315,
+    -0.0006381313430451114, 0.0016628637020130838,
+    0.0024333732126576722, -0.006764185448053083,
+    -0.009164231162481846, 0.01976177894257264,
+    0.03268357426711183, -0.0412892087501817,
+    -0.10557420870333893, 0.06203596396290357,
+    0.4379916261718371, -0.7742896036529562,
+    0.4215662066908515, 0.05204316317624377,
+    -0.09192001055969624, -0.02816802897093635,
+    0.023408156785839195, 0.010131117519849788,
+    -0.004159358781386048, -0.0021782363581090178,
+    0.00035858968789573785, 0.00021208083980379827,
+};
+
+static const double deb10_lp[20] = {
+    -1.326420300235487e-05, 9.358867000108985e-05,
+    -0.0001164668549943862, -0.0006858566950046825,
+    0.00199240529499085, 0.0013953517469940798,
+    -0.010733175482979604, 0.0036065535669883944,
+    0.03321267405893324, -0.02945753682194567,
+    -0.07139414716586077, 0.09305736460380659,
+    0.12736934033574265, -0.19594627437659665,
+    -0.24984642432648865, 0.2811723436604265,
+    0.6884590394525921, 0.5272011889309198,
+    0.18817680007762133, 0.026670057900950818,
+};
+
+static const double deb10_hp[20] = {
+    -0.026670057900950818, 0.18817680007762133,
+    -0.5272011889309198, 0.6884590394525921,
+    -0.2811723436604265, -0.24984642432648865,
+    0.19594627437659665, 0.12736934033574265,
+    -0.09305736460380659, -0.07139414716586077,
+    0.02945753682194567, 0.03321267405893324,
+    -0.0036065535669883944, -0.010733175482979604,
+    -0.0013953517469940798, 0.00199240529499085,
+    0.0006858566950046825, -0.0001164668549943862,
+    -9.358867000108985e-05, -1.326420300235487e-05,
+};
+
+static const double deb10_ilp[20] = {
+    0.026670057900950818, 0.18817680007762133,
+    0.5272011889309198, 0.6884590394525921,
+    0.2811723436604265, -0.24984642432648865,
+    -0.19594627437659665, 0.12736934033574265,
+    0.09305736460380659, -0.07139414716586077,
+    -0.02945753682194567, 0.03321267405893324,
+    0.0036065535669883944, -0.010733175482979604,
+    0.0013953517469940798, 0.00199240529499085,
+    -0.0006858566950046825, -0.0001164668549943862,
+    9.358867000108985e-05, -1.326420300235487e-05,
+};
+
+static const double deb10_ihp[20] = {
+    -1.326420300235487e-05, -9.358867000108985e-05,
+    -0.0001164668549943862, 0.0006858566950046825,
+    0.00199240529499085, -0.0013953517469940798,
+    -0.010733175482979604, -0.0036065535669883944,
+    0.03321267405893324, 0.02945753682194567,
+    -0.07139414716586077, -0.09305736460380659,
+    0.12736934033574265, 0.19594627437659665,
+    -0.24984642432648865, -0.2811723436604265,
+    0.6884590394525921, -0.5272011889309198,
+    0.18817680007762133, -0.026670057900950818,
+};
+
+static const double deb4_lp[8] = {
+    -0.0105974018, 0.0328830117,
+     0.0308413818, -0.1870348117,
+    -0.0279837694, 0.6308807679,
+     0.7148465706, 0.2303778133,
+};
+
+static const double deb4_hp[8] = {
+    -0.2303778133, 0.7148465706,
+    -0.6308807679, -0.0279837694,
+     0.1870348117, 0.0308413818,
+    -0.0328830117, -0.0105974018,
+};
+
+static const double deb4_ilp[8] = {
+     0.23037781330885523, 0.7148465705525415,
+     0.6308807679295904, -0.02798376941698385,
+    -0.18703481171888114, 0.030841381835986965,
+     0.032883011666982945, -0.010597401784997278,
+};
+
+static const double deb4_ihp[8] = {
+    -0.010597401784997278, -0.032883011666982945,
+     0.030841381835986965, 0.18703481171888114,
+    -0.02798376941698385, -0.6308807679295904,
+     0.7148465705525415, -0.23037781330885523,
+};
+
+typedef struct AudioFWTDNContext {
+    const AVClass *class;
+
+    double sigma;
+    double percent;
+
+    int wavelet_type;
+    int thresholding_function;
+    int nb_samples;
+    int levels;
+    int wavelet_length;
+    int length[11];
+    int outlength;
+
+    const double *lp, *hp;
+    const double *ilp, *ihp;
+
+    AVFrame *temp0, *temp1;
+    AVFrame *wtc;
+
+    void (*thresholding)(AVFilterContext *ctx, double *wtc, int length, double sigma);
+} AudioFWTDNContext;
+
+#define OFFSET(x) offsetof(AudioFWTDNContext, x)
+#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption afwtdn_options[] = {
+    { "sigma", "set noise sigma", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1000, AF },
+    { "level", "set wavelet level", OFFSET(levels), AV_OPT_TYPE_INT, {.i64=5}, 1,   10, AF },
+    { "thref", "set threshold function", OFFSET(thresholding_function), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, AF, "thret" },
+    { "hard",  "hard", 0, AV_OPT_TYPE_CONST,  {.i64=0}, 0, 0, AF, "thret"},
+    { "soft",  "soft", 0, AV_OPT_TYPE_CONST,  {.i64=1}, 0, 0, AF, "thret"},
+    { "garrote", "garrote", 0, AV_OPT_TYPE_CONST,  {.i64=2}, 0, 0, AF, "thret"},
+    { "new",   "new", 0, AV_OPT_TYPE_CONST,  {.i64=3}, 0, 0, AF, "thret"},
+    { "semisoft", "semisoft", 0, AV_OPT_TYPE_CONST,  {.i64=4}, 0, 0, AF, "thret"},
+    { "wavet", "set wavelet type", OFFSET(wavelet_type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, "wavet"},
+    { "deb4",  "deb4", 0, AV_OPT_TYPE_CONST,  {.i64=0}, 0, 2, AF, "wavet"},
+    { "deb10", "deb10", 0, AV_OPT_TYPE_CONST,  {.i64=1}, 0, 2, AF, "wavet"},
+    { "coif5", "coif5", 0, AV_OPT_TYPE_CONST,  {.i64=2}, 0, 2, AF, "wavet"},
+    { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_DOUBLE, {.dbl=85}, 0, 100, AF },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(afwtdn);
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats = NULL;
+    AVFilterChannelLayouts *layouts = NULL;
+    static const enum AVSampleFormat sample_fmts[] = {
+        AV_SAMPLE_FMT_DBLP,
+        AV_SAMPLE_FMT_NONE
+    };
+    int ret;
+
+    formats = ff_make_format_list(sample_fmts);
+    if (!formats)
+        return AVERROR(ENOMEM);
+    ret = ff_set_common_formats(ctx, formats);
+    if (ret < 0)
+        return ret;
+
+    layouts = ff_all_channel_counts();
+    if (!layouts)
+        return AVERROR(ENOMEM);
+
+    ret = ff_set_common_channel_layouts(ctx, layouts);
+    if (ret < 0)
+        return ret;
+
+    formats = ff_all_samplerates();
+    return ff_set_common_samplerates(ctx, formats);
+}
+
+static void hard_thresholding(AVFilterContext *ctx, double *wtc, int length, double threshold)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    const double frac = 1.0 - s->percent * 0.01;
+
+    for (int i = 0; i < length; i++) {
+        if (fabs(wtc[i]) <= threshold)
+            wtc[i] *= frac;
+    }
+}
+
+static void soft_thresholding(AVFilterContext *ctx, double *wtc, int length, double threshold)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    const double frac = 1.0 - s->percent * 0.01;
+    const double shift = threshold * 0.01 * s->percent;
+
+    for (int i = 0; i < length; i++) {
+        const double temp = fabs(wtc[i]);
+        if (temp <= threshold)
+            wtc[i] *= frac;
+        else
+            wtc[i] = (wtc[i] < 0.0 ? -1.0 : (wtc[i] > 0.0 ? 1.0 : 0.0)) * (temp - shift);
+    }
+}
+
+static void semisoft_thresholding(AVFilterContext *ctx, double *wtc, int length, double threshold)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    const double percent01 = s->percent * 0.01;
+    const double frac = 1.0 - percent01;
+    const double sigma2 = threshold * 0.1 * s->percent;
+
+    for (int i = 0; i < length; i++) {
+        const double temp = fabs(wtc[i]);
+        if (temp <= threshold)
+            wtc[i] *= frac;
+        else if (temp <= sigma2)
+            wtc[i] = wtc[i] * sigma2 * (1.0 - threshold / temp) / (sigma2 - threshold);
+    }
+}
+
+static void garrote_thresholding(AVFilterContext *ctx, double *wtc, int length, double threshold)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    const double percent01 = s->percent * 0.01;
+    const double tr2 = threshold * threshold * percent01;
+    const double frac = 1.0 - percent01;
+
+    for (int i = 0; i < length; i++) {
+        const double temp = fabs(wtc[i]);
+        if (temp <= threshold) {
+            wtc[i] *= frac;
+        } else {
+            const double tp2 = temp * temp;
+            wtc[i] *= (tp2 - tr2) / tp2;
+        }
+    }
+}
+
+static void new_thresholding(AVFilterContext *ctx, double *wtc, int length, double threshold)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    const double percent01 = s->percent * 0.01;
+    const double frac = 1.0 - percent01;
+
+    for (int i = 0; i < length; i++) {
+        const double temp = fabs(wtc[i]);
+        if (temp <= threshold)
+            wtc[i] *= frac;
+        else
+            wtc[i] = FFSIGN(wtc[i]) * (temp - threshold / exp((temp - threshold) / threshold));
+    }
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AudioFWTDNContext *s = ctx->priv;
+    int N, i;
+
+    switch (s->wavelet_type) {
+    case 0:
+        s->wavelet_length = 8;
+        s->lp  = deb4_lp;
+        s->hp  = deb4_hp;
+        s->ilp = deb4_ilp;
+        s->ihp = deb4_ihp;
+        break;
+    case 1:
+        s->wavelet_length = 20;
+        s->lp  = deb10_lp;
+        s->hp  = deb10_hp;
+        s->ilp = deb10_ilp;
+        s->ihp = deb10_ihp;
+        break;
+    case 2:
+        s->wavelet_length = 30;
+        s->lp  = coif5_lp;
+        s->hp  = coif5_hp;
+        s->ilp = coif5_ilp;
+        s->ihp = coif5_ihp;
+        break;
+    default:
+        av_assert0(0);
+    }
+
+    s->nb_samples = 8192;
+    s->levels = FFMIN(s->levels, lrint(log(s->nb_samples / (s->wavelet_length - 1.0)) / M_LN2));
+    N = s->nb_samples;
+    i = s->levels;
+
+    while (i > 0) {
+        N = N + s->wavelet_length - 2;
+        N = ceil(N / 2.0);
+        s->length[i] = N;
+        s->outlength += N;
+        i--;
+    }
+
+    s->length[0]  = s->length[1];
+    s->outlength += s->length[0];
+
+    s->temp0 = ff_get_audio_buffer(outlink, 2 * s->nb_samples + 2 * s->wavelet_length);
+    s->temp1 = ff_get_audio_buffer(outlink, 2 * s->nb_samples + 2 * s->wavelet_length);
+    s->wtc   = ff_get_audio_buffer(outlink, 2 * s->nb_samples + 2 * s->wavelet_length);
+    if (!s->temp0 || !s->temp1 || !s->wtc)
+        return AVERROR(ENOMEM);
+
+    switch (s->thresholding_function) {
+    case 0:
+        s->thresholding = hard_thresholding;
+        break;
+    case 1:
+        s->thresholding = soft_thresholding;
+        break;
+    case 2:
+        s->thresholding = garrote_thresholding;
+        break;
+    case 3:
+        s->thresholding = new_thresholding;
+        break;
+    case 4:
+        s->thresholding = semisoft_thresholding;
+        break;
+    default:
+        av_assert0(0);
+    }
+
+    return 0;
+}
+
+static void dwt(double *src, int N,
+                const double *lf, const double *hf, int flen,
+                double *low, double *high, int lhlen)
+{
+    for (int i = 0; i < lhlen; i++) {
+        const int t = 2 * i + 1;
+
+        low[i] = 0.0;
+        high[i] = 0.0;
+
+        for (int l = 0; l < flen; l++) {
+            if (t - l >= 0 && t - l < N) {
+                const int is = t - l;
+                low[i] += lf[l] * src[is];
+                high[i] += hf[l] * src[is];
+            } else if (t - l < 0) {
+                const int is = -t + l - 1;
+                low[i] += lf[l] * src[is];
+                high[i] += hf[l] * src[is];
+            } else if (t - l >= N) {
+                const int is = 2 * N - t + l - 1;
+                low[i] += lf[l] * src[is];
+                high[i] += hf[l] * src[is];
+            }
+        }
+    }
+}
+
+static void idwt(double *low, int lhlen, double *high,
+                 const double *lf, const double *hf,
+                 int flen, double *dst)
+{
+    int m = -2, n = -1;
+
+    for (int v = 0; v < lhlen; v++) {
+        m += 2;
+        n += 2;
+        dst[m] = 0.0;
+        dst[n] = 0.0;
+        for (int l = 0; l < flen / 2; l++) {
+            const int t = 2 * l;
+
+            if (v - l >= 0 && v - l < lhlen) {
+                const int is = v - l;
+
+                dst[m] += lf[t] * low[is] + hf[t] * high[is];
+                dst[n] += lf[t + 1] * low[is] + hf[t + 1] * high[is];
+            }
+        }
+    }
+}
+
+static void dwt_levels(AudioFWTDNContext *s, int levels, int inlength, int outlength,
+                       const double *in, double *temp0, double *temp1, double *wtc)
+{
+    int N = outlength;
+    int temp_len = s->nb_samples;
+
+    for (int i = 0; i < inlength; i++)
+        temp0[i] = in[i];
+    for (int i = inlength; i < s->nb_samples; i++)
+        temp0[i] = 0.;
+
+    for (int level = 0; level < levels; level++) {
+        int level_length = s->length[levels - level];
+
+        N -= level_length;
+        dwt(temp0, temp_len, s->lp, s->hp, s->wavelet_length, temp1, wtc + N, level_length);
+        temp_len = s->length[levels - level];
+
+        if (level == levels - 1) {
+            for (int i = 0; i < level_length; i++)
+                wtc[i] = temp1[i];
+        } else {
+            for (int i = 0; i < level_length; i++)
+                temp0[i] = temp1[i];
+        }
+    }
+}
+
+static void idwt_levels(AudioFWTDNContext *s, int levels, double *out,
+                        double *temp0, double *temp1, double *wtc)
+{
+    int app_len = s->length[0];
+    int det_len = s->length[1];
+    int lf = (s->wavelet_length + s->wavelet_length) / 2;
+    int iter = app_len;
+
+    for (int i = 0; i < app_len; i++)
+        out[i] = wtc[i];
+
+    for (int i = 0; i < levels; i++) {
+        idwt(out, det_len, wtc + iter, s->ilp, s->ihp, s->wavelet_length, temp1);
+        for (int k = lf - 2; k < 2 * det_len; k++) {
+            out[k - lf + 2] = temp1[k];
+        }
+
+        iter += det_len;
+        det_len = s->length[i + 2];
+    }
+}
+
+typedef struct ThreadData {
+    AVFrame *in, *out;
+} ThreadData;
+
+static int filter_channel(AVFilterContext *ctx, void *arg, int ch, int nb_jobs)
+{
+    AudioFWTDNContext *s = ctx->priv;
+    ThreadData *td = arg;
+    AVFrame *in = td->in;
+    AVFrame *out = td->out;
+    const double *src = (const double *)(in->extended_data[ch]);
+    double *temp0 = (double *)s->temp0->extended_data[ch];
+    double *temp1 = (double *)s->temp1->extended_data[ch];
+    double *wtc = (double *)s->wtc->extended_data[ch];
+    double *dst = (double *)out->extended_data[ch];
+    double sigma = 4.0 * s->sigma / 10000.;
+
+    dwt_levels(s, s->levels, in->nb_samples, s->outlength, src, temp0, temp1, wtc);
+
+    s->thresholding(ctx, wtc, 2 * (s->wavelet_length + s->nb_samples), sigma);
+
+    idwt_levels(s, s->levels, dst, temp0, temp1, wtc);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    ThreadData td;
+    AVFrame *out = NULL;
+
+    out = ff_get_audio_buffer(outlink, in->nb_samples);
+    if (!out) {
+        av_frame_free(&in);
+        return AVERROR(ENOMEM);
+    }
+    out->pts = in->pts;
+
+    td.in  = in;
+    td.out = out;
+    ctx->internal->execute(ctx, filter_channel, &td, NULL, inlink->channels);
+
+    av_frame_free(&in);
+    return ff_filter_frame(outlink, out);
+}
+
+static int activate(AVFilterContext *ctx)
+{
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    AudioFWTDNContext *s = ctx->priv;
+    AVFrame *in = NULL;
+    int ret;
+
+    FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
+
+    ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in);
+    if (ret < 0)
+        return ret;
+    if (ret > 0)
+        return filter_frame(inlink, in);
+
+    FF_FILTER_FORWARD_STATUS(inlink, outlink);
+    FF_FILTER_FORWARD_WANTED(outlink, inlink);
+
+    return FFERROR_NOT_READY;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    AudioFWTDNContext *s = ctx->priv;
+
+    av_frame_free(&s->wtc);
+    av_frame_free(&s->temp0);
+    av_frame_free(&s->temp1);
+}
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_AUDIO,
+    },
+    { NULL }
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_AUDIO,
+        .config_props  = config_output,
+    },
+    { NULL }
+};
+
+AVFilter ff_af_afwtdn = {
+    .name          = "afwtdn",
+    .description   = NULL_IF_CONFIG_SMALL("Reduce broadband noise from stream using Fast Wavelet Transform."),
+    .query_formats = query_formats,
+    .priv_size     = sizeof(AudioFWTDNContext),
+    .priv_class    = &afwtdn_class,
+    .activate      = activate,
+    .uninit        = uninit,
+    .inputs        = inputs,
+    .outputs       = outputs,
+    .flags         = AVFILTER_FLAG_SLICE_THREADS,
+};
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 1183e40267..de5884529c 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -43,6 +43,7 @@ extern AVFilter ff_af_afftdn;
 extern AVFilter ff_af_afftfilt;
 extern AVFilter ff_af_afir;
 extern AVFilter ff_af_aformat;
+extern AVFilter ff_af_afwtdn;
 extern AVFilter ff_af_agate;
 extern AVFilter ff_af_aiir;
 extern AVFilter ff_af_aintegral;
-- 
2.17.1



More information about the ffmpeg-devel mailing list