FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/graphparser.c
Date: 2025-01-20 09:27:23
Exec Total Coverage
Lines: 347 586 59.2%
Functions: 23 28 82.1%
Branches: 198 362 54.7%

Line Branch Exec Source
1 /*
2 * filter graph parser
3 * Copyright (c) 2008 Vitor Sessak
4 * Copyright (c) 2007 Bobby Bingham
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <string.h>
24 #include <stdio.h>
25
26 #include "libavutil/avstring.h"
27 #include "libavutil/dict.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/opt.h"
30
31 #include "avfilter.h"
32 #include "avfilter_internal.h"
33 #include "filters.h"
34
35 #define WHITESPACES " \n\t\r"
36
37 /**
38 * Parse the name of a link, which has the format "[linkname]".
39 *
40 * @return a pointer (that need to be freed after use) to the name
41 * between parenthesis
42 */
43 1047 static char *parse_link_name(const char **buf, void *log_ctx)
44 {
45 1047 const char *start = *buf;
46 char *name;
47 1047 (*buf)++;
48
49 1047 name = av_get_token(buf, "]");
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (!name)
51 return NULL;
52
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (!name[0]) {
54 av_log(log_ctx, AV_LOG_ERROR,
55 "Bad (empty?) label found in the following: \"%s\".\n", start);
56 goto fail;
57 }
58
59
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (**buf != ']') {
60 av_log(log_ctx, AV_LOG_ERROR,
61 "Mismatched '[' found in the following: \"%s\".\n", start);
62 fail:
63 av_freep(&name);
64 return NULL;
65 }
66 1047 (*buf)++;
67
68 1047 return name;
69 }
70
71 AVFilterInOut *avfilter_inout_alloc(void)
72 {
73 return av_mallocz(sizeof(AVFilterInOut));
74 }
75
76 30428 void avfilter_inout_free(AVFilterInOut **inout)
77 {
78
2/2
✓ Branch 0 taken 29051 times.
✓ Branch 1 taken 30428 times.
59479 while (*inout) {
79 29051 AVFilterInOut *next = (*inout)->next;
80 29051 av_freep(&(*inout)->name);
81 29051 av_freep(inout);
82 29051 *inout = next;
83 }
84 30428 }
85
86 45 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
87 {
88 AVFilterInOut *ret;
89
90
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
45 while (*links && (!(*links)->name || strcmp((*links)->name, label)))
91 links = &((*links)->next);
92
93 45 ret = *links;
94
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (ret) {
96 *links = ret->next;
97 ret->next = NULL;
98 }
99
100 45 return ret;
101 }
102
103 29096 static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
104 {
105
4/4
✓ Branch 0 taken 5142 times.
✓ Branch 1 taken 28662 times.
✓ Branch 2 taken 4708 times.
✓ Branch 3 taken 434 times.
33804 while (*inouts && (*inouts)->next)
106 4708 inouts = &((*inouts)->next);
107
108
2/2
✓ Branch 0 taken 28662 times.
✓ Branch 1 taken 434 times.
29096 if (!*inouts)
109 28662 *inouts = *element;
110 else
111 434 (*inouts)->next = *element;
112 29096 *element = NULL;
113 29096 }
114
115 15171 static int parse_sws_flags(const char **buf, char **dst, void *log_ctx)
116 {
117 15171 char *p = strchr(*buf, ';');
118
119
2/2
✓ Branch 0 taken 15122 times.
✓ Branch 1 taken 49 times.
15171 if (strncmp(*buf, "sws_flags=", 10))
120 15122 return 0;
121
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 if (!p) {
123 av_log(log_ctx, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
124 return AVERROR(EINVAL);
125 }
126
127 49 *buf += 4; // keep the 'flags=' part
128
129 49 av_freep(dst);
130
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
49 if (!(*dst = av_mallocz(p - *buf + 1)))
131 return AVERROR(ENOMEM);
132 49 av_strlcpy(*dst, *buf, p - *buf + 1);
133
134 49 *buf = p + 1;
135 49 return 0;
136 }
137
138 int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
139 AVFilterInOut **inputs,
140 AVFilterInOut **outputs)
141 {
142 AVFilterGraphSegment *seg;
143 int ret;
144
145 ret = avfilter_graph_segment_parse(graph, filters, 0, &seg);
146 if (ret < 0)
147 return ret;
148
149 ret = avfilter_graph_segment_apply(seg, 0, inputs, outputs);
150 avfilter_graph_segment_free(&seg);
151 if (ret < 0)
152 goto end;
153
154 return 0;
155
156 end:
157 while (graph->nb_filters)
158 avfilter_free(graph->filters[0]);
159 av_freep(&graph->filters);
160
161 return ret;
162 }
163
164 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
165 AVFilterInOut *open_inputs,
166 AVFilterInOut *open_outputs, void *log_ctx)
167 {
168 int ret;
169 AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL;
170
171 if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0)
172 goto fail;
173
174 /* First input can be omitted if it is "[in]" */
175 if (inputs && !inputs->name)
176 inputs->name = av_strdup("in");
177 for (cur = inputs; cur; cur = cur->next) {
178 if (!cur->name) {
179 av_log(log_ctx, AV_LOG_ERROR,
180 "Not enough inputs specified for the \"%s\" filter.\n",
181 cur->filter_ctx->filter->name);
182 ret = AVERROR(EINVAL);
183 goto fail;
184 }
185 if (!(match = extract_inout(cur->name, &open_outputs)))
186 continue;
187 ret = avfilter_link(match->filter_ctx, match->pad_idx,
188 cur->filter_ctx, cur->pad_idx);
189 avfilter_inout_free(&match);
190 if (ret < 0)
191 goto fail;
192 }
193
194 /* Last output can be omitted if it is "[out]" */
195 if (outputs && !outputs->name)
196 outputs->name = av_strdup("out");
197 for (cur = outputs; cur; cur = cur->next) {
198 if (!cur->name) {
199 av_log(log_ctx, AV_LOG_ERROR,
200 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
201 filters);
202 ret = AVERROR(EINVAL);
203 goto fail;
204 }
205 if (!(match = extract_inout(cur->name, &open_inputs)))
206 continue;
207 ret = avfilter_link(cur->filter_ctx, cur->pad_idx,
208 match->filter_ctx, match->pad_idx);
209 avfilter_inout_free(&match);
210 if (ret < 0)
211 goto fail;
212 }
213
214 fail:
215 if (ret < 0) {
216 while (graph->nb_filters)
217 avfilter_free(graph->filters[0]);
218 av_freep(&graph->filters);
219 }
220 avfilter_inout_free(&inputs);
221 avfilter_inout_free(&outputs);
222 avfilter_inout_free(&open_inputs);
223 avfilter_inout_free(&open_outputs);
224 return ret;
225 }
226
227 1047 static void pad_params_free(AVFilterPadParams **pfpp)
228 {
229 1047 AVFilterPadParams *fpp = *pfpp;
230
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (!fpp)
232 return;
233
234 1047 av_freep(&fpp->label);
235
236 1047 av_freep(pfpp);
237 }
238
239 33428 static void filter_params_free(AVFilterParams **pp)
240 {
241 33428 AVFilterParams *p = *pp;
242
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!p)
244 return;
245
246
2/2
✓ Branch 0 taken 524 times.
✓ Branch 1 taken 33428 times.
33952 for (unsigned i = 0; i < p->nb_inputs; i++)
247 524 pad_params_free(&p->inputs[i]);
248 33428 av_freep(&p->inputs);
249
250
2/2
✓ Branch 0 taken 523 times.
✓ Branch 1 taken 33428 times.
33951 for (unsigned i = 0; i < p->nb_outputs; i++)
251 523 pad_params_free(&p->outputs[i]);
252 33428 av_freep(&p->outputs);
253
254 33428 av_dict_free(&p->opts);
255
256 33428 av_freep(&p->filter_name);
257 33428 av_freep(&p->instance_name);
258
259 33428 av_freep(pp);
260 }
261
262 15581 static void chain_free(AVFilterChain **pch)
263 {
264 15581 AVFilterChain *ch = *pch;
265
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15581 times.
15581 if (!ch)
267 return;
268
269
2/2
✓ Branch 0 taken 33428 times.
✓ Branch 1 taken 15581 times.
49009 for (size_t i = 0; i < ch->nb_filters; i++)
270 33428 filter_params_free(&ch->filters[i]);
271 15581 av_freep(&ch->filters);
272
273 15581 av_freep(pch);
274 }
275
276 15214 void avfilter_graph_segment_free(AVFilterGraphSegment **pseg)
277 {
278 15214 AVFilterGraphSegment *seg = *pseg;
279
280
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 15171 times.
15214 if (!seg)
281 43 return;
282
283
2/2
✓ Branch 0 taken 15581 times.
✓ Branch 1 taken 15171 times.
30752 for (size_t i = 0; i < seg->nb_chains; i++)
284 15581 chain_free(&seg->chains[i]);
285 15171 av_freep(&seg->chains);
286
287 15171 av_freep(&seg->scale_sws_opts);
288
289 15171 av_freep(pseg);
290 }
291
292 66893 static int linklabels_parse(void *logctx, const char **linklabels,
293 AVFilterPadParams ***res, unsigned *nb_res)
294 {
295 66893 AVFilterPadParams **pp = NULL;
296 66893 int nb = 0;
297 int ret;
298
299
2/2
✓ Branch 0 taken 1047 times.
✓ Branch 1 taken 66893 times.
67940 while (**linklabels == '[') {
300 char *label;
301 AVFilterPadParams *par;
302
303 1047 label = parse_link_name(linklabels, logctx);
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (!label) {
305 ret = AVERROR(EINVAL);
306 goto fail;
307 }
308
309 1047 par = av_mallocz(sizeof(*par));
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (!par) {
311 av_freep(&label);
312 ret = AVERROR(ENOMEM);
313 goto fail;
314 }
315
316 1047 par->label = label;
317
318 1047 ret = av_dynarray_add_nofree(&pp, &nb, par);
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
1047 if (ret < 0) {
320 pad_params_free(&par);
321 goto fail;
322 }
323
324 1047 *linklabels += strspn(*linklabels, WHITESPACES);
325 }
326
327 66893 *res = pp;
328 66893 *nb_res = nb;
329
330 66893 return 0;
331 fail:
332 for (unsigned i = 0; i < nb; i++)
333 pad_params_free(&pp[i]);
334 av_freep(&pp);
335 return ret;
336 }
337
338 33428 static int filter_parse(void *logctx, const char **filter,
339 AVFilterParams **pp)
340 {
341 AVFilterParams *p;
342 char *inst_name;
343 int ret;
344
345 33428 p = av_mallocz(sizeof(*p));
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!p)
347 return AVERROR(ENOMEM);
348
349 33428 ret = linklabels_parse(logctx, filter, &p->inputs, &p->nb_inputs);
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
351 goto fail;
352
353 33428 p->filter_name = av_get_token(filter, "=,;[");
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!p->filter_name) {
355 ret = AVERROR(ENOMEM);
356 goto fail;
357 }
358
359 33428 inst_name = strchr(p->filter_name, '@');
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (inst_name) {
361 *inst_name++ = 0;
362 p->instance_name = av_strdup(inst_name);
363 if (!p->instance_name) {
364 ret = AVERROR(ENOMEM);
365 goto fail;
366 }
367 }
368
369
2/2
✓ Branch 0 taken 20483 times.
✓ Branch 1 taken 12945 times.
33428 if (**filter == '=') {
370 20483 const AVFilter *f = avfilter_get_by_name(p->filter_name);
371 char *opts;
372
373 20483 (*filter)++;
374
375 20483 opts = av_get_token(filter, "[],;");
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20483 times.
20483 if (!opts) {
377 ret = AVERROR(ENOMEM);
378 goto fail;
379 }
380
381 20483 ret = ff_filter_opt_parse(logctx, f ? f->priv_class : NULL,
382
1/2
✓ Branch 0 taken 20483 times.
✗ Branch 1 not taken.
20483 &p->opts, opts);
383 20483 av_freep(&opts);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20483 times.
20483 if (ret < 0)
385 goto fail;
386 }
387
388 33428 ret = linklabels_parse(logctx, filter, &p->outputs, &p->nb_outputs);
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
390 goto fail;
391
392 33428 *filter += strspn(*filter, WHITESPACES);
393
394 33428 *pp = p;
395 33428 return 0;
396 fail:
397 av_log(logctx, AV_LOG_ERROR,
398 "Error parsing a filter description around: %s\n", *filter);
399 filter_params_free(&p);
400 return ret;
401 }
402
403 15581 static int chain_parse(void *logctx, const char **pchain,
404 AVFilterChain **pch)
405 {
406 15581 const char *chain = *pchain;
407 AVFilterChain *ch;
408 15581 int ret, nb_filters = 0;
409
410 15581 *pch = NULL;
411
412 15581 ch = av_mallocz(sizeof(*ch));
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15581 times.
15581 if (!ch)
414 return AVERROR(ENOMEM);
415
416
2/2
✓ Branch 0 taken 33428 times.
✓ Branch 1 taken 15153 times.
48581 while (*chain) {
417 AVFilterParams *p;
418 char chr;
419
420 33428 ret = filter_parse(logctx, &chain, &p);
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
422 goto fail;
423
424 33428 ret = av_dynarray_add_nofree(&ch->filters, &nb_filters, p);
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0) {
426 filter_params_free(&p);
427 goto fail;
428 }
429 33428 ch->nb_filters = nb_filters;
430
431 // a filter ends with one of: , ; end-of-string
432 33428 chr = *chain;
433
5/6
✓ Branch 0 taken 18275 times.
✓ Branch 1 taken 15153 times.
✓ Branch 2 taken 428 times.
✓ Branch 3 taken 17847 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 428 times.
33428 if (chr && chr != ',' && chr != ';') {
434 av_log(logctx, AV_LOG_ERROR,
435 "Trailing garbage after a filter: %s\n", chain);
436 ret = AVERROR(EINVAL);
437 goto fail;
438 }
439
440
2/2
✓ Branch 0 taken 18275 times.
✓ Branch 1 taken 15153 times.
33428 if (chr) {
441 18275 chain++;
442 18275 chain += strspn(chain, WHITESPACES);
443
444
2/2
✓ Branch 0 taken 428 times.
✓ Branch 1 taken 17847 times.
18275 if (chr == ';')
445 428 break;
446 }
447 }
448
449 15581 *pchain = chain;
450 15581 *pch = ch;
451
452 15581 return 0;
453 fail:
454 av_log(logctx, AV_LOG_ERROR,
455 "Error parsing filterchain '%s' around: %s\n", *pchain, chain);
456 chain_free(&ch);
457 return ret;
458 }
459
460 15171 int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str,
461 int flags, AVFilterGraphSegment **pseg)
462 {
463 AVFilterGraphSegment *seg;
464 15171 int ret, nb_chains = 0;
465
466 15171 *pseg = NULL;
467
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (flags)
469 return AVERROR(ENOSYS);
470
471 15171 seg = av_mallocz(sizeof(*seg));
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (!seg)
473 return AVERROR(ENOMEM);
474
475 15171 seg->graph = graph;
476
477 15171 graph_str += strspn(graph_str, WHITESPACES);
478
479 15171 ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, graph);
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (ret < 0)
481 goto fail;
482
483 15171 graph_str += strspn(graph_str, WHITESPACES);
484
485
2/2
✓ Branch 0 taken 15581 times.
✓ Branch 1 taken 15171 times.
30752 while (*graph_str) {
486 AVFilterChain *ch;
487
488 15581 ret = chain_parse(graph, &graph_str, &ch);
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15581 times.
15581 if (ret < 0)
490 goto fail;
491
492 15581 ret = av_dynarray_add_nofree(&seg->chains, &nb_chains, ch);
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15581 times.
15581 if (ret < 0) {
494 chain_free(&ch);
495 goto fail;
496 }
497 15581 seg->nb_chains = nb_chains;
498
499 15581 graph_str += strspn(graph_str, WHITESPACES);
500 }
501
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (!seg->nb_chains) {
503 av_log(graph, AV_LOG_ERROR, "No filters specified in the graph description\n");
504 ret = AVERROR(EINVAL);
505 goto fail;
506 }
507
508 15171 *pseg = seg;
509
510 15171 return 0;
511 fail:
512 avfilter_graph_segment_free(&seg);
513 return ret;
514 }
515
516 30342 int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags)
517 {
518 30342 size_t idx = 0;
519
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30342 times.
30342 if (flags)
521 return AVERROR(ENOSYS);
522
523
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 30244 times.
30342 if (seg->scale_sws_opts) {
524 98 av_freep(&seg->graph->scale_sws_opts);
525 98 seg->graph->scale_sws_opts = av_strdup(seg->scale_sws_opts);
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 if (!seg->graph->scale_sws_opts)
527 return AVERROR(ENOMEM);
528 }
529
530
2/2
✓ Branch 0 taken 31162 times.
✓ Branch 1 taken 30342 times.
61504 for (size_t i = 0; i < seg->nb_chains; i++) {
531 31162 AVFilterChain *ch = seg->chains[i];
532
533
2/2
✓ Branch 0 taken 66856 times.
✓ Branch 1 taken 31162 times.
98018 for (size_t j = 0; j < ch->nb_filters; j++) {
534 66856 AVFilterParams *p = ch->filters[j];
535 66856 const AVFilter *f = avfilter_get_by_name(p->filter_name);
536 char name[64];
537
538 // skip already processed filters
539
3/4
✓ Branch 0 taken 33428 times.
✓ Branch 1 taken 33428 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33428 times.
66856 if (p->filter || !p->filter_name)
540 33428 continue;
541
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!f) {
543 av_log(seg->graph, AV_LOG_ERROR,
544 "No such filter: '%s'\n", p->filter_name);
545 return AVERROR_FILTER_NOT_FOUND;
546 }
547
548
1/2
✓ Branch 0 taken 33428 times.
✗ Branch 1 not taken.
33428 if (!p->instance_name)
549 33428 snprintf(name, sizeof(name), "Parsed_%s_%zu", f->name, idx);
550 else
551 snprintf(name, sizeof(name), "%s@%s", f->name, p->instance_name);
552
553 33428 p->filter = avfilter_graph_alloc_filter(seg->graph, f, name);
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!p->filter)
555 return AVERROR(ENOMEM);
556
557
4/4
✓ Branch 0 taken 8187 times.
✓ Branch 1 taken 25241 times.
✓ Branch 2 taken 2985 times.
✓ Branch 3 taken 5202 times.
33428 if (!strcmp(f->name, "scale") && seg->graph->scale_sws_opts) {
558 2985 int ret = av_set_options_string(p->filter, seg->graph->scale_sws_opts,
559 "=", ":");
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2985 times.
2985 if (ret < 0) {
561 avfilter_free(p->filter);
562 p->filter = NULL;
563 return ret;
564 }
565 }
566
567 33428 av_freep(&p->filter_name);
568 33428 av_freep(&p->instance_name);
569
570 33428 idx++;
571 }
572 }
573
574 30342 return 0;
575 }
576
577 static int fail_creation_pending(AVFilterGraphSegment *seg, const char *fn,
578 const char *func)
579 {
580 av_log(seg->graph, AV_LOG_ERROR,
581 "A creation-pending filter '%s' present in the segment. All filters "
582 "must be created or disabled before calling %s().\n", fn, func);
583 return AVERROR(EINVAL);
584 }
585
586 15214 int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags)
587 {
588 15214 int ret, leftover_opts = 0;
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15214 times.
15214 if (flags)
591 return AVERROR(ENOSYS);
592
593
2/2
✓ Branch 0 taken 15628 times.
✓ Branch 1 taken 15214 times.
30842 for (size_t i = 0; i < seg->nb_chains; i++) {
594 15628 AVFilterChain *ch = seg->chains[i];
595
596
2/2
✓ Branch 0 taken 33499 times.
✓ Branch 1 taken 15628 times.
49127 for (size_t j = 0; j < ch->nb_filters; j++) {
597 33499 AVFilterParams *p = ch->filters[j];
598
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33499 times.
33499 if (p->filter_name)
600 return fail_creation_pending(seg, p->filter_name, __func__);
601
3/4
✓ Branch 0 taken 33499 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33434 times.
✓ Branch 3 taken 65 times.
33499 if (!p->filter || !p->opts)
602 33434 continue;
603
604 65 ret = av_opt_set_dict2(p->filter, &p->opts, AV_OPT_SEARCH_CHILDREN);
605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65 times.
65 if (ret < 0)
606 return ret;
607
608
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
65 if (av_dict_count(p->opts))
609 leftover_opts = 1;
610 }
611 }
612
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15214 times.
15214 return leftover_opts ? AVERROR_OPTION_NOT_FOUND : 0;
614 }
615
616 15214 int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags)
617 {
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15214 times.
15214 if (flags)
619 return AVERROR(ENOSYS);
620
621
2/2
✓ Branch 0 taken 15628 times.
✓ Branch 1 taken 15214 times.
30842 for (size_t i = 0; i < seg->nb_chains; i++) {
622 15628 AVFilterChain *ch = seg->chains[i];
623
624
2/2
✓ Branch 0 taken 33499 times.
✓ Branch 1 taken 15628 times.
49127 for (size_t j = 0; j < ch->nb_filters; j++) {
625 33499 AVFilterParams *p = ch->filters[j];
626 int ret;
627
628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33499 times.
33499 if (p->filter_name)
629 return fail_creation_pending(seg, p->filter_name, __func__);
630
1/2
✓ Branch 0 taken 33499 times.
✗ Branch 1 not taken.
33499 if (!p->filter ||
631
2/2
✓ Branch 1 taken 71 times.
✓ Branch 2 taken 33428 times.
33499 (fffilterctx(p->filter)->state_flags & AV_CLASS_STATE_INITIALIZED))
632 71 continue;
633
634 33428 ret = avfilter_init_dict(p->filter, NULL);
635
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
636 return ret;
637 }
638 }
639
640 15214 return 0;
641 }
642
643 static unsigned
644 711 find_linklabel(AVFilterGraphSegment *seg, const char *label,
645 int output, size_t idx_chain, size_t idx_filter,
646 AVFilterParams **pp)
647 {
648
2/2
✓ Branch 0 taken 6253 times.
✓ Branch 1 taken 375 times.
6628 for (; idx_chain < seg->nb_chains; idx_chain++) {
649 6253 AVFilterChain *ch = seg->chains[idx_chain];
650
651
2/2
✓ Branch 0 taken 6317 times.
✓ Branch 1 taken 5917 times.
12234 for (; idx_filter < ch->nb_filters; idx_filter++) {
652 6317 AVFilterParams *p = ch->filters[idx_filter];
653
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 5707 times.
6317 AVFilterPadParams **io = output ? p->outputs : p->inputs;
654
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 5707 times.
6317 unsigned nb_io = output ? p->nb_outputs : p->nb_inputs;
655 AVFilterLink **l;
656 unsigned nb_l;
657
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6317 times.
6317 if (!p->filter)
659 continue;
660
661
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 5707 times.
6317 l = output ? p->filter->outputs : p->filter->inputs;
662
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 5707 times.
6317 nb_l = output ? p->filter->nb_outputs : p->filter->nb_inputs;
663
664
2/2
✓ Branch 0 taken 5945 times.
✓ Branch 1 taken 5981 times.
11926 for (unsigned i = 0; i < FFMIN(nb_io, nb_l); i++)
665
5/6
✓ Branch 0 taken 1530 times.
✓ Branch 1 taken 4415 times.
✓ Branch 2 taken 1530 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 336 times.
✓ Branch 5 taken 1194 times.
5945 if (!l[i] && io[i]->label && !strcmp(io[i]->label, label)) {
666 336 *pp = p;
667 336 return i;
668 }
669 }
670
671 5917 idx_filter = 0;
672 }
673
674 375 *pp = NULL;
675 375 return 0;
676 }
677
678 29051 static int inout_add(AVFilterInOut **inouts, AVFilterContext *f, unsigned pad_idx,
679 const char *label)
680 {
681 29051 AVFilterInOut *io = av_mallocz(sizeof(*io));
682
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29051 times.
29051 if (!io)
684 return AVERROR(ENOMEM);
685
686 29051 io->filter_ctx = f;
687 29051 io->pad_idx = pad_idx;
688
689
2/2
✓ Branch 0 taken 375 times.
✓ Branch 1 taken 28676 times.
29051 if (label) {
690 375 io->name = av_strdup(label);
691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 375 times.
375 if (!io->name) {
692 avfilter_inout_free(&io);
693 return AVERROR(ENOMEM);
694 }
695 }
696
697 29051 append_inout(inouts, &io);
698
699 29051 return 0;
700 }
701
702 33428 static int link_inputs(AVFilterGraphSegment *seg, size_t idx_chain,
703 size_t idx_filter, AVFilterInOut **inputs)
704 {
705 33428 AVFilterChain *ch = seg->chains[idx_chain];
706 33428 AVFilterParams *p = ch->filters[idx_filter];
707 33428 AVFilterContext *f = p->filter;
708
709 int ret;
710
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (f->nb_inputs < p->nb_inputs) {
712 av_log(seg->graph, AV_LOG_ERROR,
713 "More input link labels specified for filter '%s' than "
714 "it has inputs: %u > %d\n", f->filter->name,
715 p->nb_inputs, f->nb_inputs);
716 return AVERROR(EINVAL);
717 }
718
719
2/2
✓ Branch 0 taken 31803 times.
✓ Branch 1 taken 33428 times.
65231 for (unsigned in = 0; in < f->nb_inputs; in++) {
720
2/2
✓ Branch 0 taken 524 times.
✓ Branch 1 taken 31279 times.
31803 const char *label = (in < p->nb_inputs) ? p->inputs[in]->label : NULL;
721
722 // skip already linked inputs
723
2/2
✓ Branch 0 taken 18185 times.
✓ Branch 1 taken 13618 times.
31803 if (f->inputs[in])
724 18185 continue;
725
726
2/2
✓ Branch 0 taken 188 times.
✓ Branch 1 taken 13430 times.
13618 if (label) {
727 188 AVFilterParams *po = NULL;
728 188 unsigned idx = find_linklabel(seg, label, 1, idx_chain, idx_filter, &po);
729
730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 188 times.
188 if (po) {
731 ret = avfilter_link(po->filter, idx, f, in);
732 if (ret < 0)
733 return ret;
734
735 continue;
736 }
737 }
738
739 13618 ret = inout_add(inputs, f, in, label);
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13618 times.
13618 if (ret < 0)
741 return ret;
742 }
743
744 33428 return 0;
745 }
746
747 33428 static int link_outputs(AVFilterGraphSegment *seg, size_t idx_chain,
748 size_t idx_filter, AVFilterInOut **outputs)
749 {
750 33428 AVFilterChain *ch = seg->chains[idx_chain];
751 33428 AVFilterParams *p = ch->filters[idx_filter];
752 33428 AVFilterContext *f = p->filter;
753
754 int ret;
755
756
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (f->nb_outputs < p->nb_outputs) {
757 av_log(seg->graph, AV_LOG_ERROR,
758 "More output link labels specified for filter '%s' than "
759 "it has outputs: %u > %d\n", f->filter->name,
760 p->nb_outputs, f->nb_outputs);
761 return AVERROR(EINVAL);
762 }
763
2/2
✓ Branch 0 taken 33618 times.
✓ Branch 1 taken 33428 times.
67046 for (unsigned out = 0; out < f->nb_outputs; out++) {
764
2/2
✓ Branch 0 taken 523 times.
✓ Branch 1 taken 33095 times.
33618 char *label = (out < p->nb_outputs) ? p->outputs[out]->label : NULL;
765
766 // skip already linked outputs
767
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33618 times.
33618 if (f->outputs[out])
768 continue;
769
770
2/2
✓ Branch 0 taken 523 times.
✓ Branch 1 taken 33095 times.
33618 if (label) {
771 523 AVFilterParams *po = NULL;
772 523 unsigned idx = find_linklabel(seg, label, 0, idx_chain, idx_filter, &po);
773
774
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 187 times.
523 if (po) {
775 336 ret = avfilter_link(f, out, po->filter, idx);
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (ret < 0)
777 return ret;
778
779 336 continue;
780 }
781 }
782
783 // if this output is unlabeled, try linking it to an unlabeled
784 // input in the next non-disabled filter in the chain
785
3/4
✓ Branch 0 taken 17849 times.
✓ Branch 1 taken 15433 times.
✓ Branch 2 taken 17849 times.
✗ Branch 3 not taken.
33282 for (size_t i = idx_filter + 1; i < ch->nb_filters && !label; i++) {
786 17849 AVFilterParams *p_next = ch->filters[i];
787
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17849 times.
17849 if (!p_next->filter)
789 continue;
790
791
1/2
✓ Branch 0 taken 17853 times.
✗ Branch 1 not taken.
17853 for (unsigned in = 0; in < p_next->filter->nb_inputs; in++) {
792
2/2
✓ Branch 0 taken 17849 times.
✓ Branch 1 taken 4 times.
17853 if (!p_next->filter->inputs[in] &&
793
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17849 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17849 (in >= p_next->nb_inputs || !p_next->inputs[in]->label)) {
794 17849 ret = avfilter_link(f, out, p_next->filter, in);
795
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17849 times.
17849 if (ret < 0)
796 return ret;
797
798 17849 goto cont;
799 }
800 }
801 break;
802 }
803
804 15433 ret = inout_add(outputs, f, out, label);
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15433 times.
15433 if (ret < 0)
806 return ret;
807
808 33282 cont:;
809 }
810
811 33428 return 0;
812 }
813
814 15171 int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags,
815 AVFilterInOut **inputs,
816 AVFilterInOut **outputs)
817 {
818 int ret;
819
820 15171 *inputs = NULL;
821 15171 *outputs = NULL;
822
823
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (flags)
824 return AVERROR(ENOSYS);
825
826
2/2
✓ Branch 0 taken 15581 times.
✓ Branch 1 taken 15171 times.
30752 for (size_t idx_chain = 0; idx_chain < seg->nb_chains; idx_chain++) {
827 15581 AVFilterChain *ch = seg->chains[idx_chain];
828
829
2/2
✓ Branch 0 taken 33428 times.
✓ Branch 1 taken 15581 times.
49009 for (size_t idx_filter = 0; idx_filter < ch->nb_filters; idx_filter++) {
830 33428 AVFilterParams *p = ch->filters[idx_filter];
831
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (p->filter_name) {
833 ret = fail_creation_pending(seg, p->filter_name, __func__);
834 goto fail;
835 }
836
837
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (!p->filter)
838 continue;
839
840 33428 ret = link_inputs(seg, idx_chain, idx_filter, inputs);
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
842 goto fail;
843
844 33428 ret = link_outputs(seg, idx_chain, idx_filter, outputs);
845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33428 times.
33428 if (ret < 0)
846 goto fail;
847 }
848 }
849 15171 return 0;
850 fail:
851 avfilter_inout_free(inputs);
852 avfilter_inout_free(outputs);
853 return ret;
854 }
855
856 // print an error message if some options were not found
857 static void log_unknown_opt(const AVFilterGraphSegment *seg)
858 {
859 for (size_t i = 0; i < seg->nb_chains; i++) {
860 const AVFilterChain *ch = seg->chains[i];
861
862 for (size_t j = 0; j < ch->nb_filters; j++) {
863 const AVFilterParams *p = ch->filters[j];
864 const AVDictionaryEntry *e;
865
866 if (!p->filter)
867 continue;
868
869 e = av_dict_iterate(p->opts, NULL);
870
871 if (e) {
872 av_log(p->filter, AV_LOG_ERROR,
873 "Could not set non-existent option '%s' to value '%s'\n",
874 e->key, e->value);
875 return;
876 }
877 }
878 }
879
880 }
881
882 15171 int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags,
883 AVFilterInOut **inputs,
884 AVFilterInOut **outputs)
885 {
886 int ret;
887
888
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (flags)
889 return AVERROR(ENOSYS);
890
891 15171 ret = avfilter_graph_segment_create_filters(seg, 0);
892
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (ret < 0) {
893 av_log(seg->graph, AV_LOG_ERROR, "Error creating filters\n");
894 return ret;
895 }
896
897 15171 ret = avfilter_graph_segment_apply_opts(seg, 0);
898
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (ret < 0) {
899 if (ret == AVERROR_OPTION_NOT_FOUND)
900 log_unknown_opt(seg);
901 av_log(seg->graph, AV_LOG_ERROR, "Error applying filter options\n");
902 return ret;
903 }
904
905 15171 ret = avfilter_graph_segment_init(seg, 0);
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (ret < 0) {
907 av_log(seg->graph, AV_LOG_ERROR, "Error initializing filters\n");
908 return ret;
909 }
910
911 15171 ret = avfilter_graph_segment_link(seg, 0, inputs, outputs);
912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15171 times.
15171 if (ret < 0) {
913 av_log(seg->graph, AV_LOG_ERROR, "Error linking filters\n");
914 return ret;
915 }
916
917 15171 return 0;
918 }
919
920 43 int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
921 AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr,
922 void *log_ctx)
923 {
924
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 AVFilterInOut *user_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL;
925
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 AVFilterInOut *user_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
926
927 43 AVFilterInOut *inputs = NULL, *outputs = NULL;
928 43 AVFilterGraphSegment *seg = NULL;
929 AVFilterChain *ch;
930 AVFilterParams *p;
931 int ret;
932
933 43 ret = avfilter_graph_segment_parse(graph, filters, 0, &seg);
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0)
935 goto end;
936
937 43 ret = avfilter_graph_segment_create_filters(seg, 0);
938
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0)
939 goto end;
940
941 43 ret = avfilter_graph_segment_apply_opts(seg, 0);
942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0) {
943 if (ret == AVERROR_OPTION_NOT_FOUND)
944 log_unknown_opt(seg);
945 goto end;
946 }
947
948 43 ret = avfilter_graph_segment_init(seg, 0);
949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0)
950 goto end;
951
952 /* First input pad, assume it is "[in]" if not specified */
953 43 p = seg->chains[0]->filters[0];
954
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
43 if (p->filter->nb_inputs == 1 && !p->inputs) {
955 const char *tmp = "[in]";
956
957 ret = linklabels_parse(graph, &tmp, &p->inputs, &p->nb_inputs);
958 if (ret < 0)
959 goto end;
960 }
961
962 /* Last output pad, assume it is "[out]" if not specified */
963 43 ch = seg->chains[seg->nb_chains - 1];
964 43 p = ch->filters[ch->nb_filters - 1];
965
3/4
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 6 times.
43 if (p->filter->nb_outputs == 1 && !p->outputs) {
966 37 const char *tmp = "[out]";
967
968 37 ret = linklabels_parse(graph, &tmp, &p->outputs, &p->nb_outputs);
969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (ret < 0)
970 goto end;
971 }
972
973 43 ret = avfilter_graph_segment_apply(seg, 0, &inputs, &outputs);
974 43 avfilter_graph_segment_free(&seg);
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0)
976 goto end;
977
978 // process user-supplied inputs/outputs
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 while (inputs) {
980 AVFilterInOut *cur, *match = NULL;
981
982 cur = inputs;
983 inputs = cur->next;
984 cur->next = NULL;
985
986 if (cur->name)
987 match = extract_inout(cur->name, &user_outputs);
988
989 if (match) {
990 ret = avfilter_link(match->filter_ctx, match->pad_idx,
991 cur->filter_ctx, cur->pad_idx);
992 avfilter_inout_free(&match);
993 avfilter_inout_free(&cur);
994 if (ret < 0)
995 goto end;
996 } else
997 append_inout(&user_inputs, &cur);
998 }
999
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 43 times.
88 while (outputs) {
1000 45 AVFilterInOut *cur, *match = NULL;
1001
1002 45 cur = outputs;
1003 45 outputs = cur->next;
1004 45 cur->next = NULL;
1005
1006
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 if (cur->name)
1007 45 match = extract_inout(cur->name, &user_inputs);
1008
1009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (match) {
1010 ret = avfilter_link(cur->filter_ctx, cur->pad_idx,
1011 match->filter_ctx, match->pad_idx);
1012 avfilter_inout_free(&match);
1013 avfilter_inout_free(&cur);
1014 if (ret < 0)
1015 goto end;
1016 } else
1017 45 append_inout(&user_outputs, &cur);
1018 }
1019
1020 43 end:
1021 43 avfilter_graph_segment_free(&seg);
1022
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (ret < 0) {
1024 av_log(graph, AV_LOG_ERROR, "Error processing filtergraph: %s\n",
1025 av_err2str(ret));
1026
1027 while (graph->nb_filters)
1028 avfilter_free(graph->filters[0]);
1029 av_freep(&graph->filters);
1030 }
1031
1032 /* clear open_in/outputs only if not passed as parameters */
1033
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if (open_inputs_ptr) *open_inputs_ptr = user_inputs;
1034 else avfilter_inout_free(&user_inputs);
1035
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if (open_outputs_ptr) *open_outputs_ptr = user_outputs;
1036 else avfilter_inout_free(&user_outputs);
1037
1038 43 avfilter_inout_free(&inputs);
1039 43 avfilter_inout_free(&outputs);
1040
1041 43 return ret;
1042 }
1043