FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/graphparser.c
Date: 2024-11-20 23:03:26
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 1007 static char *parse_link_name(const char **buf, void *log_ctx)
44 {
45 1007 const char *start = *buf;
46 char *name;
47 1007 (*buf)++;
48
49 1007 name = av_get_token(buf, "]");
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 if (!name)
51 return NULL;
52
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 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 1007 times.
1007 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 1007 (*buf)++;
67
68 1007 return name;
69 }
70
71 AVFilterInOut *avfilter_inout_alloc(void)
72 {
73 return av_mallocz(sizeof(AVFilterInOut));
74 }
75
76 30412 void avfilter_inout_free(AVFilterInOut **inout)
77 {
78
2/2
✓ Branch 0 taken 29001 times.
✓ Branch 1 taken 30412 times.
59413 while (*inout) {
79 29001 AVFilterInOut *next = (*inout)->next;
80 29001 av_freep(&(*inout)->name);
81 29001 av_freep(inout);
82 29001 *inout = next;
83 }
84 30412 }
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 29046 static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
104 {
105
4/4
✓ Branch 0 taken 4962 times.
✓ Branch 1 taken 28648 times.
✓ Branch 2 taken 4564 times.
✓ Branch 3 taken 398 times.
33610 while (*inouts && (*inouts)->next)
106 4564 inouts = &((*inouts)->next);
107
108
2/2
✓ Branch 0 taken 28648 times.
✓ Branch 1 taken 398 times.
29046 if (!*inouts)
109 28648 *inouts = *element;
110 else
111 398 (*inouts)->next = *element;
112 29046 *element = NULL;
113 29046 }
114
115 15163 static int parse_sws_flags(const char **buf, char **dst, void *log_ctx)
116 {
117 15163 char *p = strchr(*buf, ';');
118
119
2/2
✓ Branch 0 taken 15114 times.
✓ Branch 1 taken 49 times.
15163 if (strncmp(*buf, "sws_flags=", 10))
120 15114 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 1007 static void pad_params_free(AVFilterPadParams **pfpp)
228 {
229 1007 AVFilterPadParams *fpp = *pfpp;
230
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 if (!fpp)
232 return;
233
234 1007 av_freep(&fpp->label);
235
236 1007 av_freep(pfpp);
237 }
238
239 33398 static void filter_params_free(AVFilterParams **pp)
240 {
241 33398 AVFilterParams *p = *pp;
242
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (!p)
244 return;
245
246
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 33398 times.
33902 for (unsigned i = 0; i < p->nb_inputs; i++)
247 504 pad_params_free(&p->inputs[i]);
248 33398 av_freep(&p->inputs);
249
250
2/2
✓ Branch 0 taken 503 times.
✓ Branch 1 taken 33398 times.
33901 for (unsigned i = 0; i < p->nb_outputs; i++)
251 503 pad_params_free(&p->outputs[i]);
252 33398 av_freep(&p->outputs);
253
254 33398 av_dict_free(&p->opts);
255
256 33398 av_freep(&p->filter_name);
257 33398 av_freep(&p->instance_name);
258
259 33398 av_freep(pp);
260 }
261
262 15555 static void chain_free(AVFilterChain **pch)
263 {
264 15555 AVFilterChain *ch = *pch;
265
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15555 times.
15555 if (!ch)
267 return;
268
269
2/2
✓ Branch 0 taken 33398 times.
✓ Branch 1 taken 15555 times.
48953 for (size_t i = 0; i < ch->nb_filters; i++)
270 33398 filter_params_free(&ch->filters[i]);
271 15555 av_freep(&ch->filters);
272
273 15555 av_freep(pch);
274 }
275
276 15206 void avfilter_graph_segment_free(AVFilterGraphSegment **pseg)
277 {
278 15206 AVFilterGraphSegment *seg = *pseg;
279
280
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 15163 times.
15206 if (!seg)
281 43 return;
282
283
2/2
✓ Branch 0 taken 15555 times.
✓ Branch 1 taken 15163 times.
30718 for (size_t i = 0; i < seg->nb_chains; i++)
284 15555 chain_free(&seg->chains[i]);
285 15163 av_freep(&seg->chains);
286
287 15163 av_freep(&seg->scale_sws_opts);
288
289 15163 av_freep(pseg);
290 }
291
292 66833 static int linklabels_parse(void *logctx, const char **linklabels,
293 AVFilterPadParams ***res, unsigned *nb_res)
294 {
295 66833 AVFilterPadParams **pp = NULL;
296 66833 int nb = 0;
297 int ret;
298
299
2/2
✓ Branch 0 taken 1007 times.
✓ Branch 1 taken 66833 times.
67840 while (**linklabels == '[') {
300 char *label;
301 AVFilterPadParams *par;
302
303 1007 label = parse_link_name(linklabels, logctx);
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 if (!label) {
305 ret = AVERROR(EINVAL);
306 goto fail;
307 }
308
309 1007 par = av_mallocz(sizeof(*par));
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 if (!par) {
311 av_freep(&label);
312 ret = AVERROR(ENOMEM);
313 goto fail;
314 }
315
316 1007 par->label = label;
317
318 1007 ret = av_dynarray_add_nofree(&pp, &nb, par);
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 if (ret < 0) {
320 pad_params_free(&par);
321 goto fail;
322 }
323
324 1007 *linklabels += strspn(*linklabels, WHITESPACES);
325 }
326
327 66833 *res = pp;
328 66833 *nb_res = nb;
329
330 66833 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 33398 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 33398 p = av_mallocz(sizeof(*p));
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (!p)
347 return AVERROR(ENOMEM);
348
349 33398 ret = linklabels_parse(logctx, filter, &p->inputs, &p->nb_inputs);
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
351 goto fail;
352
353 33398 p->filter_name = av_get_token(filter, "=,;[");
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (!p->filter_name) {
355 ret = AVERROR(ENOMEM);
356 goto fail;
357 }
358
359 33398 inst_name = strchr(p->filter_name, '@');
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 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 20457 times.
✓ Branch 1 taken 12941 times.
33398 if (**filter == '=') {
370 20457 const AVFilter *f = avfilter_get_by_name(p->filter_name);
371 char *opts;
372
373 20457 (*filter)++;
374
375 20457 opts = av_get_token(filter, "[],;");
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20457 times.
20457 if (!opts) {
377 ret = AVERROR(ENOMEM);
378 goto fail;
379 }
380
381 20457 ret = ff_filter_opt_parse(logctx, f ? f->priv_class : NULL,
382
1/2
✓ Branch 0 taken 20457 times.
✗ Branch 1 not taken.
20457 &p->opts, opts);
383 20457 av_freep(&opts);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20457 times.
20457 if (ret < 0)
385 goto fail;
386 }
387
388 33398 ret = linklabels_parse(logctx, filter, &p->outputs, &p->nb_outputs);
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
390 goto fail;
391
392 33398 *filter += strspn(*filter, WHITESPACES);
393
394 33398 *pp = p;
395 33398 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 15555 static int chain_parse(void *logctx, const char **pchain,
404 AVFilterChain **pch)
405 {
406 15555 const char *chain = *pchain;
407 AVFilterChain *ch;
408 15555 int ret, nb_filters = 0;
409
410 15555 *pch = NULL;
411
412 15555 ch = av_mallocz(sizeof(*ch));
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15555 times.
15555 if (!ch)
414 return AVERROR(ENOMEM);
415
416
2/2
✓ Branch 0 taken 33398 times.
✓ Branch 1 taken 15147 times.
48545 while (*chain) {
417 AVFilterParams *p;
418 char chr;
419
420 33398 ret = filter_parse(logctx, &chain, &p);
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
422 goto fail;
423
424 33398 ret = av_dynarray_add_nofree(&ch->filters, &nb_filters, p);
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0) {
426 filter_params_free(&p);
427 goto fail;
428 }
429 33398 ch->nb_filters = nb_filters;
430
431 // a filter ends with one of: , ; end-of-string
432 33398 chr = *chain;
433
5/6
✓ Branch 0 taken 18251 times.
✓ Branch 1 taken 15147 times.
✓ Branch 2 taken 408 times.
✓ Branch 3 taken 17843 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 408 times.
33398 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 18251 times.
✓ Branch 1 taken 15147 times.
33398 if (chr) {
441 18251 chain++;
442 18251 chain += strspn(chain, WHITESPACES);
443
444
2/2
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 17843 times.
18251 if (chr == ';')
445 408 break;
446 }
447 }
448
449 15555 *pchain = chain;
450 15555 *pch = ch;
451
452 15555 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 15163 int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str,
461 int flags, AVFilterGraphSegment **pseg)
462 {
463 AVFilterGraphSegment *seg;
464 15163 int ret, nb_chains = 0;
465
466 15163 *pseg = NULL;
467
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (flags)
469 return AVERROR(ENOSYS);
470
471 15163 seg = av_mallocz(sizeof(*seg));
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (!seg)
473 return AVERROR(ENOMEM);
474
475 15163 seg->graph = graph;
476
477 15163 graph_str += strspn(graph_str, WHITESPACES);
478
479 15163 ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, graph);
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (ret < 0)
481 goto fail;
482
483 15163 graph_str += strspn(graph_str, WHITESPACES);
484
485
2/2
✓ Branch 0 taken 15555 times.
✓ Branch 1 taken 15163 times.
30718 while (*graph_str) {
486 AVFilterChain *ch;
487
488 15555 ret = chain_parse(graph, &graph_str, &ch);
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15555 times.
15555 if (ret < 0)
490 goto fail;
491
492 15555 ret = av_dynarray_add_nofree(&seg->chains, &nb_chains, ch);
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15555 times.
15555 if (ret < 0) {
494 chain_free(&ch);
495 goto fail;
496 }
497 15555 seg->nb_chains = nb_chains;
498
499 15555 graph_str += strspn(graph_str, WHITESPACES);
500 }
501
502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 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 15163 *pseg = seg;
509
510 15163 return 0;
511 fail:
512 avfilter_graph_segment_free(&seg);
513 return ret;
514 }
515
516 30326 int avfilter_graph_segment_create_filters(AVFilterGraphSegment *seg, int flags)
517 {
518 30326 size_t idx = 0;
519
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30326 times.
30326 if (flags)
521 return AVERROR(ENOSYS);
522
523
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 30228 times.
30326 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 31110 times.
✓ Branch 1 taken 30326 times.
61436 for (size_t i = 0; i < seg->nb_chains; i++) {
531 31110 AVFilterChain *ch = seg->chains[i];
532
533
2/2
✓ Branch 0 taken 66796 times.
✓ Branch 1 taken 31110 times.
97906 for (size_t j = 0; j < ch->nb_filters; j++) {
534 66796 AVFilterParams *p = ch->filters[j];
535 66796 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 33398 times.
✓ Branch 1 taken 33398 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33398 times.
66796 if (p->filter || !p->filter_name)
540 33398 continue;
541
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 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 33398 times.
✗ Branch 1 not taken.
33398 if (!p->instance_name)
549 33398 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 33398 p->filter = avfilter_graph_alloc_filter(seg->graph, f, name);
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (!p->filter)
555 return AVERROR(ENOMEM);
556
557
4/4
✓ Branch 0 taken 8185 times.
✓ Branch 1 taken 25213 times.
✓ Branch 2 taken 2985 times.
✓ Branch 3 taken 5200 times.
33398 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 33398 av_freep(&p->filter_name);
568 33398 av_freep(&p->instance_name);
569
570 33398 idx++;
571 }
572 }
573
574 30326 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 15206 int avfilter_graph_segment_apply_opts(AVFilterGraphSegment *seg, int flags)
587 {
588 15206 int ret, leftover_opts = 0;
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15206 times.
15206 if (flags)
591 return AVERROR(ENOSYS);
592
593
2/2
✓ Branch 0 taken 15602 times.
✓ Branch 1 taken 15206 times.
30808 for (size_t i = 0; i < seg->nb_chains; i++) {
594 15602 AVFilterChain *ch = seg->chains[i];
595
596
2/2
✓ Branch 0 taken 33469 times.
✓ Branch 1 taken 15602 times.
49071 for (size_t j = 0; j < ch->nb_filters; j++) {
597 33469 AVFilterParams *p = ch->filters[j];
598
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33469 times.
33469 if (p->filter_name)
600 return fail_creation_pending(seg, p->filter_name, __func__);
601
3/4
✓ Branch 0 taken 33469 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33404 times.
✓ Branch 3 taken 65 times.
33469 if (!p->filter || !p->opts)
602 33404 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 15206 times.
15206 return leftover_opts ? AVERROR_OPTION_NOT_FOUND : 0;
614 }
615
616 15206 int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags)
617 {
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15206 times.
15206 if (flags)
619 return AVERROR(ENOSYS);
620
621
2/2
✓ Branch 0 taken 15602 times.
✓ Branch 1 taken 15206 times.
30808 for (size_t i = 0; i < seg->nb_chains; i++) {
622 15602 AVFilterChain *ch = seg->chains[i];
623
624
2/2
✓ Branch 0 taken 33469 times.
✓ Branch 1 taken 15602 times.
49071 for (size_t j = 0; j < ch->nb_filters; j++) {
625 33469 AVFilterParams *p = ch->filters[j];
626 int ret;
627
628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33469 times.
33469 if (p->filter_name)
629 return fail_creation_pending(seg, p->filter_name, __func__);
630
1/2
✓ Branch 0 taken 33469 times.
✗ Branch 1 not taken.
33469 if (!p->filter ||
631
2/2
✓ Branch 1 taken 71 times.
✓ Branch 2 taken 33398 times.
33469 (fffilterctx(p->filter)->state_flags & AV_CLASS_STATE_INITIALIZED))
632 71 continue;
633
634 33398 ret = avfilter_init_dict(p->filter, NULL);
635
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
636 return ret;
637 }
638 }
639
640 15206 return 0;
641 }
642
643 static unsigned
644 671 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 6033 times.
✓ Branch 1 taken 335 times.
6368 for (; idx_chain < seg->nb_chains; idx_chain++) {
649 6033 AVFilterChain *ch = seg->chains[idx_chain];
650
651
2/2
✓ Branch 0 taken 6097 times.
✓ Branch 1 taken 5697 times.
11794 for (; idx_filter < ch->nb_filters; idx_filter++) {
652 6097 AVFilterParams *p = ch->filters[idx_filter];
653
2/2
✓ Branch 0 taken 500 times.
✓ Branch 1 taken 5597 times.
6097 AVFilterPadParams **io = output ? p->outputs : p->inputs;
654
2/2
✓ Branch 0 taken 500 times.
✓ Branch 1 taken 5597 times.
6097 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 6097 times.
6097 if (!p->filter)
659 continue;
660
661
2/2
✓ Branch 0 taken 500 times.
✓ Branch 1 taken 5597 times.
6097 l = output ? p->filter->outputs : p->filter->inputs;
662
2/2
✓ Branch 0 taken 500 times.
✓ Branch 1 taken 5597 times.
6097 nb_l = output ? p->filter->nb_outputs : p->filter->nb_inputs;
663
664
2/2
✓ Branch 0 taken 5725 times.
✓ Branch 1 taken 5761 times.
11486 for (unsigned i = 0; i < FFMIN(nb_io, nb_l); i++)
665
5/6
✓ Branch 0 taken 1310 times.
✓ Branch 1 taken 4415 times.
✓ Branch 2 taken 1310 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 336 times.
✓ Branch 5 taken 974 times.
5725 if (!l[i] && io[i]->label && !strcmp(io[i]->label, label)) {
666 336 *pp = p;
667 336 return i;
668 }
669 }
670
671 5697 idx_filter = 0;
672 }
673
674 335 *pp = NULL;
675 335 return 0;
676 }
677
678 29001 static int inout_add(AVFilterInOut **inouts, AVFilterContext *f, unsigned pad_idx,
679 const char *label)
680 {
681 29001 AVFilterInOut *io = av_mallocz(sizeof(*io));
682
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29001 times.
29001 if (!io)
684 return AVERROR(ENOMEM);
685
686 29001 io->filter_ctx = f;
687 29001 io->pad_idx = pad_idx;
688
689
2/2
✓ Branch 0 taken 335 times.
✓ Branch 1 taken 28666 times.
29001 if (label) {
690 335 io->name = av_strdup(label);
691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 335 times.
335 if (!io->name) {
692 avfilter_inout_free(&io);
693 return AVERROR(ENOMEM);
694 }
695 }
696
697 29001 append_inout(inouts, &io);
698
699 29001 return 0;
700 }
701
702 33398 static int link_inputs(AVFilterGraphSegment *seg, size_t idx_chain,
703 size_t idx_filter, AVFilterInOut **inputs)
704 {
705 33398 AVFilterChain *ch = seg->chains[idx_chain];
706 33398 AVFilterParams *p = ch->filters[idx_filter];
707 33398 AVFilterContext *f = p->filter;
708
709 int ret;
710
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 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 31775 times.
✓ Branch 1 taken 33398 times.
65173 for (unsigned in = 0; in < f->nb_inputs; in++) {
720
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 31271 times.
31775 const char *label = (in < p->nb_inputs) ? p->inputs[in]->label : NULL;
721
722 // skip already linked inputs
723
2/2
✓ Branch 0 taken 18181 times.
✓ Branch 1 taken 13594 times.
31775 if (f->inputs[in])
724 18181 continue;
725
726
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 13426 times.
13594 if (label) {
727 168 AVFilterParams *po = NULL;
728 168 unsigned idx = find_linklabel(seg, label, 1, idx_chain, idx_filter, &po);
729
730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 168 times.
168 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 13594 ret = inout_add(inputs, f, in, label);
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13594 times.
13594 if (ret < 0)
741 return ret;
742 }
743
744 33398 return 0;
745 }
746
747 33398 static int link_outputs(AVFilterGraphSegment *seg, size_t idx_chain,
748 size_t idx_filter, AVFilterInOut **outputs)
749 {
750 33398 AVFilterChain *ch = seg->chains[idx_chain];
751 33398 AVFilterParams *p = ch->filters[idx_filter];
752 33398 AVFilterContext *f = p->filter;
753
754 int ret;
755
756
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 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 33588 times.
✓ Branch 1 taken 33398 times.
66986 for (unsigned out = 0; out < f->nb_outputs; out++) {
764
2/2
✓ Branch 0 taken 503 times.
✓ Branch 1 taken 33085 times.
33588 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 33588 times.
33588 if (f->outputs[out])
768 continue;
769
770
2/2
✓ Branch 0 taken 503 times.
✓ Branch 1 taken 33085 times.
33588 if (label) {
771 503 AVFilterParams *po = NULL;
772 503 unsigned idx = find_linklabel(seg, label, 0, idx_chain, idx_filter, &po);
773
774
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 167 times.
503 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 17845 times.
✓ Branch 1 taken 15407 times.
✓ Branch 2 taken 17845 times.
✗ Branch 3 not taken.
33252 for (size_t i = idx_filter + 1; i < ch->nb_filters && !label; i++) {
786 17845 AVFilterParams *p_next = ch->filters[i];
787
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17845 times.
17845 if (!p_next->filter)
789 continue;
790
791
1/2
✓ Branch 0 taken 17849 times.
✗ Branch 1 not taken.
17849 for (unsigned in = 0; in < p_next->filter->nb_inputs; in++) {
792
2/2
✓ Branch 0 taken 17845 times.
✓ Branch 1 taken 4 times.
17849 if (!p_next->filter->inputs[in] &&
793
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17845 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17845 (in >= p_next->nb_inputs || !p_next->inputs[in]->label)) {
794 17845 ret = avfilter_link(f, out, p_next->filter, in);
795
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17845 times.
17845 if (ret < 0)
796 return ret;
797
798 17845 goto cont;
799 }
800 }
801 break;
802 }
803
804 15407 ret = inout_add(outputs, f, out, label);
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15407 times.
15407 if (ret < 0)
806 return ret;
807
808 33252 cont:;
809 }
810
811 33398 return 0;
812 }
813
814 15163 int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags,
815 AVFilterInOut **inputs,
816 AVFilterInOut **outputs)
817 {
818 int ret;
819
820 15163 *inputs = NULL;
821 15163 *outputs = NULL;
822
823
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (flags)
824 return AVERROR(ENOSYS);
825
826
2/2
✓ Branch 0 taken 15555 times.
✓ Branch 1 taken 15163 times.
30718 for (size_t idx_chain = 0; idx_chain < seg->nb_chains; idx_chain++) {
827 15555 AVFilterChain *ch = seg->chains[idx_chain];
828
829
2/2
✓ Branch 0 taken 33398 times.
✓ Branch 1 taken 15555 times.
48953 for (size_t idx_filter = 0; idx_filter < ch->nb_filters; idx_filter++) {
830 33398 AVFilterParams *p = ch->filters[idx_filter];
831
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 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 33398 times.
33398 if (!p->filter)
838 continue;
839
840 33398 ret = link_inputs(seg, idx_chain, idx_filter, inputs);
841
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
842 goto fail;
843
844 33398 ret = link_outputs(seg, idx_chain, idx_filter, outputs);
845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33398 times.
33398 if (ret < 0)
846 goto fail;
847 }
848 }
849 15163 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 15163 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 15163 times.
15163 if (flags)
889 return AVERROR(ENOSYS);
890
891 15163 ret = avfilter_graph_segment_create_filters(seg, 0);
892
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (ret < 0) {
893 av_log(seg->graph, AV_LOG_ERROR, "Error creating filters\n");
894 return ret;
895 }
896
897 15163 ret = avfilter_graph_segment_apply_opts(seg, 0);
898
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 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 15163 ret = avfilter_graph_segment_init(seg, 0);
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (ret < 0) {
907 av_log(seg->graph, AV_LOG_ERROR, "Error initializing filters\n");
908 return ret;
909 }
910
911 15163 ret = avfilter_graph_segment_link(seg, 0, inputs, outputs);
912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15163 times.
15163 if (ret < 0) {
913 av_log(seg->graph, AV_LOG_ERROR, "Error linking filters\n");
914 return ret;
915 }
916
917 15163 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