FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/graphparser.c
Date: 2026-05-03 13:33:45
Exec Total Coverage
Lines: 351 586 59.9%
Functions: 23 28 82.1%
Branches: 202 362 55.8%

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 1190 static char *parse_link_name(const char **buf, void *log_ctx)
44 {
45 1190 const char *start = *buf;
46 char *name;
47 1190 (*buf)++;
48
49 1190 name = av_get_token(buf, "]");
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 if (!name)
51 return NULL;
52
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 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 1190 times.
1190 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 1190 (*buf)++;
67
68 1190 return name;
69 }
70
71 AVFilterInOut *avfilter_inout_alloc(void)
72 {
73 return av_mallocz(sizeof(AVFilterInOut));
74 }
75
76 32798 void avfilter_inout_free(AVFilterInOut **inout)
77 {
78
2/2
✓ Branch 0 taken 30726 times.
✓ Branch 1 taken 32798 times.
63524 while (*inout) {
79 30726 AVFilterInOut *next = (*inout)->next;
80 30726 av_freep(&(*inout)->name);
81 30726 av_freep(inout);
82 30726 *inout = next;
83 }
84 32798 }
85
86 63 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
87 {
88 AVFilterInOut *ret;
89
90
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
63 while (*links && (!(*links)->name || strcmp((*links)->name, label)))
91 links = &((*links)->next);
92
93 63 ret = *links;
94
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (ret) {
96 *links = ret->next;
97 ret->next = NULL;
98 }
99
100 63 return ret;
101 }
102
103 30789 static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
104 {
105
4/4
✓ Branch 0 taken 5252 times.
✓ Branch 1 taken 30302 times.
✓ Branch 2 taken 4765 times.
✓ Branch 3 taken 487 times.
35554 while (*inouts && (*inouts)->next)
106 4765 inouts = &((*inouts)->next);
107
108
2/2
✓ Branch 0 taken 30302 times.
✓ Branch 1 taken 487 times.
30789 if (!*inouts)
109 30302 *inouts = *element;
110 else
111 487 (*inouts)->next = *element;
112 30789 *element = NULL;
113 30789 }
114
115 16339 static int parse_sws_flags(const char **buf, char **dst, void *log_ctx)
116 {
117 16339 char *p = strchr(*buf, ';');
118
119
2/2
✓ Branch 0 taken 16288 times.
✓ Branch 1 taken 51 times.
16339 if (strncmp(*buf, "sws_flags=", 10))
120 16288 return 0;
121
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (!p) {
123 av_log(log_ctx, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
124 return AVERROR(EINVAL);
125 }
126
127 51 *buf += 4; // keep the 'flags=' part
128
129 51 av_freep(dst);
130
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
51 if (!(*dst = av_mallocz(p - *buf + 1)))
131 return AVERROR(ENOMEM);
132 51 av_strlcpy(*dst, *buf, p - *buf + 1);
133
134 51 *buf = p + 1;
135 51 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 1190 static void pad_params_free(AVFilterPadParams **pfpp)
228 {
229 1190 AVFilterPadParams *fpp = *pfpp;
230
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 if (!fpp)
232 return;
233
234 1190 av_freep(&fpp->label);
235
236 1190 av_freep(pfpp);
237 }
238
239 37181 static void filter_params_free(AVFilterParams **pp)
240 {
241 37181 AVFilterParams *p = *pp;
242
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (!p)
244 return;
245
246
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 37181 times.
37781 for (unsigned i = 0; i < p->nb_inputs; i++)
247 600 pad_params_free(&p->inputs[i]);
248 37181 av_freep(&p->inputs);
249
250
2/2
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 37181 times.
37771 for (unsigned i = 0; i < p->nb_outputs; i++)
251 590 pad_params_free(&p->outputs[i]);
252 37181 av_freep(&p->outputs);
253
254 37181 av_dict_free(&p->opts);
255
256 37181 av_freep(&p->filter_name);
257 37181 av_freep(&p->instance_name);
258
259 37181 av_freep(pp);
260 }
261
262 16767 static void chain_free(AVFilterChain **pch)
263 {
264 16767 AVFilterChain *ch = *pch;
265
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16767 times.
16767 if (!ch)
267 return;
268
269
2/2
✓ Branch 0 taken 37181 times.
✓ Branch 1 taken 16767 times.
53948 for (size_t i = 0; i < ch->nb_filters; i++)
270 37181 filter_params_free(&ch->filters[i]);
271 16767 av_freep(&ch->filters);
272
273 16767 av_freep(pch);
274 }
275
276 16399 void avfilter_graph_segment_free(AVFilterGraphSegment **pseg)
277 {
278 16399 AVFilterGraphSegment *seg = *pseg;
279
280
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 16339 times.
16399 if (!seg)
281 60 return;
282
283
2/2
✓ Branch 0 taken 16767 times.
✓ Branch 1 taken 16339 times.
33106 for (size_t i = 0; i < seg->nb_chains; i++)
284 16767 chain_free(&seg->chains[i]);
285 16339 av_freep(&seg->chains);
286
287 16339 av_freep(&seg->scale_sws_opts);
288
289 16339 av_freep(pseg);
290 }
291
292 74414 static int linklabels_parse(void *logctx, const char **linklabels,
293 AVFilterPadParams ***res, unsigned *nb_res)
294 {
295 74414 AVFilterPadParams **pp = NULL;
296 74414 int nb = 0;
297 int ret;
298
299
2/2
✓ Branch 0 taken 1190 times.
✓ Branch 1 taken 74414 times.
75604 while (**linklabels == '[') {
300 char *label;
301 AVFilterPadParams *par;
302
303 1190 label = parse_link_name(linklabels, logctx);
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 if (!label) {
305 ret = AVERROR(EINVAL);
306 goto fail;
307 }
308
309 1190 par = av_mallocz(sizeof(*par));
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 if (!par) {
311 av_freep(&label);
312 ret = AVERROR(ENOMEM);
313 goto fail;
314 }
315
316 1190 par->label = label;
317
318 1190 ret = av_dynarray_add_nofree(&pp, &nb, par);
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1190 times.
1190 if (ret < 0) {
320 pad_params_free(&par);
321 goto fail;
322 }
323
324 1190 *linklabels += strspn(*linklabels, WHITESPACES);
325 }
326
327 74414 *res = pp;
328 74414 *nb_res = nb;
329
330 74414 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 37181 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 37181 p = av_mallocz(sizeof(*p));
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (!p)
347 return AVERROR(ENOMEM);
348
349 37181 ret = linklabels_parse(logctx, filter, &p->inputs, &p->nb_inputs);
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
351 goto fail;
352
353 37181 p->filter_name = av_get_token(filter, "=,;[");
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (!p->filter_name) {
355 ret = AVERROR(ENOMEM);
356 goto fail;
357 }
358
359 37181 inst_name = strchr(p->filter_name, '@');
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 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 23759 times.
✓ Branch 1 taken 13422 times.
37181 if (**filter == '=') {
370 23759 const AVFilter *f = avfilter_get_by_name(p->filter_name);
371 char *opts;
372
373 23759 (*filter)++;
374
375 23759 opts = av_get_token(filter, "[],;");
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23759 times.
23759 if (!opts) {
377 ret = AVERROR(ENOMEM);
378 goto fail;
379 }
380
381 23759 ret = ff_filter_opt_parse(logctx, f ? f->priv_class : NULL,
382
1/2
✓ Branch 0 taken 23759 times.
✗ Branch 1 not taken.
23759 &p->opts, opts);
383 23759 av_freep(&opts);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23759 times.
23759 if (ret < 0)
385 goto fail;
386 }
387
388 37181 ret = linklabels_parse(logctx, filter, &p->outputs, &p->nb_outputs);
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
390 goto fail;
391
392 37181 *filter += strspn(*filter, WHITESPACES);
393
394 37181 *pp = p;
395 37181 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 16767 static int chain_parse(void *logctx, const char **pchain,
404 AVFilterChain **pch)
405 {
406 16767 const char *chain = *pchain;
407 AVFilterChain *ch;
408 16767 int ret, nb_filters = 0;
409
410 16767 *pch = NULL;
411
412 16767 ch = av_mallocz(sizeof(*ch));
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16767 times.
16767 if (!ch)
414 return AVERROR(ENOMEM);
415
416
2/2
✓ Branch 0 taken 37181 times.
✓ Branch 1 taken 16317 times.
53498 while (*chain) {
417 AVFilterParams *p;
418 char chr;
419
420 37181 ret = filter_parse(logctx, &chain, &p);
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
422 goto fail;
423
424 37181 ret = av_dynarray_add_nofree(&ch->filters, &nb_filters, p);
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0) {
426 filter_params_free(&p);
427 goto fail;
428 }
429 37181 ch->nb_filters = nb_filters;
430
431 // a filter ends with one of: , ; end-of-string
432 37181 chr = *chain;
433
5/6
✓ Branch 0 taken 20864 times.
✓ Branch 1 taken 16317 times.
✓ Branch 2 taken 450 times.
✓ Branch 3 taken 20414 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 450 times.
37181 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 20864 times.
✓ Branch 1 taken 16317 times.
37181 if (chr) {
441 20864 chain++;
442 20864 chain += strspn(chain, WHITESPACES);
443
444
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 20414 times.
20864 if (chr == ';')
445 450 break;
446 }
447 }
448
449 16767 *pchain = chain;
450 16767 *pch = ch;
451
452 16767 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 16339 int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str,
461 int flags, AVFilterGraphSegment **pseg)
462 {
463 AVFilterGraphSegment *seg;
464 16339 int ret, nb_chains = 0;
465
466 16339 *pseg = NULL;
467
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (flags)
469 return AVERROR(ENOSYS);
470
471 16339 seg = av_mallocz(sizeof(*seg));
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (!seg)
473 return AVERROR(ENOMEM);
474
475 16339 seg->graph = graph;
476
477 16339 graph_str += strspn(graph_str, WHITESPACES);
478
479 16339 ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, graph);
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (ret < 0)
481 goto fail;
482
483 16339 graph_str += strspn(graph_str, WHITESPACES);
484
485
2/2
✓ Branch 0 taken 16767 times.
✓ Branch 1 taken 16339 times.
33106 while (*graph_str) {
486 AVFilterChain *ch;
487
488 16767 ret = chain_parse(graph, &graph_str, &ch);
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16767 times.
16767 if (ret < 0)
490 goto fail;
491
492 16767 ret = av_dynarray_add_nofree(&seg->chains, &nb_chains, ch);
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16767 times.
16767 if (ret < 0) {
494 chain_free(&ch);
495 goto fail;
496 }
497 16767 seg->nb_chains = nb_chains;
498
499 16767 graph_str += strspn(graph_str, WHITESPACES);
500 }
501
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 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 16339 *pseg = seg;
509
510 16339 return 0;
511 fail:
512 avfilter_graph_segment_free(&seg);
513 return ret;
514 }
515
516 32678 int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags)
517 {
518 32678 size_t idx = 0;
519
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32678 times.
32678 if (flags)
521 return AVERROR(ENOSYS);
522
523
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 32576 times.
32678 if (seg->scale_sws_opts) {
524 102 av_freep(&seg->graph->scale_sws_opts);
525 102 seg->graph->scale_sws_opts = av_strdup(seg->scale_sws_opts);
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (!seg->graph->scale_sws_opts)
527 return AVERROR(ENOMEM);
528 }
529
530
2/2
✓ Branch 0 taken 33534 times.
✓ Branch 1 taken 32678 times.
66212 for (size_t i = 0; i < seg->nb_chains; i++) {
531 33534 AVFilterChain *ch = seg->chains[i];
532
533
2/2
✓ Branch 0 taken 74362 times.
✓ Branch 1 taken 33534 times.
107896 for (size_t j = 0; j < ch->nb_filters; j++) {
534 74362 AVFilterParams *p = ch->filters[j];
535 74362 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 37181 times.
✓ Branch 1 taken 37181 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 37181 times.
74362 if (p->filter || !p->filter_name)
540 37181 continue;
541
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 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 37181 times.
✗ Branch 1 not taken.
37181 if (!p->instance_name)
549 37181 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 37181 p->filter = avfilter_graph_alloc_filter(seg->graph, f, name);
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (!p->filter)
555 return AVERROR(ENOMEM);
556
557
4/4
✓ Branch 0 taken 9163 times.
✓ Branch 1 taken 28018 times.
✓ Branch 2 taken 3124 times.
✓ Branch 3 taken 6039 times.
37181 if (!strcmp(f->name, "scale") && seg->graph->scale_sws_opts) {
558 3124 int ret = av_set_options_string(p->filter, seg->graph->scale_sws_opts,
559 "=", ":");
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3124 times.
3124 if (ret < 0) {
561 avfilter_free(p->filter);
562 p->filter = NULL;
563 return ret;
564 }
565 }
566
567 37181 av_freep(&p->filter_name);
568 37181 av_freep(&p->instance_name);
569
570 37181 idx++;
571 }
572 }
573
574 32678 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 16399 int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags)
587 {
588 16399 int ret, leftover_opts = 0;
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16399 times.
16399 if (flags)
591 return AVERROR(ENOSYS);
592
593
2/2
✓ Branch 0 taken 16831 times.
✓ Branch 1 taken 16399 times.
33230 for (size_t i = 0; i < seg->nb_chains; i++) {
594 16831 AVFilterChain *ch = seg->chains[i];
595
596
2/2
✓ Branch 0 taken 37276 times.
✓ Branch 1 taken 16831 times.
54107 for (size_t j = 0; j < ch->nb_filters; j++) {
597 37276 AVFilterParams *p = ch->filters[j];
598
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37276 times.
37276 if (p->filter_name)
600 return fail_creation_pending(seg, p->filter_name, __func__);
601
3/4
✓ Branch 0 taken 37276 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37188 times.
✓ Branch 3 taken 88 times.
37276 if (!p->filter || !p->opts)
602 37188 continue;
603
604 88 ret = av_opt_set_dict2(p->filter, &p->opts, AV_OPT_SEARCH_CHILDREN);
605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (ret < 0)
606 return ret;
607
608
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
88 if (av_dict_count(p->opts))
609 leftover_opts = 1;
610 }
611 }
612
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16399 times.
16399 return leftover_opts ? AVERROR_OPTION_NOT_FOUND : 0;
614 }
615
616 16399 int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags)
617 {
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16399 times.
16399 if (flags)
619 return AVERROR(ENOSYS);
620
621
2/2
✓ Branch 0 taken 16831 times.
✓ Branch 1 taken 16399 times.
33230 for (size_t i = 0; i < seg->nb_chains; i++) {
622 16831 AVFilterChain *ch = seg->chains[i];
623
624
2/2
✓ Branch 0 taken 37276 times.
✓ Branch 1 taken 16831 times.
54107 for (size_t j = 0; j < ch->nb_filters; j++) {
625 37276 AVFilterParams *p = ch->filters[j];
626 int ret;
627
628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37276 times.
37276 if (p->filter_name)
629 return fail_creation_pending(seg, p->filter_name, __func__);
630
1/2
✓ Branch 0 taken 37276 times.
✗ Branch 1 not taken.
37276 if (!p->filter ||
631
2/2
✓ Branch 1 taken 95 times.
✓ Branch 2 taken 37181 times.
37276 (fffilterctx(p->filter)->state_flags & AV_CLASS_STATE_INITIALIZED))
632 95 continue;
633
634 37181 ret = avfilter_init_dict(p->filter, NULL);
635
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
636 return ret;
637 }
638 }
639
640 16399 return 0;
641 }
642
643 static unsigned
644 838 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 6444 times.
✓ Branch 1 taken 486 times.
6930 for (; idx_chain < seg->nb_chains; idx_chain++) {
649 6444 AVFilterChain *ch = seg->chains[idx_chain];
650
651
2/2
✓ Branch 0 taken 6514 times.
✓ Branch 1 taken 6092 times.
12606 for (; idx_filter < ch->nb_filters; idx_filter++) {
652 6514 AVFilterParams *p = ch->filters[idx_filter];
653
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 5802 times.
6514 AVFilterPadParams **io = output ? p->outputs : p->inputs;
654
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 5802 times.
6514 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 6514 times.
6514 if (!p->filter)
659 continue;
660
661
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 5802 times.
6514 l = output ? p->filter->outputs : p->filter->inputs;
662
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 5802 times.
6514 nb_l = output ? p->filter->nb_outputs : p->filter->nb_inputs;
663
664
2/2
✓ Branch 0 taken 6159 times.
✓ Branch 1 taken 6162 times.
12321 for (unsigned i = 0; i < FFMIN(nb_io, nb_l); i++)
665
5/6
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 4431 times.
✓ Branch 2 taken 1728 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 352 times.
✓ Branch 5 taken 1376 times.
6159 if (!l[i] && io[i]->label && !strcmp(io[i]->label, label)) {
666 352 *pp = p;
667 352 return i;
668 }
669 }
670
671 6092 idx_filter = 0;
672 }
673
674 486 *pp = NULL;
675 486 return 0;
676 }
677
678 30726 static int inout_add(AVFilterInOut **inouts, AVFilterContext *f, unsigned pad_idx,
679 const char *label)
680 {
681 30726 AVFilterInOut *io = av_mallocz(sizeof(*io));
682
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30726 times.
30726 if (!io)
684 return AVERROR(ENOMEM);
685
686 30726 io->filter_ctx = f;
687 30726 io->pad_idx = pad_idx;
688
689
2/2
✓ Branch 0 taken 486 times.
✓ Branch 1 taken 30240 times.
30726 if (label) {
690 486 io->name = av_strdup(label);
691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 486 times.
486 if (!io->name) {
692 avfilter_inout_free(&io);
693 return AVERROR(ENOMEM);
694 }
695 }
696
697 30726 append_inout(inouts, &io);
698
699 30726 return 0;
700 }
701
702 37181 static int link_inputs(AVFilterGraphSegment *seg, size_t idx_chain,
703 size_t idx_filter, AVFilterInOut **inputs)
704 {
705 37181 AVFilterChain *ch = seg->chains[idx_chain];
706 37181 AVFilterParams *p = ch->filters[idx_filter];
707 37181 AVFilterContext *f = p->filter;
708
709 int ret;
710
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 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 34882 times.
✓ Branch 1 taken 37181 times.
72063 for (unsigned in = 0; in < f->nb_inputs; in++) {
720
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 34282 times.
34882 const char *label = (in < p->nb_inputs) ? p->inputs[in]->label : NULL;
721
722 // skip already linked inputs
723
2/2
✓ Branch 0 taken 20764 times.
✓ Branch 1 taken 14118 times.
34882 if (f->inputs[in])
724 20764 continue;
725
726
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 13866 times.
14118 if (label) {
727 252 AVFilterParams *po = NULL;
728 252 unsigned idx = find_linklabel(seg, label, 1, idx_chain, idx_filter, &po);
729
730
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 248 times.
252 if (po) {
731 4 ret = avfilter_link(po->filter, idx, f, in);
732
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret < 0)
733 return ret;
734
735 4 continue;
736 }
737 }
738
739 14114 ret = inout_add(inputs, f, in, label);
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14114 times.
14114 if (ret < 0)
741 return ret;
742 }
743
744 37181 return 0;
745 }
746
747 37181 static int link_outputs(AVFilterGraphSegment *seg, size_t idx_chain,
748 size_t idx_filter, AVFilterInOut **outputs)
749 {
750 37181 AVFilterChain *ch = seg->chains[idx_chain];
751 37181 AVFilterParams *p = ch->filters[idx_filter];
752 37181 AVFilterContext *f = p->filter;
753
754 int ret;
755
756
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 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 37380 times.
✓ Branch 1 taken 37181 times.
74561 for (unsigned out = 0; out < f->nb_outputs; out++) {
764
2/2
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 36790 times.
37380 char *label = (out < p->nb_outputs) ? p->outputs[out]->label : NULL;
765
766 // skip already linked outputs
767
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 37376 times.
37380 if (f->outputs[out])
768 4 continue;
769
770
2/2
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 36790 times.
37376 if (label) {
771 586 AVFilterParams *po = NULL;
772 586 unsigned idx = find_linklabel(seg, label, 0, idx_chain, idx_filter, &po);
773
774
2/2
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 238 times.
586 if (po) {
775 348 ret = avfilter_link(f, out, po->filter, idx);
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 348 times.
348 if (ret < 0)
777 return ret;
778
779 348 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 20416 times.
✓ Branch 1 taken 16612 times.
✓ Branch 2 taken 20416 times.
✗ Branch 3 not taken.
37028 for (size_t i = idx_filter + 1; i < ch->nb_filters && !label; i++) {
786 20416 AVFilterParams *p_next = ch->filters[i];
787
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20416 times.
20416 if (!p_next->filter)
789 continue;
790
791
1/2
✓ Branch 0 taken 20420 times.
✗ Branch 1 not taken.
20420 for (unsigned in = 0; in < p_next->filter->nb_inputs; in++) {
792
2/2
✓ Branch 0 taken 20416 times.
✓ Branch 1 taken 4 times.
20420 if (!p_next->filter->inputs[in] &&
793
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 20416 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
20416 (in >= p_next->nb_inputs || !p_next->inputs[in]->label)) {
794 20416 ret = avfilter_link(f, out, p_next->filter, in);
795
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20416 times.
20416 if (ret < 0)
796 return ret;
797
798 20416 goto cont;
799 }
800 }
801 break;
802 }
803
804 16612 ret = inout_add(outputs, f, out, label);
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16612 times.
16612 if (ret < 0)
806 return ret;
807
808 37028 cont:;
809 }
810
811 37181 return 0;
812 }
813
814 16339 int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags,
815 AVFilterInOut **inputs,
816 AVFilterInOut **outputs)
817 {
818 int ret;
819
820 16339 *inputs = NULL;
821 16339 *outputs = NULL;
822
823
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (flags)
824 return AVERROR(ENOSYS);
825
826
2/2
✓ Branch 0 taken 16767 times.
✓ Branch 1 taken 16339 times.
33106 for (size_t idx_chain = 0; idx_chain < seg->nb_chains; idx_chain++) {
827 16767 AVFilterChain *ch = seg->chains[idx_chain];
828
829
2/2
✓ Branch 0 taken 37181 times.
✓ Branch 1 taken 16767 times.
53948 for (size_t idx_filter = 0; idx_filter < ch->nb_filters; idx_filter++) {
830 37181 AVFilterParams *p = ch->filters[idx_filter];
831
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 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 37181 times.
37181 if (!p->filter)
838 continue;
839
840 37181 ret = link_inputs(seg, idx_chain, idx_filter, inputs);
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
842 goto fail;
843
844 37181 ret = link_outputs(seg, idx_chain, idx_filter, outputs);
845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37181 times.
37181 if (ret < 0)
846 goto fail;
847 }
848 }
849 16339 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 16339 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 16339 times.
16339 if (flags)
889 return AVERROR(ENOSYS);
890
891 16339 ret = avfilter_graph_segment_create_filters(seg, 0);
892
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (ret < 0) {
893 av_log(seg->graph, AV_LOG_ERROR, "Error creating filters\n");
894 return ret;
895 }
896
897 16339 ret = avfilter_graph_segment_apply_opts(seg, 0);
898
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 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 16339 ret = avfilter_graph_segment_init(seg, 0);
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (ret < 0) {
907 av_log(seg->graph, AV_LOG_ERROR, "Error initializing filters\n");
908 return ret;
909 }
910
911 16339 ret = avfilter_graph_segment_link(seg, 0, inputs, outputs);
912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16339 times.
16339 if (ret < 0) {
913 av_log(seg->graph, AV_LOG_ERROR, "Error linking filters\n");
914 return ret;
915 }
916
917 16339 return 0;
918 }
919
920 60 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 60 times.
✗ Branch 1 not taken.
60 AVFilterInOut *user_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL;
925
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 AVFilterInOut *user_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
926
927 60 AVFilterInOut *inputs = NULL, *outputs = NULL;
928 60 AVFilterGraphSegment *seg = NULL;
929 AVFilterChain *ch;
930 AVFilterParams *p;
931 int ret;
932
933 60 ret = avfilter_graph_segment_parse(graph, filters, 0, &seg);
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0)
935 goto end;
936
937 60 ret = avfilter_graph_segment_create_filters(seg, 0);
938
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0)
939 goto end;
940
941 60 ret = avfilter_graph_segment_apply_opts(seg, 0);
942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0) {
943 if (ret == AVERROR_OPTION_NOT_FOUND)
944 log_unknown_opt(seg);
945 goto end;
946 }
947
948 60 ret = avfilter_graph_segment_init(seg, 0);
949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0)
950 goto end;
951
952 /* First input pad, assume it is "[in]" if not specified */
953 60 p = seg->chains[0]->filters[0];
954
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
60 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 60 ch = seg->chains[seg->nb_chains - 1];
964 60 p = ch->filters[ch->nb_filters - 1];
965
4/4
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 7 times.
60 if (p->filter->nb_outputs == 1 && !p->outputs) {
966 52 const char *tmp = "[out]";
967
968 52 ret = linklabels_parse(graph, &tmp, &p->outputs, &p->nb_outputs);
969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (ret < 0)
970 goto end;
971 }
972
973 60 ret = avfilter_graph_segment_apply(seg, 0, &inputs, &outputs);
974 60 avfilter_graph_segment_free(&seg);
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (ret < 0)
976 goto end;
977
978 // process user-supplied inputs/outputs
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 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 63 times.
✓ Branch 1 taken 60 times.
123 while (outputs) {
1000 63 AVFilterInOut *cur, *match = NULL;
1001
1002 63 cur = outputs;
1003 63 outputs = cur->next;
1004 63 cur->next = NULL;
1005
1006
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 if (cur->name)
1007 63 match = extract_inout(cur->name, &user_inputs);
1008
1009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 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 63 append_inout(&user_outputs, &cur);
1018 }
1019
1020 60 end:
1021 60 avfilter_graph_segment_free(&seg);
1022
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 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 60 times.
✗ Branch 1 not taken.
60 if (open_inputs_ptr) *open_inputs_ptr = user_inputs;
1034 else avfilter_inout_free(&user_inputs);
1035
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 if (open_outputs_ptr) *open_outputs_ptr = user_outputs;
1036 else avfilter_inout_free(&user_outputs);
1037
1038 60 avfilter_inout_free(&inputs);
1039 60 avfilter_inout_free(&outputs);
1040
1041 60 return ret;
1042 }
1043