Commit | Line | Data |
---|---|---|
27afb09d VS |
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 <ctype.h> | |
24 | #include <string.h> | |
25 | ||
6a0c770b | 26 | #include "graphparser.h" |
27afb09d VS |
27 | #include "avfilter.h" |
28 | #include "avfiltergraph.h" | |
dd04911c | 29 | #include "parseutils.h" |
27afb09d | 30 | |
fd548e5b SS |
31 | #define WHITESPACES " \n\t" |
32 | ||
9710beaf | 33 | static int link_filter(AVFilterContext *src, int srcpad, |
3a70bb2d VS |
34 | AVFilterContext *dst, int dstpad, |
35 | AVClass *log_ctx) | |
27afb09d | 36 | { |
9710beaf | 37 | if(avfilter_link(src, srcpad, dst, dstpad)) { |
3a70bb2d | 38 | av_log(log_ctx, AV_LOG_ERROR, |
9710beaf VS |
39 | "cannot create the link %s:%d -> %s:%d\n", |
40 | src->filter->name, srcpad, dst->filter->name, dstpad); | |
27afb09d VS |
41 | return -1; |
42 | } | |
43 | ||
44 | return 0; | |
45 | } | |
46 | ||
27afb09d | 47 | /** |
ffac8784 | 48 | * Parse "[linkname]" |
69fa7e80 VS |
49 | * @param name a pointer (that need to be free'd after use) to the name between |
50 | * parenthesis | |
27afb09d | 51 | */ |
bd80b349 | 52 | static char *parse_link_name(const char **buf, AVClass *log_ctx) |
27afb09d | 53 | { |
22260824 | 54 | const char *start = *buf; |
bd80b349 | 55 | char *name; |
27afb09d VS |
56 | (*buf)++; |
57 | ||
dd04911c | 58 | name = av_get_token(buf, "]"); |
27afb09d | 59 | |
bd80b349 | 60 | if(!name[0]) { |
3a70bb2d | 61 | av_log(log_ctx, AV_LOG_ERROR, |
22260824 | 62 | "Bad (empty?) label found in the following: \"%s\".\n", start); |
27afb09d | 63 | goto fail; |
22260824 | 64 | } |
27afb09d | 65 | |
22260824 | 66 | if(*(*buf)++ != ']') { |
3a70bb2d | 67 | av_log(log_ctx, AV_LOG_ERROR, |
22260824 | 68 | "Mismatched '[' found in the following: \"%s\".\n", start); |
85cb8af7 | 69 | fail: |
bd80b349 | 70 | av_freep(&name); |
22260824 | 71 | } |
bd80b349 VS |
72 | |
73 | return name; | |
27afb09d VS |
74 | } |
75 | ||
8e74c889 | 76 | static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, |
e4a5f397 | 77 | const char *filt_name, const char *args, |
8e74c889 VS |
78 | AVClass *log_ctx) |
79 | { | |
e4a5f397 | 80 | AVFilterContext *filt_ctx; |
8e74c889 | 81 | |
e4a5f397 | 82 | AVFilter *filt; |
8e74c889 VS |
83 | char inst_name[30]; |
84 | ||
85 | snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index); | |
86 | ||
e4a5f397 | 87 | filt = avfilter_get_by_name(filt_name); |
5e600185 | 88 | |
e4a5f397 | 89 | if(!filt) { |
8e74c889 | 90 | av_log(log_ctx, AV_LOG_ERROR, |
e4a5f397 | 91 | "no such filter: '%s'\n", filt_name); |
8e74c889 VS |
92 | return NULL; |
93 | } | |
94 | ||
e4a5f397 SS |
95 | filt_ctx = avfilter_open(filt, inst_name); |
96 | if(!filt_ctx) { | |
8e74c889 | 97 | av_log(log_ctx, AV_LOG_ERROR, |
e4a5f397 | 98 | "error creating filter '%s'\n", filt_name); |
8e74c889 VS |
99 | return NULL; |
100 | } | |
101 | ||
e4a5f397 SS |
102 | if(avfilter_graph_add_filter(ctx, filt_ctx) < 0) { |
103 | avfilter_destroy(filt_ctx); | |
8e74c889 | 104 | return NULL; |
64b164f4 | 105 | } |
8e74c889 | 106 | |
e4a5f397 | 107 | if(avfilter_init_filter(filt_ctx, args, NULL)) { |
8e74c889 | 108 | av_log(log_ctx, AV_LOG_ERROR, |
e4a5f397 | 109 | "error initializing filter '%s' with args '%s'\n", filt_name, args); |
8e74c889 VS |
110 | return NULL; |
111 | } | |
112 | ||
e4a5f397 | 113 | return filt_ctx; |
8e74c889 VS |
114 | } |
115 | ||
f5cbde2e VS |
116 | /** |
117 | * Parse "filter=params" | |
f5cbde2e VS |
118 | */ |
119 | static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, | |
120 | int index, AVClass *log_ctx) | |
121 | { | |
ba3fed2f | 122 | char *opts = NULL; |
15a316c0 | 123 | char *name = av_get_token(buf, "=,;[\n"); |
64b164f4 | 124 | AVFilterContext *ret; |
f5cbde2e | 125 | |
12849837 | 126 | if(**buf == '=') { |
f5cbde2e | 127 | (*buf)++; |
15a316c0 | 128 | opts = av_get_token(buf, "[],;\n"); |
12849837 | 129 | } |
f5cbde2e | 130 | |
64b164f4 VS |
131 | ret = create_filter(graph, index, name, opts, log_ctx); |
132 | av_free(name); | |
133 | av_free(opts); | |
134 | return ret; | |
f5cbde2e VS |
135 | } |
136 | ||
27afb09d VS |
137 | static void free_inout(AVFilterInOut *head) |
138 | { | |
487c0e67 | 139 | while(head) { |
55672c83 | 140 | AVFilterInOut *next = head->next; |
64b164f4 | 141 | av_free(head->name); |
27afb09d VS |
142 | av_free(head); |
143 | head = next; | |
144 | } | |
145 | } | |
146 | ||
c9987633 VS |
147 | static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) |
148 | { | |
149 | AVFilterInOut *ret; | |
c9987633 | 150 | |
fec2e513 | 151 | while(*links && strcmp((*links)->name, label)) |
f6557d5e | 152 | links = &((*links)->next); |
c9987633 | 153 | |
f6557d5e | 154 | ret = *links; |
c9987633 | 155 | |
fec2e513 | 156 | if(ret) |
f6557d5e | 157 | *links = ret->next; |
c9987633 VS |
158 | |
159 | return ret; | |
160 | } | |
161 | ||
e97908ee VS |
162 | static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) |
163 | { | |
164 | element->next = *inouts; | |
165 | *inouts = element; | |
166 | } | |
c9987633 VS |
167 | |
168 | static int link_filter_inouts(AVFilterContext *filter, | |
7f9b3266 SS |
169 | AVFilterInOut **curr_inputs, |
170 | AVFilterInOut **open_inputs, AVClass *log_ctx) | |
c9987633 | 171 | { |
90ed076c | 172 | int pad = filter->input_count; |
c9987633 | 173 | |
4fd9d074 | 174 | while(pad--) { |
7f9b3266 | 175 | AVFilterInOut *p = *curr_inputs; |
c9987633 VS |
176 | if(!p) { |
177 | av_log(log_ctx, AV_LOG_ERROR, | |
178 | "Not enough inputs specified for the \"%s\" filter.\n", | |
089d3714 | 179 | filter->filter->name); |
c9987633 VS |
180 | return -1; |
181 | } | |
182 | ||
7f9b3266 | 183 | *curr_inputs = (*curr_inputs)->next; |
4d11beb2 | 184 | |
c9987633 VS |
185 | if(p->filter) { |
186 | if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx)) | |
187 | return -1; | |
64b164f4 | 188 | av_free(p->name); |
c9987633 VS |
189 | av_free(p); |
190 | } else { | |
c9987633 VS |
191 | p->filter = filter; |
192 | p->pad_idx = pad; | |
7f9b3266 | 193 | insert_inout(open_inputs, p); |
c9987633 VS |
194 | } |
195 | } | |
196 | ||
7f9b3266 | 197 | if(*curr_inputs) { |
c9987633 VS |
198 | av_log(log_ctx, AV_LOG_ERROR, |
199 | "Too many inputs specified for the \"%s\" filter.\n", | |
089d3714 | 200 | filter->filter->name); |
c9987633 VS |
201 | return -1; |
202 | } | |
203 | ||
204 | pad = filter->output_count; | |
4fd9d074 | 205 | while(pad--) { |
c956dd43 | 206 | AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); |
c9987633 VS |
207 | currlinkn->filter = filter; |
208 | currlinkn->pad_idx = pad; | |
7f9b3266 | 209 | insert_inout(curr_inputs, currlinkn); |
c9987633 VS |
210 | } |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
7f9b3266 SS |
215 | static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, |
216 | AVFilterInOut **open_outputs, AVClass *log_ctx) | |
27afb09d | 217 | { |
c9987633 | 218 | int pad = 0; |
c9987633 | 219 | |
487c0e67 | 220 | while(**buf == '[') { |
bd80b349 | 221 | char *name = parse_link_name(buf, log_ctx); |
b2ac16da | 222 | AVFilterInOut *match; |
22260824 | 223 | |
a4501887 | 224 | if(!name) |
22260824 | 225 | return -1; |
22260824 | 226 | |
7f9b3266 SS |
227 | /* First check if the label is not in the open_outputs list */ |
228 | match = extract_inout(name, open_outputs); | |
cf4f7d38 | 229 | |
b2ac16da | 230 | if(match) { |
64fbf5e2 | 231 | av_free(name); |
0de3407b VS |
232 | } else { |
233 | /* Not in the list, so add it as an input */ | |
c880791f VS |
234 | match = av_mallocz(sizeof(AVFilterInOut)); |
235 | match->name = name; | |
c880791f | 236 | match->pad_idx = pad; |
cf4f7d38 | 237 | } |
e97908ee | 238 | |
7f9b3266 | 239 | insert_inout(curr_inputs, match); |
e97908ee | 240 | |
fd548e5b | 241 | *buf += strspn(*buf, WHITESPACES); |
c9987633 | 242 | pad++; |
27afb09d | 243 | } |
cf4f7d38 | 244 | |
27afb09d VS |
245 | return pad; |
246 | } | |
247 | ||
7f9b3266 SS |
248 | static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, |
249 | AVFilterInOut **open_inputs, | |
250 | AVFilterInOut **open_outputs, AVClass *log_ctx) | |
9710beaf | 251 | { |
c9987633 VS |
252 | int pad = 0; |
253 | ||
487c0e67 | 254 | while(**buf == '[') { |
bd80b349 | 255 | char *name = parse_link_name(buf, log_ctx); |
c9987633 VS |
256 | AVFilterInOut *match; |
257 | ||
7f9b3266 SS |
258 | AVFilterInOut *input = *curr_inputs; |
259 | *curr_inputs = (*curr_inputs)->next; | |
443c10ef | 260 | |
c9987633 VS |
261 | if(!name) |
262 | return -1; | |
263 | ||
7f9b3266 SS |
264 | /* First check if the label is not in the open_inputs list */ |
265 | match = extract_inout(name, open_inputs); | |
c9987633 | 266 | |
0de3407b | 267 | if(match) { |
7baa6210 | 268 | if(link_filter(input->filter, input->pad_idx, |
c9987633 VS |
269 | match->filter, match->pad_idx, log_ctx) < 0) |
270 | return -1; | |
64b164f4 VS |
271 | av_free(match->name); |
272 | av_free(name); | |
c9987633 | 273 | av_free(match); |
7baa6210 | 274 | av_free(input); |
0de3407b | 275 | } else { |
7f9b3266 | 276 | /* Not in the list, so add the first input as a open_output */ |
7baa6210 | 277 | input->name = name; |
7f9b3266 | 278 | insert_inout(open_outputs, input); |
c9987633 | 279 | } |
fd548e5b | 280 | *buf += strspn(*buf, WHITESPACES); |
c9987633 | 281 | pad++; |
8095a014 | 282 | } |
9710beaf | 283 | |
c9987633 VS |
284 | return pad; |
285 | } | |
9710beaf | 286 | |
86a47378 | 287 | int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, |
7f9b3266 SS |
288 | AVFilterInOut *open_inputs, |
289 | AVFilterInOut *open_outputs, AVClass *log_ctx) | |
27afb09d | 290 | { |
27afb09d VS |
291 | int index = 0; |
292 | char chr = 0; | |
27afb09d | 293 | |
7f9b3266 | 294 | AVFilterInOut *curr_inputs = NULL; |
27afb09d | 295 | |
27afb09d | 296 | do { |
9710beaf | 297 | AVFilterContext *filter; |
fd548e5b | 298 | filters += strspn(filters, WHITESPACES); |
27afb09d | 299 | |
7f9b3266 | 300 | if(parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx) < 0) |
27afb09d VS |
301 | goto fail; |
302 | ||
5e600185 VS |
303 | filter = parse_filter(&filters, graph, index, log_ctx); |
304 | ||
305 | if(!filter) | |
22260824 VS |
306 | goto fail; |
307 | ||
7f9b3266 | 308 | if(filter->input_count == 1 && !curr_inputs && !index) { |
e916c2ac | 309 | /* First input can be omitted if it is "[in]" */ |
c9987633 | 310 | const char *tmp = "[in]"; |
7f9b3266 | 311 | if(parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx) < 0) |
27afb09d | 312 | goto fail; |
27afb09d VS |
313 | } |
314 | ||
7f9b3266 | 315 | if(link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx) < 0) |
c9987633 | 316 | goto fail; |
da790674 | 317 | |
7f9b3266 | 318 | if(parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, |
e2b5fdaf | 319 | log_ctx) < 0) |
e84f0b62 VS |
320 | goto fail; |
321 | ||
fd548e5b | 322 | filters += strspn(filters, WHITESPACES); |
27afb09d | 323 | chr = *filters++; |
27afb09d | 324 | |
7f9b3266 | 325 | if(chr == ';' && curr_inputs) { |
c9987633 VS |
326 | av_log(log_ctx, AV_LOG_ERROR, |
327 | "Could not find a output to link when parsing \"%s\"\n", | |
328 | filters - 1); | |
329 | goto fail; | |
27afb09d | 330 | } |
c9987633 | 331 | index++; |
487c0e67 | 332 | } while(chr == ',' || chr == ';'); |
27afb09d | 333 | |
fd51ff16 | 334 | if (chr) { |
78471234 SS |
335 | av_log(log_ctx, AV_LOG_ERROR, |
336 | "Unable to parse graph description substring: \"%s\"\n", | |
337 | filters - 1); | |
338 | goto fail; | |
339 | } | |
340 | ||
7f9b3266 | 341 | if(open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { |
e916c2ac | 342 | /* Last output can be omitted if it is "[out]" */ |
c9987633 | 343 | const char *tmp = "[out]"; |
7f9b3266 SS |
344 | if(parse_outputs(&tmp, &curr_inputs, &open_inputs, |
345 | &open_outputs, log_ctx) < 0) | |
27afb09d | 346 | goto fail; |
27afb09d VS |
347 | } |
348 | ||
349 | return 0; | |
350 | ||
351 | fail: | |
d7dbe558 | 352 | avfilter_graph_destroy(graph); |
7f9b3266 SS |
353 | free_inout(open_inputs); |
354 | free_inout(open_outputs); | |
355 | free_inout(curr_inputs); | |
27afb09d VS |
356 | return -1; |
357 | } |