GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/af_afftfilt.c Lines: 0 232 0.0 %
Date: 2020-10-23 17:01:47 Branches: 0 144 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 2016 Paul B Mahol
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License,
9
 * or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include "libavutil/audio_fifo.h"
22
#include "libavutil/avstring.h"
23
#include "libavfilter/internal.h"
24
#include "libavutil/common.h"
25
#include "libavutil/opt.h"
26
#include "libavcodec/avfft.h"
27
#include "libavutil/eval.h"
28
#include "audio.h"
29
#include "filters.h"
30
#include "window_func.h"
31
32
typedef struct AFFTFiltContext {
33
    const AVClass *class;
34
    char *real_str;
35
    char *img_str;
36
    int fft_size;
37
    int fft_bits;
38
39
    FFTContext *fft, *ifft;
40
    FFTComplex **fft_data;
41
    FFTComplex **fft_temp;
42
    int nb_exprs;
43
    int channels;
44
    int window_size;
45
    AVExpr **real;
46
    AVExpr **imag;
47
    AVAudioFifo *fifo;
48
    int64_t pts;
49
    int hop_size;
50
    float overlap;
51
    AVFrame *buffer;
52
    int eof;
53
    int win_func;
54
    float *window_func_lut;
55
} AFFTFiltContext;
56
57
static const char *const var_names[] = {            "sr",     "b",       "nb",        "ch",        "chs",   "pts",     "re",     "im", NULL };
58
enum                                   { VAR_SAMPLE_RATE, VAR_BIN, VAR_NBBINS, VAR_CHANNEL, VAR_CHANNELS, VAR_PTS, VAR_REAL, VAR_IMAG, VAR_VARS_NB };
59
60
#define OFFSET(x) offsetof(AFFTFiltContext, x)
61
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
62
63
static const AVOption afftfilt_options[] = {
64
    { "real", "set channels real expressions",       OFFSET(real_str), AV_OPT_TYPE_STRING, {.str = "re" }, 0, 0, A },
65
    { "imag", "set channels imaginary expressions",  OFFSET(img_str),  AV_OPT_TYPE_STRING, {.str = "im" }, 0, 0, A },
66
    { "win_size", "set window size", OFFSET(fft_size), AV_OPT_TYPE_INT, {.i64=4096}, 16, 131072, A },
67
    { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64 = WFUNC_HANNING}, 0, NB_WFUNC-1, A, "win_func" },
68
        { "rect",     "Rectangular",      0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT},     0, 0, A, "win_func" },
69
        { "bartlett", "Bartlett",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, A, "win_func" },
70
        { "hann",     "Hann",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, A, "win_func" },
71
        { "hanning",  "Hanning",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, A, "win_func" },
72
        { "hamming",  "Hamming",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING},  0, 0, A, "win_func" },
73
        { "blackman", "Blackman",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, A, "win_func" },
74
        { "welch",    "Welch",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH},    0, 0, A, "win_func" },
75
        { "flattop",  "Flat-top",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP},  0, 0, A, "win_func" },
76
        { "bharris",  "Blackman-Harris",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS},  0, 0, A, "win_func" },
77
        { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, A, "win_func" },
78
        { "bhann",    "Bartlett-Hann",    0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN},    0, 0, A, "win_func" },
79
        { "sine",     "Sine",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE},     0, 0, A, "win_func" },
80
        { "nuttall",  "Nuttall",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL},  0, 0, A, "win_func" },
81
        { "lanczos",  "Lanczos",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS},  0, 0, A, "win_func" },
82
        { "gauss",    "Gauss",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS},    0, 0, A, "win_func" },
83
        { "tukey",    "Tukey",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY},    0, 0, A, "win_func" },
84
        { "dolph",    "Dolph-Chebyshev",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH},    0, 0, A, "win_func" },
85
        { "cauchy",   "Cauchy",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY},   0, 0, A, "win_func" },
86
        { "parzen",   "Parzen",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN},   0, 0, A, "win_func" },
87
        { "poisson",  "Poisson",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON},  0, 0, A, "win_func" },
88
        { "bohman",   "Bohman",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN},   0, 0, A, "win_func" },
89
    { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0,  1, A },
90
    { NULL },
