X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Faf_afftfilt.c;h=6c1ddbe51fc2ffcae852fd02e4d4daa2afafb51d;hb=a04ad248a05e7b613abe09b3bb067f555108d794;hp=8518f08dc5eb5238abe1e5d81a6e5d8794ea8f8d;hpb=bad70b7af6b909691f5389e14eb7d0c03db10af9;p=ffmpeg diff --git a/libavfilter/af_afftfilt.c b/libavfilter/af_afftfilt.c index 8518f08dc5e..6c1ddbe51fc 100644 --- a/libavfilter/af_afftfilt.c +++ b/libavfilter/af_afftfilt.c @@ -26,18 +26,21 @@ #include "libavcodec/avfft.h" #include "libavutil/eval.h" #include "audio.h" +#include "filters.h" #include "window_func.h" typedef struct AFFTFiltContext { const AVClass *class; char *real_str; char *img_str; + int fft_size; int fft_bits; FFTContext *fft, *ifft; FFTComplex **fft_data; FFTComplex **fft_temp; int nb_exprs; + int channels; int window_size; AVExpr **real; AVExpr **imag; @@ -46,9 +49,8 @@ typedef struct AFFTFiltContext { int hop_size; float overlap; AVFrame *buffer; - int start, end; + int eof; int win_func; - float win_scale; float *window_func_lut; } AFFTFiltContext; @@ -61,21 +63,7 @@ enum { VAR_SAMPLE_RATE, VAR_BIN, VAR_NBBINS, V static const AVOption afftfilt_options[] = { { "real", "set channels real expressions", OFFSET(real_str), AV_OPT_TYPE_STRING, {.str = "re" }, 0, 0, A }, { "imag", "set channels imaginary expressions", OFFSET(img_str), AV_OPT_TYPE_STRING, {.str = "im" }, 0, 0, A }, - { "win_size", "set window size", OFFSET(fft_bits), AV_OPT_TYPE_INT, {.i64=12}, 4, 17, A, "fft" }, - { "w16", 0, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, A, "fft" }, - { "w32", 0, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, A, "fft" }, - { "w64", 0, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, A, "fft" }, - { "w128", 0, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, A, "fft" }, - { "w256", 0, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, A, "fft" }, - { "w512", 0, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, A, "fft" }, - { "w1024", 0, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, A, "fft" }, - { "w2048", 0, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, A, "fft" }, - { "w4096", 0, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, A, "fft" }, - { "w8192", 0, 0, AV_OPT_TYPE_CONST, {.i64=13}, 0, 0, A, "fft" }, - { "w16384", 0, 0, AV_OPT_TYPE_CONST, {.i64=14}, 0, 0, A, "fft" }, - { "w32768", 0, 0, AV_OPT_TYPE_CONST, {.i64=15}, 0, 0, A, "fft" }, - { "w65536", 0, 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, A, "fft" }, - { "w131072",0, 0, AV_OPT_TYPE_CONST, {.i64=17}, 0, 0, A, "fft" }, + { "win_size", "set window size", OFFSET(fft_size), AV_OPT_TYPE_INT, {.i64=4096}, 16, 131072, A }, { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64 = WFUNC_HANNING}, 0, NB_WFUNC-1, A, "win_func" }, { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, A, "win_func" }, { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, A, "win_func" }, @@ -130,19 +118,21 @@ static double realf(void *priv, double x, double ch) { return getreal(priv, x, c static double imagf(void *priv, double x, double ch) { return getimag(priv, x, ch); } static const char *const func2_names[] = { "real", "imag", NULL }; -double (*func2[])(void *, double, double) = { realf, imagf, NULL }; +static double (*const func2[])(void *, double, double) = { realf, imagf, NULL }; static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AFFTFiltContext *s = ctx->priv; char *saveptr = NULL; - int ret = 0, ch, i; + int ret = 0, ch; float overlap; char *args; const char *last_expr = "1"; + s->channels = inlink->channels; s->pts = AV_NOPTS_VALUE; + s->fft_bits = av_log2(s->fft_size); s->fft = av_fft_init(s->fft_bits, 0); s->ifft = av_fft_init(s->fft_bits, 1); if (!s->fft || !s->ifft) @@ -188,30 +178,32 @@ static int config_input(AVFilterLink *inlink) ret = av_expr_parse(&s->real[ch], arg ? arg : last_expr, var_names, NULL, NULL, func2_names, func2, 0, ctx); if (ret < 0) - break; + goto fail; if (arg) last_expr = arg; s->nb_exprs++; } - av_free(args); + av_freep(&args); args = av_strdup(s->img_str ? s->img_str : s->real_str); if (!args) return AVERROR(ENOMEM); + saveptr = NULL; + last_expr = "1"; for (ch = 0; ch < inlink->channels; ch++) { char *arg = av_strtok(ch == 0 ? args : NULL, "|", &saveptr); ret = av_expr_parse(&s->imag[ch], arg ? arg : last_expr, var_names, NULL, NULL, func2_names, func2, 0, ctx); if (ret < 0) - break; + goto fail; if (arg) last_expr = arg; } - av_free(args); + av_freep(&args); s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->window_size); if (!s->fifo) @@ -225,10 +217,6 @@ static int config_input(AVFilterLink *inlink) if (s->overlap == 1) s->overlap = overlap; - for (s->win_scale = 0, i = 0; i < s->window_size; i++) { - s->win_scale += s->window_func_lut[i] * s->window_func_lut[i]; - } - s->hop_size = s->window_size * (1 - s->overlap); if (s->hop_size <= 0) return AVERROR(EINVAL); @@ -237,152 +225,178 @@ static int config_input(AVFilterLink *inlink) if (!s->buffer) return AVERROR(ENOMEM); +fail: + av_freep(&args); + return ret; } -static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +static int filter_frame(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; AFFTFiltContext *s = ctx->priv; const int window_size = s->window_size; - const float f = 1. / s->win_scale; + const float f = 1. / (s->window_size / 2); double values[VAR_VARS_NB]; AVFrame *out, *in = NULL; - int ch, n, ret, i, j, k; - int start = s->start, end = s->end; + int ch, n, ret, i; - if (s->pts == AV_NOPTS_VALUE) - s->pts = frame->pts; + if (!in) { + in = ff_get_audio_buffer(outlink, window_size); + if (!in) + return AVERROR(ENOMEM); + } - ret = av_audio_fifo_write(s->fifo, (void **)frame->extended_data, frame->nb_samples); - av_frame_free(&frame); + ret = av_audio_fifo_peek(s->fifo, (void **)in->extended_data, window_size); if (ret < 0) - return ret; + goto fail; + + for (ch = 0; ch < inlink->channels; ch++) { + const float *src = (float *)in->extended_data[ch]; + FFTComplex *fft_data = s->fft_data[ch]; - while (av_audio_fifo_size(s->fifo) >= window_size) { - if (!in) { - in = ff_get_audio_buffer(outlink, window_size); - if (!in) - return AVERROR(ENOMEM); + for (n = 0; n < in->nb_samples; n++) { + fft_data[n].re = src[n] * s->window_func_lut[n]; + fft_data[n].im = 0; } - ret = av_audio_fifo_peek(s->fifo, (void **)in->extended_data, window_size); - if (ret < 0) - break; + for (; n < window_size; n++) { + fft_data[n].re = 0; + fft_data[n].im = 0; + } + } - for (ch = 0; ch < inlink->channels; ch++) { - const float *src = (float *)in->extended_data[ch]; - FFTComplex *fft_data = s->fft_data[ch]; + values[VAR_PTS] = s->pts; + values[VAR_SAMPLE_RATE] = inlink->sample_rate; + values[VAR_NBBINS] = window_size / 2; + values[VAR_CHANNELS] = inlink->channels; - for (n = 0; n < in->nb_samples; n++) { - fft_data[n].re = src[n] * s->window_func_lut[n]; - fft_data[n].im = 0; - } + for (ch = 0; ch < inlink->channels; ch++) { + FFTComplex *fft_data = s->fft_data[ch]; - for (; n < window_size; n++) { - fft_data[n].re = 0; - fft_data[n].im = 0; - } - } + av_fft_permute(s->fft, fft_data); + av_fft_calc(s->fft, fft_data); + } - values[VAR_PTS] = s->pts; - values[VAR_SAMPLE_RATE] = inlink->sample_rate; - values[VAR_NBBINS] = window_size / 2; - values[VAR_CHANNELS] = inlink->channels; + for (ch = 0; ch < inlink->channels; ch++) { + FFTComplex *fft_data = s->fft_data[ch]; + FFTComplex *fft_temp = s->fft_temp[ch]; + float *buf = (float *)s->buffer->extended_data[ch]; + int x; + values[VAR_CHANNEL] = ch; - for (ch = 0; ch < inlink->channels; ch++) { - FFTComplex *fft_data = s->fft_data[ch]; + for (n = 0; n <= window_size / 2; n++) { + float fr, fi; - av_fft_permute(s->fft, fft_data); - av_fft_calc(s->fft, fft_data); - } + values[VAR_BIN] = n; + values[VAR_REAL] = fft_data[n].re; + values[VAR_IMAG] = fft_data[n].im; - for (ch = 0; ch < inlink->channels; ch++) { - FFTComplex *fft_data = s->fft_data[ch]; - FFTComplex *fft_temp = s->fft_temp[ch]; - float *buf = (float *)s->buffer->extended_data[ch]; - int x; - values[VAR_CHANNEL] = ch; + fr = av_expr_eval(s->real[ch], values, s); + fi = av_expr_eval(s->imag[ch], values, s); - for (n = 0; n <= window_size / 2; n++) { - float fr, fi; + fft_temp[n].re = fr; + fft_temp[n].im = fi; + } - values[VAR_BIN] = n; - values[VAR_REAL] = fft_data[n].re; - values[VAR_IMAG] = fft_data[n].im; + for (n = window_size / 2 + 1, x = window_size / 2 - 1; n < window_size; n++, x--) { + fft_temp[n].re = fft_temp[x].re; + fft_temp[n].im = -fft_temp[x].im; + } - fr = av_expr_eval(s->real[ch], values, s); - fi = av_expr_eval(s->imag[ch], values, s); + av_fft_permute(s->ifft, fft_temp); + av_fft_calc(s->ifft, fft_temp); - fft_temp[n].re = fr; - fft_temp[n].im = fi; - } + for (i = 0; i < window_size; i++) { + buf[i] += s->fft_temp[ch][i].re * f; + } + } - for (n = window_size / 2 + 1, x = window_size / 2 - 1; n < window_size; n++, x--) { - fft_temp[n].re = fft_temp[x].re; - fft_temp[n].im = -fft_temp[x].im; - } + out = ff_get_audio_buffer(outlink, s->hop_size); + if (!out) { + ret = AVERROR(ENOMEM); + goto fail; + } - av_fft_permute(s->ifft, fft_temp); - av_fft_calc(s->ifft, fft_temp); + out->pts = s->pts; + s->pts += av_rescale_q(s->hop_size, (AVRational){1, outlink->sample_rate}, outlink->time_base); - start = s->start; - end = s->end; - k = end; - for (i = 0, j = start; j < k && i < window_size; i++, j++) { - buf[j] += s->fft_temp[ch][i].re * f; - } + for (ch = 0; ch < inlink->channels; ch++) { + float *dst = (float *)out->extended_data[ch]; + float *buf = (float *)s->buffer->extended_data[ch]; - for (; i < window_size; i++, j++) { - buf[j] = s->fft_temp[ch][i].re * f; - } + for (n = 0; n < s->hop_size; n++) + dst[n] = buf[n] * (1.f - s->overlap); + memmove(buf, buf + s->hop_size, window_size * 4); + } - start += s->hop_size; - end = j; - } + ret = ff_filter_frame(outlink, out); + if (ret < 0) + goto fail; - s->start = start; - s->end = end; + av_audio_fifo_drain(s->fifo, s->hop_size); - if (start >= window_size) { - float *dst, *buf; +fail: + av_frame_free(&in); + return ret < 0 ? ret : 0; +} - start -= window_size; - end -= window_size; +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AFFTFiltContext *s = ctx->priv; + AVFrame *in = NULL; + int ret = 0, status; + int64_t pts; - s->start = start; - s->end = end; + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - out = ff_get_audio_buffer(outlink, window_size); - if (!out) { - ret = AVERROR(ENOMEM); - break; - } + if (!s->eof && av_audio_fifo_size(s->fifo) < s->window_size) { + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; - out->pts = s->pts; - s->pts += window_size; + if (ret > 0) { + ret = av_audio_fifo_write(s->fifo, (void **)in->extended_data, + in->nb_samples); + if (ret >= 0 && s->pts == AV_NOPTS_VALUE) + s->pts = in->pts; - for (ch = 0; ch < inlink->channels; ch++) { - dst = (float *)out->extended_data[ch]; - buf = (float *)s->buffer->extended_data[ch]; + av_frame_free(&in); + if (ret < 0) + return ret; + } + } - for (n = 0; n < window_size; n++) { - dst[n] = buf[n] * (1 - s->overlap); - } - memmove(buf, buf + window_size, window_size * 4); - } + if ((av_audio_fifo_size(s->fifo) >= s->window_size) || + (av_audio_fifo_size(s->fifo) > 0 && s->eof)) { + ret = filter_frame(inlink); + if (av_audio_fifo_size(s->fifo) >= s->window_size) + ff_filter_set_ready(ctx, 100); + return ret; + } - ret = ff_filter_frame(outlink, out); - if (ret < 0) - break; + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + s->eof = 1; + if (av_audio_fifo_size(s->fifo) >= 0) { + ff_filter_set_ready(ctx, 100); + return 0; + } } + } - av_audio_fifo_drain(s->fifo, s->hop_size); + if (s->eof && av_audio_fifo_size(s->fifo) <= 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; } - av_frame_free(&in); - return ret < 0 ? ret : 0; + if (!s->eof) + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static int query_formats(AVFilterContext *ctx) @@ -423,7 +437,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_fft_end(s->fft); av_fft_end(s->ifft); - for (i = 0; i < s->nb_exprs; i++) { + for (i = 0; i < s->channels; i++) { if (s->fft_data) av_freep(&s->fft_data[i]); if (s->fft_temp) @@ -450,7 +464,6 @@ static const AVFilterPad inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, { NULL } }; @@ -463,13 +476,14 @@ static const AVFilterPad outputs[] = { { NULL } }; -AVFilter ff_af_afftfilt = { +const AVFilter ff_af_afftfilt = { .name = "afftfilt", .description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to samples in frequency domain."), .priv_size = sizeof(AFFTFiltContext), .priv_class = &afftfilt_class, .inputs = inputs, .outputs = outputs, + .activate = activate, .query_formats = query_formats, .uninit = uninit, };