FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/aeval.c
Date: 2024-07-24 19:24:46
Exec Total Coverage
Lines: 134 176 76.1%
Functions: 10 10 100.0%
Branches: 49 100 49.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2011 Stefano Sabatini
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, 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 /**
22 * @file
23 * eval audio source
24 */
25
26 #include "config_components.h"
27
28 #include "libavutil/avstring.h"
29 #include "libavutil/channel_layout.h"
30 #include "libavutil/eval.h"
31 #include "libavutil/mem.h"
32 #include "libavutil/opt.h"
33 #include "avfilter.h"
34 #include "audio.h"
35 #include "filters.h"
36 #include "formats.h"
37 #include "internal.h"
38
39 static const char * const var_names[] = {
40 "ch", ///< the value of the current channel
41 "n", ///< number of frame
42 "nb_in_channels",
43 "nb_out_channels",
44 "t", ///< timestamp expressed in seconds
45 "s", ///< sample rate
46 NULL
47 };
48
49 enum var_name {
50 VAR_CH,
51 VAR_N,
52 VAR_NB_IN_CHANNELS,
53 VAR_NB_OUT_CHANNELS,
54 VAR_T,
55 VAR_S,
56 VAR_VARS_NB
57 };
58
59 typedef struct EvalContext {
60 const AVClass *class;
61 char *sample_rate_str;
62 int sample_rate;
63 AVChannelLayout chlayout;
64 char *chlayout_str;
65 int nb_channels; ///< number of output channels
66 int nb_in_channels; ///< number of input channels
67 int same_chlayout; ///< set output as input channel layout
68 int64_t pts;
69 AVExpr **expr;
70 char *exprs;
71 int nb_samples; ///< number of samples per requested frame
72 int64_t duration;
73 uint64_t n;
74 double var_values[VAR_VARS_NB];
75 double *channel_values;
76 } EvalContext;
77
78 264600 static double val(void *priv, double ch)
79 {
80 264600 EvalContext *eval = priv;
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 264600 times.
264600 return eval->channel_values[FFMIN((int)ch, eval->nb_in_channels-1)];
82 }
83
84 static double (* const aeval_func1[])(void *, double) = { val, NULL };
85 static const char * const aeval_func1_names[] = { "val", NULL };
86
87 #define OFFSET(x) offsetof(EvalContext, x)
88 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
89
90 static const AVOption aevalsrc_options[]= {
91 { "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
92 { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
93 { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
94 { "sample_rate", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS },
95 { "s", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS },
96 { "duration", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
97 { "d", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
98 { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
99 { "c", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
100 { NULL }
101 };
102
103 AVFILTER_DEFINE_CLASS(aevalsrc);
104
105 17 static int parse_channel_expressions(AVFilterContext *ctx,
106 int expected_nb_channels)
107 {
108 17 EvalContext *eval = ctx->priv;
109 17 char *args1 = av_strdup(eval->exprs);
110 17 char *expr, *last_expr = NULL, *buf;
111 17 double (* const *func1)(void *, double) = NULL;
112 17 const char * const *func1_names = NULL;
113 17 int i, ret = 0;
114
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!args1)
116 return AVERROR(ENOMEM);
117
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!eval->exprs) {
119 av_log(ctx, AV_LOG_ERROR, "Channels expressions list is empty\n");
120 return AVERROR(EINVAL);
121 }
122
123
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if (!strcmp(ctx->filter->name, "aeval")) {
124 2 func1 = aeval_func1;
125 2 func1_names = aeval_func1_names;
126 }
127
128 #define ADD_EXPRESSION(expr_) do { \
129 ret = av_dynarray_add_nofree(&eval->expr, \
130 &eval->nb_channels, NULL); \
131 if (ret < 0) \
132 goto end; \
133 eval->expr[eval->nb_channels-1] = NULL; \
134 ret = av_expr_parse(&eval->expr[eval->nb_channels - 1], expr_, \
135 var_names, func1_names, func1, \
136 NULL, NULL, 0, ctx); \
137 if (ret < 0) \
138 goto end; \
139 } while (0)
140
141 /* reset expressions */
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 for (i = 0; i < eval->nb_channels; i++) {
143 av_expr_free(eval->expr[i]);
144 eval->expr[i] = NULL;
145 }
146 17 av_freep(&eval->expr);
147 17 eval->nb_channels = 0;
148
149 17 buf = args1;
150
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 17 times.
34 while (expr = av_strtok(buf, "|", &buf)) {
151
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 17 times.
17 ADD_EXPRESSION(expr);
152 17 last_expr = expr;
153 }
154
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (expected_nb_channels > eval->nb_channels)
156 for (i = eval->nb_channels; i < expected_nb_channels; i++)
157 ADD_EXPRESSION(last_expr);
158
159
1/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17 if (expected_nb_channels > 0 && eval->nb_channels != expected_nb_channels) {
160 av_log(ctx, AV_LOG_ERROR,
161 "Mismatch between the specified number of channel expressions '%d' "
162 "and the number of expected output channels '%d' for the specified channel layout\n",
163 eval->nb_channels, expected_nb_channels);
164 ret = AVERROR(EINVAL);
165 goto end;
166 }
167
168 17 end:
169 17 av_free(args1);
170 17 return ret;
171 }
172
173 17 static av_cold int init(AVFilterContext *ctx)
174 {
175 17 EvalContext *eval = ctx->priv;
176 17 int ret = 0;
177
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (eval->chlayout_str) {
179 if (!strcmp(eval->chlayout_str, "same") && !strcmp(ctx->filter->name, "aeval")) {
180 eval->same_chlayout = 1;
181 } else {
182 ret = ff_parse_channel_layout(&eval->chlayout, NULL, eval->chlayout_str, ctx);
183 if (ret < 0)
184 return ret;
185
186 ret = parse_channel_expressions(ctx, eval->chlayout.nb_channels);
187 if (ret < 0)
188 return ret;
189 }
190 } else {
191 /* guess channel layout from nb expressions/channels */
192
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if ((ret = parse_channel_expressions(ctx, -1)) < 0)
193 return ret;
194
195 17 av_channel_layout_default(&eval->chlayout, eval->nb_channels);
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (eval->nb_channels <= 0) {
197 av_log(ctx, AV_LOG_ERROR, "Invalid number of channels '%d' provided\n",
198 eval->nb_channels);
199 return AVERROR(EINVAL);
200 }
201 }
202
203
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
17 if (eval->sample_rate_str)
204
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if ((ret = ff_parse_sample_rate(&eval->sample_rate, eval->sample_rate_str, ctx)))
205 return ret;
206 17 eval->n = 0;
207
208 17 return ret;
209 }
210
211 17 static av_cold void uninit(AVFilterContext *ctx)
212 {
213 17 EvalContext *eval = ctx->priv;
214 int i;
215
216
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 for (i = 0; i < eval->nb_channels; i++) {
217 17 av_expr_free(eval->expr[i]);
218 17 eval->expr[i] = NULL;
219 }
220 17 av_freep(&eval->expr);
221 17 av_freep(&eval->channel_values);
222 17 av_channel_layout_uninit(&eval->chlayout);
223 17 }
224
225 14 static int config_props(AVFilterLink *outlink)
226 {
227 14 EvalContext *eval = outlink->src->priv;
228 char buf[128];
229
230 14 outlink->time_base = (AVRational){1, eval->sample_rate};
231 14 outlink->sample_rate = eval->sample_rate;
232
233 14 eval->var_values[VAR_S] = eval->sample_rate;
234 14 eval->var_values[VAR_NB_IN_CHANNELS] = NAN;
235 14 eval->var_values[VAR_NB_OUT_CHANNELS] = outlink->ch_layout.nb_channels;
236
237 14 av_channel_layout_describe(&eval->chlayout, buf, sizeof(buf));
238
239 14 av_log(outlink->src, AV_LOG_VERBOSE,
240 "sample_rate:%d chlayout:%s duration:%"PRId64"\n",
241 eval->sample_rate, buf, eval->duration);
242
243 14 return 0;
244 }
245
246 14 static int query_formats(AVFilterContext *ctx)
247 {
248 14 EvalContext *eval = ctx->priv;
249 static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE };
250
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 AVChannelLayout chlayouts[] = { eval->chlayout.nb_channels ? eval->chlayout : FF_COUNT2LAYOUT(eval->nb_channels), { 0 } };
251 14 int sample_rates[] = { eval->sample_rate, -1 };
252 int ret;
253
254 14 ret = ff_set_common_formats_from_list(ctx, sample_fmts);
255
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ret < 0)
256 return ret;
257
258 14 ret = ff_set_common_channel_layouts_from_list(ctx, chlayouts);
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ret < 0)
260 return ret;
261
262 14 return ff_set_common_samplerates_from_list(ctx, sample_rates);
263 }
264
265 8527 static int activate(AVFilterContext *ctx)
266 {
267 8527 AVFilterLink *outlink = ctx->outputs[0];
268 8527 EvalContext *eval = outlink->src->priv;
269 AVFrame *samplesref;
270 int i, j;
271 8527 int64_t t = av_rescale(eval->n, AV_TIME_BASE, eval->sample_rate);
272 int nb_samples;
273
274
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8527 times.
8527 if (!ff_outlink_frame_wanted(outlink))
275 return FFERROR_NOT_READY;
276
277
3/4
✓ Branch 0 taken 8527 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 8513 times.
8527 if (eval->duration >= 0 && t >= eval->duration) {
278 14 ff_outlink_set_status(outlink, AVERROR_EOF, eval->pts);
279 14 return 0;
280 }
281
282
1/2
✓ Branch 0 taken 8513 times.
✗ Branch 1 not taken.
8513 if (eval->duration >= 0) {
283 8513 nb_samples = FFMIN(eval->nb_samples, av_rescale(eval->duration, eval->sample_rate, AV_TIME_BASE) - eval->pts);
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8513 times.
8513 if (!nb_samples) {
285 ff_outlink_set_status(outlink, AVERROR_EOF, eval->pts);
286 return 0;
287 }
288 } else {
289 nb_samples = eval->nb_samples;
290 }
291 8513 samplesref = ff_get_audio_buffer(outlink, nb_samples);
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8513 times.
8513 if (!samplesref)
293 return AVERROR(ENOMEM);
294
295 /* evaluate expression for each single sample and for each channel */
296
2/2
✓ Branch 0 taken 9094523 times.
✓ Branch 1 taken 8513 times.
9103036 for (i = 0; i < nb_samples; i++, eval->n++) {
297 9094523 eval->var_values[VAR_N] = eval->n;
298 9094523 eval->var_values[VAR_T] = eval->var_values[VAR_N] * (double)1/eval->sample_rate;
299
300
2/2
✓ Branch 0 taken 9094523 times.
✓ Branch 1 taken 9094523 times.
18189046 for (j = 0; j < eval->nb_channels; j++) {
301 9094523 *((double *) samplesref->extended_data[j] + i) =
302 9094523 av_expr_eval(eval->expr[j], eval->var_values, NULL);
303 }
304 }
305
306 8513 samplesref->pts = eval->pts;
307 8513 samplesref->sample_rate = eval->sample_rate;
308 8513 eval->pts += nb_samples;
309
310 8513 return ff_filter_frame(outlink, samplesref);
311 }
312
313 #if CONFIG_AEVALSRC_FILTER
314 static const AVFilterPad aevalsrc_outputs[] = {
315 {
316 .name = "default",
317 .type = AVMEDIA_TYPE_AUDIO,
318 .config_props = config_props,
319 },
320 };
321
322 const AVFilter ff_asrc_aevalsrc = {
323 .name = "aevalsrc",
324 .description = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
325 .init = init,
326 .uninit = uninit,
327 .activate = activate,
328 .priv_size = sizeof(EvalContext),
329 .inputs = NULL,
330 FILTER_OUTPUTS(aevalsrc_outputs),
331 FILTER_QUERY_FUNC(query_formats),
332 .priv_class = &aevalsrc_class,
333 };
334
335 #endif /* CONFIG_AEVALSRC_FILTER */
336
337 #define OFFSET(x) offsetof(EvalContext, x)
338 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
339
340 static const AVOption aeval_options[]= {
341 { "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
342 { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
343 { "c", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
344 { NULL }
345 };
346
347 AVFILTER_DEFINE_CLASS(aeval);
348
349 1 static int aeval_query_formats(AVFilterContext *ctx)
350 {
351 AVFilterChannelLayouts *layouts;
352 1 AVFilterLink *inlink = ctx->inputs[0];
353 1 AVFilterLink *outlink = ctx->outputs[0];
354 1 EvalContext *eval = ctx->priv;
355 static const enum AVSampleFormat sample_fmts[] = {
356 AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE
357 };
358 int ret;
359
360 // inlink supports any channel layout
361 1 layouts = ff_all_channel_counts();
362
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = ff_channel_layouts_ref(layouts, &inlink->outcfg.channel_layouts)) < 0)
363 return ret;
364
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (eval->same_chlayout) {
366 if ((ret = ff_set_common_all_channel_counts(ctx)) < 0)
367 return ret;
368 } else {
369 // outlink supports only requested output channel layout
370 1 layouts = NULL;
371
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = ff_add_channel_layout(&layouts, &FF_COUNT2LAYOUT(eval->nb_channels))) < 0)
372 return ret;
373
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = ff_channel_layouts_ref(layouts, &outlink->incfg.channel_layouts)) < 0)
374 return ret;
375 }
376
377
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts)) < 0)
378 return ret;
379
380 1 return ff_set_common_all_samplerates(ctx);
381 }
382
383 1 static int aeval_config_output(AVFilterLink *outlink)
384 {
385 1 AVFilterContext *ctx = outlink->src;
386 1 EvalContext *eval = ctx->priv;
387 1 AVFilterLink *inlink = ctx->inputs[0];
388 int ret;
389
390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (eval->same_chlayout) {
391 if ((ret = av_channel_layout_copy(&eval->chlayout, &inlink->ch_layout)) < 0)
392 return ret;
393
394 if ((ret = parse_channel_expressions(ctx, inlink->ch_layout.nb_channels)) < 0)
395 return ret;
396 }
397
398 1 eval->n = 0;
399 1 eval->nb_in_channels = eval->var_values[VAR_NB_IN_CHANNELS] = inlink->ch_layout.nb_channels;
400 1 eval->var_values[VAR_NB_OUT_CHANNELS] = outlink->ch_layout.nb_channels;
401 1 eval->var_values[VAR_S] = inlink->sample_rate;
402 1 eval->var_values[VAR_T] = NAN;
403
404 2 eval->channel_values = av_realloc_f(eval->channel_values,
405 1 inlink->ch_layout.nb_channels, sizeof(*eval->channel_values));
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!eval->channel_values)
407 return AVERROR(ENOMEM);
408
409 1 return 0;
410 }
411
412 65 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
413 {
414 65 EvalContext *eval = inlink->dst->priv;
415 65 AVFilterLink *outlink = inlink->dst->outputs[0];
416 65 int nb_samples = in->nb_samples;
417 AVFrame *out;
418 double t0;
419 int i, j;
420
421 65 out = ff_get_audio_buffer(outlink, nb_samples);
422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (!out) {
423 av_frame_free(&in);
424 return AVERROR(ENOMEM);
425 }
426 65 av_frame_copy_props(out, in);
427
428
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
65 t0 = TS2T(in->pts, inlink->time_base);
429
430 /* evaluate expression for each single sample and for each channel */
431
2/2
✓ Branch 0 taken 264600 times.
✓ Branch 1 taken 65 times.
264665 for (i = 0; i < nb_samples; i++, eval->n++) {
432 264600 eval->var_values[VAR_N] = eval->n;
433 264600 eval->var_values[VAR_T] = t0 + i * (double)1/inlink->sample_rate;
434
435
2/2
✓ Branch 0 taken 264600 times.
✓ Branch 1 taken 264600 times.
529200 for (j = 0; j < inlink->ch_layout.nb_channels; j++)
436 264600 eval->channel_values[j] = *((double *) in->extended_data[j] + i);
437
438
2/2
✓ Branch 0 taken 264600 times.
✓ Branch 1 taken 264600 times.
529200 for (j = 0; j < outlink->ch_layout.nb_channels; j++) {
439 264600 eval->var_values[VAR_CH] = j;
440 264600 *((double *) out->extended_data[j] + i) =
441 264600 av_expr_eval(eval->expr[j], eval->var_values, eval);
442 }
443 }
444
445 65 av_frame_free(&in);
446 65 return ff_filter_frame(outlink, out);
447 }
448
449 #if CONFIG_AEVAL_FILTER
450
451 static const AVFilterPad aeval_inputs[] = {
452 {
453 .name = "default",
454 .type = AVMEDIA_TYPE_AUDIO,
455 .filter_frame = filter_frame,
456 },
457 };
458
459 static const AVFilterPad aeval_outputs[] = {
460 {
461 .name = "default",
462 .type = AVMEDIA_TYPE_AUDIO,
463 .config_props = aeval_config_output,
464 },
465 };
466
467 const AVFilter ff_af_aeval = {
468 .name = "aeval",
469 .description = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
470 .init = init,
471 .uninit = uninit,
472 .priv_size = sizeof(EvalContext),
473 FILTER_INPUTS(aeval_inputs),
474 FILTER_OUTPUTS(aeval_outputs),
475 FILTER_QUERY_FUNC(aeval_query_formats),
476 .priv_class = &aeval_class,
477 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
478 };
479
480 #endif /* CONFIG_AEVAL_FILTER */
481