91
};
92
93
AVFILTER_DEFINE_CLASS(afftfilt);
94
95
static inline double getreal(void *priv, double x, double ch)
96
{
97
    AFFTFiltContext *s = priv;
98
    int ich, ix;
99
100
    ich = av_clip(ch, 0, s->nb_exprs - 1);
101
    ix = av_clip(x, 0, s->window_size / 2);
102
103
    return s->fft_data[ich][ix].re;
104
}
105
106
static inline double getimag(void *priv, double x, double ch)
107
{
108
    AFFTFiltContext *s = priv;
109
    int ich, ix;
110
111
    ich = av_clip(ch, 0, s->nb_exprs - 1);
112
    ix = av_clip(x, 0, s->window_size / 2);
113
114
    return s->fft_data[ich][ix].im;
115
}
116
117
static double realf(void *priv, double x, double ch) { return getreal(priv, x, ch); }
118
static double imagf(void *priv, double x, double ch) { return getimag(priv, x, ch); }
119
120
static const char *const func2_names[]    = { "real", "imag", NULL };
121
double (*func2[])(void *, double, double) = {  realf,  imagf, NULL };
122
123
static int config_input(AVFilterLink *inlink)
124
{
125
    AVFilterContext *ctx = inlink->dst;
126
    AFFTFiltContext *s = ctx->priv;
127
    char *saveptr = NULL;
128
    int ret = 0, ch;
129
    float overlap;
130
    char *args;
131
    const char *last_expr = "1";
132
133
    s->channels = inlink->channels;
134
    s->pts  = AV_NOPTS_VALUE;
135
    s->fft_bits = av_log2(s->fft_size);
136
    s->fft  = av_fft_init(s->fft_bits, 0);
137
    s->ifft = av_fft_init(s->fft_bits, 1);
138
    if (!s->fft || !s->ifft)
139
        return AVERROR(ENOMEM);
140
141
    s->window_size = 1 << s->fft_bits;
142
143
    s->fft_data = av_calloc(inlink->channels, sizeof(*s->fft_data));
144
    if (!s->fft_data)
145
        return AVERROR(ENOMEM);
146
147
    s->fft_temp = av_calloc(inlink->channels, sizeof(*s->fft_temp));
148
    if (!s->fft_temp)
149
        return AVERROR(ENOMEM);
150
151
    for (ch = 0; ch < inlink->channels; ch++) {
152
        s->fft_data[ch] = av_calloc(s->window_size, sizeof(**s->fft_data));
153
        if (!s->fft_data[ch])
154
            return AVERROR(ENOMEM);
155
    }
156
157
    for (ch = 0; ch < inlink->channels; ch++) {
158
        s->fft_temp[ch] = av_calloc(s->window_size, sizeof(**s->fft_temp));
159
        if (!s->fft_temp[ch])
160
            return AVERROR(ENOMEM);
161
    }
162
163
    s->real = av_calloc(inlink->channels, sizeof(*s->real));
164
    if (!s->real)
165
        return AVERROR(ENOMEM);
166
167
    s->imag = av_calloc(inlink->channels, sizeof(*s->imag));
168
    if (!s->imag)
169
        return AVERROR(ENOMEM);
170
171
    args = av_strdup(s->real_str);
172
    if (!args)
173
        return AVERROR(ENOMEM);
174
175
    for (ch = 0; ch < inlink->channels; ch++) {
176
        char *arg = av_strtok(ch == 0 ? args : NULL, "|", &saveptr);
177
178
        ret = av_expr_parse(&s->real[ch], arg ? arg : last_expr, var_names,
179
                            NULL, NULL, func2_names, func2, 0, ctx);
180
        if (ret < 0)
181
            goto fail;
182
        if (arg)
183
            last_expr = arg;
184
        s->nb_exprs++;
185
    }
186
187
    av_freep(&args);
188
189
    args = av_strdup(s->img_str ? s->img_str : s->real_str);
190
    if (!args)
191
        return AVERROR(ENOMEM);
192
193
    saveptr = NULL;
194
    last_expr = "1";
195
    for (ch = 0; ch < inlink->channels; ch++) {
196
        char *arg = av_strtok(ch == 0 ? args : NULL, "|", &saveptr);
197
198
        ret = av_expr_parse(&s->imag[ch], arg ? arg : last_expr, var_names,
199
                            NULL, NULL, func2_names, func2, 0, ctx);
200
        if (ret < 0)
201
            goto fail;
202
        if (arg)
203
            last_expr = arg;
204
    }
205
206
    av_freep(&args);
207
208
    s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->window_size);
209
    if (!s->fifo)
210
        return AVERROR(ENOMEM);
211
212
    s->window_func_lut = av_realloc_f(s->window_func_lut, s->window_size,
213
                                      sizeof(*s->window_func_lut));
214
    if (!s->window_func_lut)
215
        return AVERROR(ENOMEM);
216
    generate_window_func(s->window_func_lut, s->window_size, s->win_func, &overlap);
217
    if (s->overlap == 1)
218
        s->overlap = overlap;
219
220
    s->hop_size = s->window_size * (1 - s->overlap);
221
    if (s->hop_size <= 0)
222
        return AVERROR(EINVAL);
223
224
    s->buffer = ff_get_audio_buffer(inlink, s->window_size * 2);
