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 |