FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_acrossover.c
Date: 2024-03-29 11:55:30
Exec Total Coverage
Lines: 0 253 0.0%
Functions: 0 16 0.0%
Branches: 0 159 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 * @file
21 * Crossover filter
22 *
23 * Split an audio stream into several bands.
24 */
25
26 #include "libavutil/attributes.h"
27 #include "libavutil/avstring.h"
28 #include "libavutil/channel_layout.h"
29 #include "libavutil/eval.h"
30 #include "libavutil/float_dsp.h"
31 #include "libavutil/internal.h"
32 #include "libavutil/opt.h"
33
34 #include "audio.h"
35 #include "avfilter.h"
36 #include "filters.h"
37 #include "formats.h"
38 #include "internal.h"
39
40 #define MAX_SPLITS 16
41 #define MAX_BANDS MAX_SPLITS + 1
42
43 #define B0 0
44 #define B1 1
45 #define B2 2
46 #define A1 3
47 #define A2 4
48
49 typedef struct BiquadCoeffs {
50 double cd[5];
51 float cf[5];
52 } BiquadCoeffs;
53
54 typedef struct AudioCrossoverContext {
55 const AVClass *class;
56
57 char *splits_str;
58 char *gains_str;
59 int order_opt;
60 float level_in;
61 int precision;
62
63 int order;
64 int filter_count;
65 int first_order;
66 int ap_filter_count;
67 int nb_splits;
68 float splits[MAX_SPLITS];
69
70 float gains[MAX_BANDS];
71
72 BiquadCoeffs lp[MAX_BANDS][20];
73 BiquadCoeffs hp[MAX_BANDS][20];
74 BiquadCoeffs ap[MAX_BANDS][20];
75
76 AVFrame *xover;
77
78 AVFrame *frames[MAX_BANDS];
79
80 int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
81
82 AVFloatDSPContext *fdsp;
83 } AudioCrossoverContext;
84
85 #define OFFSET(x) offsetof(AudioCrossoverContext, x)
86 #define AF AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
87
88 static const AVOption acrossover_options[] = {
89 { "split", "set split frequencies", OFFSET(splits_str), AV_OPT_TYPE_STRING, {.str="500"}, 0, 0, AF },
90 { "order", "set filter order", OFFSET(order_opt), AV_OPT_TYPE_INT, {.i64=1}, 0, 9, AF, .unit = "m" },
91 { "2nd", "2nd order (12 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" },
92 { "4th", "4th order (24 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" },
93 { "6th", "6th order (36 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "m" },
94 { "8th", "8th order (48 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, .unit = "m" },
95 { "10th", "10th order (60 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, .unit = "m" },
96 { "12th", "12th order (72 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, AF, .unit = "m" },
97 { "14th", "14th order (84 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, AF, .unit = "m" },
98 { "16th", "16th order (96 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, AF, .unit = "m" },
99 { "18th", "18th order (108 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, AF, .unit = "m" },
100 { "20th", "20th order (120 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, AF, .unit = "m" },
101 { "level", "set input gain", OFFSET(level_in), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, AF },
102 { "gain", "set output bands gain", OFFSET(gains_str), AV_OPT_TYPE_STRING, {.str="1.f"}, 0, 0, AF },
103 { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, .unit = "precision" },
104 { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision" },
105 { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision" },
106 { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision" },
107 { NULL }
108 };
109
110 AVFILTER_DEFINE_CLASS(acrossover);
111
112 static int query_formats(AVFilterContext *ctx)
113 {
114 AudioCrossoverContext *s = ctx->priv;
115 static const enum AVSampleFormat auto_sample_fmts[] = {
116 AV_SAMPLE_FMT_FLTP,
117 AV_SAMPLE_FMT_DBLP,
118 AV_SAMPLE_FMT_NONE
119 };
120 enum AVSampleFormat sample_fmts[] = {
121 AV_SAMPLE_FMT_FLTP,
122 AV_SAMPLE_FMT_NONE
123 };
124 const enum AVSampleFormat *sample_fmts_list = sample_fmts;
125 int ret = ff_set_common_all_channel_counts(ctx);
126 if (ret < 0)
127 return ret;
128
129 switch (s->precision) {
130 case 0:
131 sample_fmts_list = auto_sample_fmts;
132 break;
133 case 1:
134 sample_fmts[0] = AV_SAMPLE_FMT_FLTP;
135 break;
136 case 2:
137 sample_fmts[0] = AV_SAMPLE_FMT_DBLP;
138 break;
139 default:
140 break;
141 }
142 ret = ff_set_common_formats_from_list(ctx, sample_fmts_list);
143 if (ret < 0)
144 return ret;
145
146 return ff_set_common_all_samplerates(ctx);
147 }
148
149 static int parse_gains(AVFilterContext *ctx)
150 {
151 AudioCrossoverContext *s = ctx->priv;
152 char *p, *arg, *saveptr = NULL;
153 int i, ret = 0;
154
155 saveptr = NULL;
156 p = s->gains_str;
157 for (i = 0; i < MAX_BANDS; i++) {
158 float gain;
159 char c[3] = { 0 };
160
161 if (!(arg = av_strtok(p, " |", &saveptr)))
162 break;
163
164 p = NULL;
165
166 if (av_sscanf(arg, "%f%2s", &gain, c) < 1) {
167 av_log(ctx, AV_LOG_ERROR, "Invalid syntax for gain[%d].\n", i);
168 ret = AVERROR(EINVAL);
169 break;
170 }
171
172 if (c[0] == 'd' && c[1] == 'B')
173 s->gains[i] = expf(gain * M_LN10 / 20.f);
174 else
175 s->gains[i] = gain;
176 }
177
178 for (; i < MAX_BANDS; i++)
179 s->gains[i] = 1.f;
180
181 return ret;
182 }
183
184 static av_cold int init(AVFilterContext *ctx)
185 {
186 AudioCrossoverContext *s = ctx->priv;
187 char *p, *arg, *saveptr = NULL;
188 int i, ret = 0;
189
190 s->fdsp = avpriv_float_dsp_alloc(0);
191 if (!s->fdsp)
192 return AVERROR(ENOMEM);
193
194 p = s->splits_str;
195 for (i = 0; i < MAX_SPLITS; i++) {
196 float freq;
197
198 if (!(arg = av_strtok(p, " |", &saveptr)))
199 break;
200
201 p = NULL;
202
203 if (av_sscanf(arg, "%f", &freq) != 1) {
204 av_log(ctx, AV_LOG_ERROR, "Invalid syntax for frequency[%d].\n", i);
205 return AVERROR(EINVAL);
206 }
207 if (freq <= 0) {
208 av_log(ctx, AV_LOG_ERROR, "Frequency %f must be positive number.\n", freq);
209 return AVERROR(EINVAL);
210 }
211
212 if (i > 0 && freq <= s->splits[i-1]) {
213 av_log(ctx, AV_LOG_ERROR, "Frequency %f must be in increasing order.\n", freq);
214 return AVERROR(EINVAL);
215 }
216
217 s->splits[i] = freq;
218 }
219
220 s->nb_splits = i;
221
222 ret = parse_gains(ctx);
223 if (ret < 0)
224 return ret;
225
226 for (i = 0; i <= s->nb_splits; i++) {
227 AVFilterPad pad = { 0 };
228 char *name;
229
230 pad.type = AVMEDIA_TYPE_AUDIO;
231 name = av_asprintf("out%d", ctx->nb_outputs);
232 if (!name)
233 return AVERROR(ENOMEM);
234 pad.name = name;
235
236 if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0)
237 return ret;
238 }
239
240 return ret;
241 }
242
243 static void set_lp(BiquadCoeffs *b, double fc, double q, double sr)
244 {
245 double omega = 2. * M_PI * fc / sr;
246 double cosine = cos(omega);
247 double alpha = sin(omega) / (2. * q);
248
249 double b0 = (1. - cosine) / 2.;
250 double b1 = 1. - cosine;
251 double b2 = (1. - cosine) / 2.;
252 double a0 = 1. + alpha;
253 double a1 = -2. * cosine;
254 double a2 = 1. - alpha;
255
256 b->cd[B0] = b0 / a0;
257 b->cd[B1] = b1 / a0;
258 b->cd[B2] = b2 / a0;
259 b->cd[A1] = -a1 / a0;
260 b->cd[A2] = -a2 / a0;
261
262 b->cf[B0] = b->cd[B0];
263 b->cf[B1] = b->cd[B1];
264 b->cf[B2] = b->cd[B2];
265 b->cf[A1] = b->cd[A1];
266 b->cf[A2] = b->cd[A2];
267 }
268
269 static void set_hp(BiquadCoeffs *b, double fc, double q, double sr)
270 {
271 double omega = 2. * M_PI * fc / sr;
272 double cosine = cos(omega);
273 double alpha = sin(omega) / (2. * q);
274
275 double b0 = (1. + cosine) / 2.;
276 double b1 = -1. - cosine;
277 double b2 = (1. + cosine) / 2.;
278 double a0 = 1. + alpha;
279 double a1 = -2. * cosine;
280 double a2 = 1. - alpha;
281
282 b->cd[B0] = b0 / a0;
283 b->cd[B1] = b1 / a0;
284 b->cd[B2] = b2 / a0;
285 b->cd[A1] = -a1 / a0;
286 b->cd[A2] = -a2 / a0;
287
288 b->cf[B0] = b->cd[B0];
289 b->cf[B1] = b->cd[B1];
290 b->cf[B2] = b->cd[B2];
291 b->cf[A1] = b->cd[A1];
292 b->cf[A2] = b->cd[A2];
293 }
294
295 static void set_ap(BiquadCoeffs *b, double fc, double q, double sr)
296 {
297 double omega = 2. * M_PI * fc / sr;
298 double cosine = cos(omega);
299 double alpha = sin(omega) / (2. * q);
300
301 double a0 = 1. + alpha;
302 double a1 = -2. * cosine;
303 double a2 = 1. - alpha;
304 double b0 = a2;
305 double b1 = a1;
306 double b2 = a0;
307
308 b->cd[B0] = b0 / a0;
309 b->cd[B1] = b1 / a0;
310 b->cd[B2] = b2 / a0;
311 b->cd[A1] = -a1 / a0;
312 b->cd[A2] = -a2 / a0;
313
314 b->cf[B0] = b->cd[B0];
315 b->cf[B1] = b->cd[B1];
316 b->cf[B2] = b->cd[B2];
317 b->cf[A1] = b->cd[A1];
318 b->cf[A2] = b->cd[A2];
319 }
320
321 static void set_ap1(BiquadCoeffs *b, double fc, double sr)
322 {
323 double omega = 2. * M_PI * fc / sr;
324
325 b->cd[A1] = exp(-omega);
326 b->cd[A2] = 0.;
327 b->cd[B0] = -b->cd[A1];
328 b->cd[B1] = 1.;
329 b->cd[B2] = 0.;
330
331 b->cf[B0] = b->cd[B0];
332 b->cf[B1] = b->cd[B1];
333 b->cf[B2] = b->cd[B2];
334 b->cf[A1] = b->cd[A1];
335 b->cf[A2] = b->cd[A2];
336 }
337
338 static void calc_q_factors(int order, double *q)
339 {
340 double n = order / 2.;
341
342 for (int i = 0; i < n / 2; i++)
343 q[i] = 1. / (-2. * cos(M_PI * (2. * (i + 1) + n - 1.) / (2. * n)));
344 }
345
346 #define BIQUAD_PROCESS(name, type) \
347 static void biquad_process_## name(const type *const c, \
348 type *b, \
349 type *dst, const type *src, \
350 int nb_samples) \
351 { \
352 const type b0 = c[B0]; \
353 const type b1 = c[B1]; \
354 const type b2 = c[B2]; \
355 const type a1 = c[A1]; \
356 const type a2 = c[A2]; \
357 type z1 = b[0]; \
358 type z2 = b[1]; \
359 \
360 for (int n = 0; n + 1 < nb_samples; n++) { \
361 type in = src[n]; \
362 type out; \
363 \
364 out = in * b0 + z1; \
365 z1 = b1 * in + z2 + a1 * out; \
366 z2 = b2 * in + a2 * out; \
367 dst[n] = out; \
368 \
369 n++; \
370 in = src[n]; \
371 out = in * b0 + z1; \
372 z1 = b1 * in + z2 + a1 * out; \
373 z2 = b2 * in + a2 * out; \
374 dst[n] = out; \
375 } \
376 \
377 if (nb_samples & 1) { \
378 const int n = nb_samples - 1; \
379 const type in = src[n]; \
380 type out; \
381 \
382 out = in * b0 + z1; \
383 z1 = b1 * in + z2 + a1 * out; \
384 z2 = b2 * in + a2 * out; \
385 dst[n] = out; \
386 } \
387 \
388 b[0] = z1; \
389 b[1] = z2; \
390 }
391
392 BIQUAD_PROCESS(fltp, float)
393 BIQUAD_PROCESS(dblp, double)
394
395 #define XOVER_PROCESS(name, type, one, ff) \
396 static int filter_channels_## name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
397 { \
398 AudioCrossoverContext *s = ctx->priv; \
399 AVFrame *in = arg; \
400 AVFrame **frames = s->frames; \
401 const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; \
402 const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; \
403 const int nb_samples = in->nb_samples; \
404 const int nb_outs = ctx->nb_outputs; \
405 const int first_order = s->first_order; \
406 \
407 for (int ch = start; ch < end; ch++) { \
408 const type *src = (const type *)in->extended_data[ch]; \
409 type *xover = (type *)s->xover->extended_data[ch]; \
410 \
411 s->fdsp->vector_## ff ##mul_scalar((type *)frames[0]->extended_data[ch], src, \
412 s->level_in, FFALIGN(nb_samples, sizeof(type))); \
413 \
414 for (int band = 0; band < nb_outs; band++) { \
415 for (int f = 0; band + 1 < nb_outs && f < s->filter_count; f++) { \
416 const type *prv = (const type *)frames[band]->extended_data[ch]; \
417 type *dst = (type *)frames[band + 1]->extended_data[ch]; \
418 const type *hsrc = f == 0 ? prv : dst; \
419 type *hp = xover + nb_outs * 20 + band * 20 + f * 2; \
420 const type *const hpc = (type *)&s->hp[band][f].c ## ff; \
421 \
422 biquad_process_## name(hpc, hp, dst, hsrc, nb_samples); \
423 } \
424 \
425 for (int f = 0; band + 1 < nb_outs && f < s->filter_count; f++) { \
426 type *dst = (type *)frames[band]->extended_data[ch]; \
427 const type *lsrc = dst; \
428 type *lp = xover + band * 20 + f * 2; \
429 const type *const lpc = (type *)&s->lp[band][f].c ## ff; \
430 \
431 biquad_process_## name(lpc, lp, dst, lsrc, nb_samples); \
432 } \
433 \
434 for (int aband = band + 1; aband + 1 < nb_outs; aband++) { \
435 if (first_order) { \
436 const type *asrc = (const type *)frames[band]->extended_data[ch]; \
437 type *dst = (type *)frames[band]->extended_data[ch]; \
438 type *ap = xover + nb_outs * 40 + (aband * nb_outs + band) * 20; \
439 const type *const apc = (type *)&s->ap[aband][0].c ## ff; \
440 \
441 biquad_process_## name(apc, ap, dst, asrc, nb_samples); \
442 } \
443 \
444 for (int f = first_order; f < s->ap_filter_count; f++) { \
445 const type *asrc = (const type *)frames[band]->extended_data[ch]; \
446 type *dst = (type *)frames[band]->extended_data[ch]; \
447 type *ap = xover + nb_outs * 40 + (aband * nb_outs + band) * 20 + f * 2;\
448 const type *const apc = (type *)&s->ap[aband][f].c ## ff; \
449 \
450 biquad_process_## name(apc, ap, dst, asrc, nb_samples); \
451 } \
452 } \
453 } \
454 \
455 for (int band = 0; band < nb_outs; band++) { \
456 const type gain = s->gains[band] * ((band & 1 && first_order) ? -one : one); \
457 type *dst = (type *)frames[band]->extended_data[ch]; \
458 \
459 s->fdsp->vector_## ff ##mul_scalar(dst, dst, gain, \
460 FFALIGN(nb_samples, sizeof(type))); \
461 } \
462 } \
463 \
464 return 0; \
465 }
466
467 XOVER_PROCESS(fltp, float, 1.f, f)
468 XOVER_PROCESS(dblp, double, 1.0, d)
469
470 static int config_input(AVFilterLink *inlink)
471 {
472 AVFilterContext *ctx = inlink->dst;
473 AudioCrossoverContext *s = ctx->priv;
474 int sample_rate = inlink->sample_rate;
475 double q[16];
476
477 s->order = (s->order_opt + 1) * 2;
478 s->filter_count = s->order / 2;
479 s->first_order = s->filter_count & 1;
480 s->ap_filter_count = s->filter_count / 2 + s->first_order;
481 calc_q_factors(s->order, q);
482
483 for (int band = 0; band <= s->nb_splits; band++) {
484 if (s->first_order) {
485 set_lp(&s->lp[band][0], s->splits[band], 0.5, sample_rate);
486 set_hp(&s->hp[band][0], s->splits[band], 0.5, sample_rate);
487 }
488
489 for (int n = s->first_order; n < s->filter_count; n++) {
490 const int idx = s->filter_count / 2 - ((n + s->first_order) / 2 - s->first_order) - 1;
491
492 set_lp(&s->lp[band][n], s->splits[band], q[idx], sample_rate);
493 set_hp(&s->hp[band][n], s->splits[band], q[idx], sample_rate);
494 }
495
496 if (s->first_order)
497 set_ap1(&s->ap[band][0], s->splits[band], sample_rate);
498
499 for (int n = s->first_order; n < s->ap_filter_count; n++) {
500 const int idx = (s->filter_count / 2 - ((n * 2 + s->first_order) / 2 - s->first_order) - 1);
501
502 set_ap(&s->ap[band][n], s->splits[band], q[idx], sample_rate);
503 }
504 }
505
506 switch (inlink->format) {
507 case AV_SAMPLE_FMT_FLTP: s->filter_channels = filter_channels_fltp; break;
508 case AV_SAMPLE_FMT_DBLP: s->filter_channels = filter_channels_dblp; break;
509 default: return AVERROR_BUG;
510 }
511
512 s->xover = ff_get_audio_buffer(inlink, 2 * (ctx->nb_outputs * 10 + ctx->nb_outputs * 10 +
513 ctx->nb_outputs * ctx->nb_outputs * 10));
514 if (!s->xover)
515 return AVERROR(ENOMEM);
516
517 return 0;
518 }
519
520 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
521 {
522 AVFilterContext *ctx = inlink->dst;
523 AudioCrossoverContext *s = ctx->priv;
524 AVFrame **frames = s->frames;
525 int ret = 0;
526
527 for (int i = 0; i < ctx->nb_outputs; i++) {
528 frames[i] = ff_get_audio_buffer(ctx->outputs[i], in->nb_samples);
529 if (!frames[i]) {
530 ret = AVERROR(ENOMEM);
531 break;
532 }
533
534 frames[i]->pts = in->pts;
535 }
536
537 if (ret < 0)
538 goto fail;
539
540 ff_filter_execute(ctx, s->filter_channels, in, NULL,
541 FFMIN(inlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx)));
542
543 for (int i = 0; i < ctx->nb_outputs; i++) {
544 if (ff_outlink_get_status(ctx->outputs[i])) {
545 av_frame_free(&frames[i]);
546 continue;
547 }
548
549 ret = ff_filter_frame(ctx->outputs[i], frames[i]);
550 frames[i] = NULL;
551 if (ret < 0)
552 break;
553 }
554
555 fail:
556 for (int i = 0; i < ctx->nb_outputs; i++)
557 av_frame_free(&frames[i]);
558
559 return ret;
560 }
561
562 static int activate(AVFilterContext *ctx)
563 {
564 AVFilterLink *inlink = ctx->inputs[0];
565 int status, ret;
566 AVFrame *in;
567 int64_t pts;
568
569 for (int i = 0; i < ctx->nb_outputs; i++) {
570 FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
571 }
572
573 ret = ff_inlink_consume_frame(inlink, &in);
574 if (ret < 0)
575 return ret;
576 if (ret > 0) {
577 ret = filter_frame(inlink, in);
578 av_frame_free(&in);
579 if (ret < 0)
580 return ret;
581 }
582
583 if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
584 for (int i = 0; i < ctx->nb_outputs; i++) {
585 if (ff_outlink_get_status(ctx->outputs[i]))
586 continue;
587 ff_outlink_set_status(ctx->outputs[i], status, pts);
588 }
589 return 0;
590 }
591
592 for (int i = 0; i < ctx->nb_outputs; i++) {
593 if (ff_outlink_get_status(ctx->outputs[i]))
594 continue;
595
596 if (ff_outlink_frame_wanted(ctx->outputs[i])) {
597 ff_inlink_request_frame(inlink);
598 return 0;
599 }
600 }
601
602 return FFERROR_NOT_READY;
603 }
604
605 static av_cold void uninit(AVFilterContext *ctx)
606 {
607 AudioCrossoverContext *s = ctx->priv;
608
609 av_freep(&s->fdsp);
610 av_frame_free(&s->xover);
611 }
612
613 static const AVFilterPad inputs[] = {
614 {
615 .name = "default",
616 .type = AVMEDIA_TYPE_AUDIO,
617 .config_props = config_input,
618 },
619 };
620
621 const AVFilter ff_af_acrossover = {
622 .name = "acrossover",
623 .description = NULL_IF_CONFIG_SMALL("Split audio into per-bands streams."),
624 .priv_size = sizeof(AudioCrossoverContext),
625 .priv_class = &acrossover_class,
626 .init = init,
627 .activate = activate,
628 .uninit = uninit,
629 FILTER_INPUTS(inputs),
630 .outputs = NULL,
631 FILTER_QUERY_FUNC(query_formats),
632 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS |
633 AVFILTER_FLAG_SLICE_THREADS,
634 };
635