225
    if (!s->buffer)
226
        return AVERROR(ENOMEM);
227
228
fail:
229
    av_freep(&args);
230
231
    return ret;
232
}
233
234
static int filter_frame(AVFilterLink *inlink)
235
{
236
    AVFilterContext *ctx = inlink->dst;
237
    AVFilterLink *outlink = ctx->outputs[0];
238
    AFFTFiltContext *s = ctx->priv;
239
    const int window_size = s->window_size;
240
    const float f = 1. / (s->window_size / 2);
241
    double values[VAR_VARS_NB];
242
    AVFrame *out, *in = NULL;
243
    int ch, n, ret, i;
244
245
    if (!in) {
246
        in = ff_get_audio_buffer(outlink, window_size);
247
        if (!in)
248
            return AVERROR(ENOMEM);
249
    }
250
251
    ret = av_audio_fifo_peek(s->fifo, (void **)in->extended_data, window_size);
252
    if (ret < 0)
253
        goto fail;
254
255
    for (ch = 0; ch < inlink->channels; ch++) {
256
        const float *src = (float *)in->extended_data[ch];
257
        FFTComplex *fft_data = s->fft_data[ch];
258
259
        for (n = 0; n < in->nb_samples; n++) {
260
            fft_data[n].re = src[n] * s->window_func_lut[n];
261
            fft_data[n].im = 0;
262
        }
263
264
        for (; n < window_size; n++) {
265
            fft_data[n].re = 0;
266
            fft_data[n].im = 0;
267
        }
268
    }
269
270
    values[VAR_PTS]         = s->pts;
271
    values[VAR_SAMPLE_RATE] = inlink->sample_rate;
272
    values[VAR_NBBINS]      = window_size / 2;
273
    values[VAR_CHANNELS]    = inlink->channels;
274
275
    for (ch = 0; ch < inlink->channels; ch++) {
276
        FFTComplex *fft_data = s->fft_data[ch];
277
278
        av_fft_permute(s->fft, fft_data);
279
        av_fft_calc(s->fft, fft_data);
280
    }
281
282
    for (ch = 0; ch < inlink->channels; ch++) {
283
        FFTComplex *fft_data = s->fft_data[ch];
284
        FFTComplex *fft_temp = s->fft_temp[ch];
285
        float *buf = (float *)s->buffer->extended_data[ch];
286
        int x;
287
        values[VAR_CHANNEL] = ch;
288
289
        for (n = 0; n <= window_size / 2; n++) {
290
            float fr, fi;
291
292
            values[VAR_BIN] = n;
293
            values[VAR_REAL] = fft_data[n].re;
294
            values[VAR_IMAG] = fft_data[n].im;
295
296
            fr = av_expr_eval(s->real[ch], values, s);
297
            fi = av_expr_eval(s->imag[ch], values, s);
298
299
            fft_temp[n].re = fr;
300
            fft_temp[n].im = fi;
301
        }
302
303
        for (n = window_size / 2 + 1, x = window_size / 2 - 1; n < window_size; n++, x--) {
304
            fft_temp[n].re =  fft_temp[x].re;
305
            fft_temp[n].im = -fft_temp[x].im;
306
        }
307
308
        av_fft_permute(s->ifft, fft_temp);
309
        av_fft_calc(s->ifft, fft_temp);
310
311
        for (i = 0; i < window_size; i++) {
312
            buf[i] += s->fft_temp[ch][i].re * f;
313
        }
314
    }
315
316
    out = ff_get_audio_buffer(outlink, s->hop_size);
317
    if (!out) {
318
        ret = AVERROR(ENOMEM);
319
        goto fail;
320
    }
321
322
    out->pts = s->pts;
323
    s->pts += av_rescale_q(s->hop_size, (AVRational){1, outlink->sample_rate}, outlink->time_base);
324
325
    for (ch = 0; ch < inlink->channels; ch++) {
326
        float *dst = (float *)out->extended_data[ch];
327
        float *buf = (float *)s->buffer->extended_data[ch];
328
329
        for (n = 0; n < s->hop_size; n++)
330
            dst[n] = buf[n] * (1.f - s->overlap);
331
        memmove(buf, buf + s->hop_size, window_size * 4);
332
    }
333
334
    ret = ff_filter_frame(outlink, out);
335
    if (ret < 0)
336
        goto fail;
337
338
    av_audio_fifo_drain(s->fifo, s->hop_size);
339
340
fail:
341
    av_frame_free(&in);
342
    return ret < 0 ? ret : 0;
343
}
344
345
static int activate(AVFilterContext *ctx)
346
{
347
    AVFilterLink *inlink = ctx->inputs[0];
348
    AVFilterLink *outlink = ctx->outputs[0];
349
    AFFTFiltContext *s = ctx->priv;
350
    AVFrame *in = NULL;
351
    int ret = 0, status;
352
    int64_t pts;
353
354
    FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
355
356
    if (!s->eof && av_audio_fifo_size(s->fifo) < s->window_size) {
357
        ret = ff_inlink_consume_frame(inlink, &in);
358
        if (ret < 0)
359
            return ret;
360
361
        if (ret > 0) {
362
            ret = av_audio_fifo_write(s->fifo, (void **)in->extended_data,
363
                                      in->nb_samples);
364
            if (ret >= 0 && s->pts == AV_NOPTS_VALUE)
365
                s->pts = in->pts;
366
367
            av_frame_free(&in);
368
            if (ret < 0)
369
                return ret;
370
        }
371
    }
372
373
    if ((av_audio_fifo_size(s->fifo) >= s->window_size) ||
374
        (av_audio_fifo_size(s->fifo) > 0 && s->eof)) {
375
        ret = filter_frame(inlink);
376
        if (av_audio_fifo_size(s->fifo) >= s->window_size)
377
            ff_filter_set_ready(ctx, 100);
378
        return ret;
379
    }
380
381
    if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) {
382
        if (status == AVERROR_EOF) {
383
            s->eof = 1;
384
            if (av_audio_fifo_size(s->fifo) >= 0) {
385
                ff_filter_set_ready(ctx, 100);
386
                return 0;
387
            }
388
        }
389
    }
390
391
    if (s->eof && av_audio_fifo_size(s->fifo) <= 0) {
392
        ff_outlink_set_status(outlink, AVERROR_EOF, s->pts);
393
        return 0;
394
    }
395
396
    if (!s->eof)
397
        FF_FILTER_FORWARD_WANTED(outlink, inlink);
398
399
    return FFERROR_NOT_READY;
400
}
401
402
static int query_formats(AVFilterContext *ctx)
403
{
404
    AVFilterFormats *formats;
405
    AVFilterChannelLayouts *layouts;
406
    static const enum AVSampleFormat sample_fmts[] = {
407
        AV_SAMPLE_FMT_FLTP,
408
        AV_SAMPLE_FMT_NONE
409
    };
410
    int ret;
411
412
    layouts = ff_all_channel_counts();
413
    if (!layouts)
414
        return AVERROR(ENOMEM);
415
    ret = ff_set_common_channel_layouts(ctx, layouts);
416
    if (ret < 0)
417
        return ret;
418
419
    formats = ff_make_format_list(sample_fmts);
420
    if (!formats)
421
        return AVERROR(ENOMEM);
422
    ret = ff_set_common_formats(ctx, formats);
423
    if (ret < 0)
424
        return ret;
425
426
    formats = ff_all_samplerates();
427
    if (!formats)
428
        return AVERROR(ENOMEM);
429
    return ff_set_common_samplerates(ctx, formats);
430
}
431
432
static av_cold void uninit(AVFilterContext *ctx)
433
{
434
    AFFTFiltContext *s = ctx->priv;
435
    int i;
436
437
    av_fft_end(s->fft);
438
    av_fft_end(s->ifft);
439
440
    for (i = 0; i < s->channels; i++) {
441
        if (s->fft_data)
442
            av_freep(&s->fft_data[i]);
443
        if (s->fft_temp)
444
            av_freep(&s->fft_temp[i]);
445
    }
446
    av_freep(&s->fft_data);
447
    av_freep(&s->fft_temp);
448
449
    for (i = 0; i < s->nb_exprs; i++) {
450
        av_expr_free(s->real[i]);
451
        av_expr_free(s->imag[i]);
452
    }
453
454
    av_freep(&s->real);
455
    av_freep(&s->imag);
456
    av_frame_free(&s->buffer);
457
    av_freep(&s->window_func_lut);
458
459
    av_audio_fifo_free(s->fifo);
460
}
461
462
static const AVFilterPad inputs[] = {
463
    {
464
        .name         = "default",
465
        .type         = AVMEDIA_TYPE_AUDIO,
466
        .config_props = config_input,
467
    },
468
    { NULL }
469
};
470
471
static const AVFilterPad outputs[] = {
472
    {
473
        .name = "default",
474
        .type = AVMEDIA_TYPE_AUDIO,
475
    },
476
    { NULL }
477
};
478
479
AVFilter ff_af_afftfilt = {
480
    .name            = "afftfilt",
481
    .description     = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to samples in frequency domain."),
482
    .priv_size       = sizeof(AFFTFiltContext),
483
    .priv_class      = &afftfilt_class,
484
    .inputs          = inputs,
485
    .outputs         = outputs,
486
    .activate        = activate,
487
    .query_formats   = query_formats,
488
    .uninit          = uninit,
489
};