Simplify
[libav.git] / libavfilter / graphparser.c
CommitLineData
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"
29
9710beaf 30static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
3a70bb2d
VS
31 const char *name, const char *args,
32 AVClass *log_ctx)
27afb09d
VS
33{
34 AVFilterContext *filt;
35
36 AVFilter *filterdef;
58a2d7a5 37 char inst_name[30];
27afb09d 38
58a2d7a5 39 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
27a669ab
VS
40
41 if(!(filterdef = avfilter_get_by_name(name))) {
dd8e311e 42 av_log(log_ctx, AV_LOG_ERROR,
27a669ab 43 "no such filter: '%s'\n", name);
9710beaf 44 return NULL;
27a669ab
VS
45 }
46
58a2d7a5 47 if(!(filt = avfilter_open(filterdef, inst_name))) {
3a70bb2d 48 av_log(log_ctx, AV_LOG_ERROR,
27afb09d 49 "error creating filter '%s'\n", name);
9710beaf 50 return NULL;
27afb09d 51 }
046f49ba 52
2839ff5e 53 if(avfilter_graph_add_filter(ctx, filt) < 0)
9710beaf 54 return NULL;
046f49ba 55
27afb09d 56 if(avfilter_init_filter(filt, args, NULL)) {
3a70bb2d 57 av_log(log_ctx, AV_LOG_ERROR,
27a669ab 58 "error initializing filter '%s' with args '%s'\n", name, args);
9710beaf 59 return NULL;
27afb09d
VS
60 }
61
9710beaf 62 return filt;
27afb09d
VS
63}
64
9710beaf 65static int link_filter(AVFilterContext *src, int srcpad,
3a70bb2d
VS
66 AVFilterContext *dst, int dstpad,
67 AVClass *log_ctx)
27afb09d 68{
9710beaf 69 if(avfilter_link(src, srcpad, dst, dstpad)) {
3a70bb2d 70 av_log(log_ctx, AV_LOG_ERROR,
9710beaf
VS
71 "cannot create the link %s:%d -> %s:%d\n",
72 src->filter->name, srcpad, dst->filter->name, dstpad);
27afb09d
VS
73 return -1;
74 }
75
76 return 0;
77}
78
79static void consume_whitespace(const char **buf)
80{
81 *buf += strspn(*buf, " \n\t");
82}
83
84/**
27afb09d
VS
85 * Consumes a string from *buf.
86 * @return a copy of the consumed string, which should be free'd after use
87 */
88static char *consume_string(const char **buf)
89{
2255026d 90 char *out = av_malloc(strlen(*buf) + 1);
93b7a0f6 91 char *ret = out;
27afb09d
VS
92
93 consume_whitespace(buf);
94
93b7a0f6 95 do{
a64821f4 96 char c = *(*buf)++;
93b7a0f6
VS
97 switch (c) {
98 case '\\':
d835680e 99 *out++ = *(*buf)++;
27afb09d 100 break;
93b7a0f6 101 case '\'':
a64821f4 102 while(**buf && **buf != '\'')
d835680e 103 *out++ = *(*buf)++;
a64821f4 104 if(**buf) (*buf)++;
93b7a0f6
VS
105 break;
106 case 0:
d7ff2297
VS
107 case ']':
108 case '[':
93b7a0f6
VS
109 case '=':
110 case ',':
dbec351d 111 case ';':
a78f2ccd
VS
112 case ' ':
113 case '\n':
d835680e 114 *out++ = 0;
93b7a0f6
VS
115 break;
116 default:
d835680e 117 *out++ = c;
93b7a0f6
VS
118 }
119 } while(out[-1]);
27afb09d 120
a64821f4 121 (*buf)--;
a78f2ccd
VS
122 consume_whitespace(buf);
123
27afb09d
VS
124 return ret;
125}
126
127/**
ffac8784 128 * Parse "[linkname]"
27afb09d
VS
129 * @arg name a pointer (that need to be free'd after use) to the name between
130 * parenthesis
131 */
3a70bb2d 132static void parse_link_name(const char **buf, char **name, AVClass *log_ctx)
27afb09d 133{
22260824 134 const char *start = *buf;
27afb09d
VS
135 (*buf)++;
136
137 *name = consume_string(buf);
138
22260824 139 if(!*name[0]) {
3a70bb2d 140 av_log(log_ctx, AV_LOG_ERROR,
22260824 141 "Bad (empty?) label found in the following: \"%s\".\n", start);
27afb09d 142 goto fail;
22260824 143 }
27afb09d 144
22260824 145 if(*(*buf)++ != ']') {
3a70bb2d 146 av_log(log_ctx, AV_LOG_ERROR,
22260824 147 "Mismatched '[' found in the following: \"%s\".\n", start);
85cb8af7
VS
148 fail:
149 av_freep(name);
22260824 150 }
27afb09d
VS
151}
152
27afb09d
VS
153static void free_inout(AVFilterInOut *head)
154{
487c0e67 155 while(head) {
55672c83 156 AVFilterInOut *next = head->next;
27afb09d
VS
157 av_free(head);
158 head = next;
159 }
160}
161
c9987633
VS
162static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
163{
164 AVFilterInOut *ret;
c9987633 165
fec2e513 166 while(*links && strcmp((*links)->name, label))
f6557d5e 167 links = &((*links)->next);
c9987633 168
f6557d5e 169 ret = *links;
c9987633 170
fec2e513 171 if(ret)
f6557d5e 172 *links = ret->next;
c9987633
VS
173
174 return ret;
175}
176
177
178static int link_filter_inouts(AVFilterContext *filter,
179 AVFilterInOut **currInputs,
180 AVFilterInOut **openLinks, AVClass *log_ctx)
181{
c9987633
VS
182 int pad = 0;
183
184 pad = filter->input_count;
4fd9d074 185 while(pad--) {
d835680e 186 AVFilterInOut *p = *currInputs;
eee68d96 187 *currInputs = (*currInputs)->next;
c9987633
VS
188 if(!p) {
189 av_log(log_ctx, AV_LOG_ERROR,
190 "Not enough inputs specified for the \"%s\" filter.\n",
191 filter->name);
192 return -1;
193 }
194
195 if(p->filter) {
196 if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx))
197 return -1;
c9987633
VS
198 av_free(p);
199 } else {
c9987633
VS
200 p->filter = filter;
201 p->pad_idx = pad;
202 p->next = *openLinks;
203 *openLinks = p;
204 }
205 }
206
207
208 if(*currInputs) {
209 av_log(log_ctx, AV_LOG_ERROR,
210 "Too many inputs specified for the \"%s\" filter.\n",
211 filter->name);
212 return -1;
213 }
214
215 pad = filter->output_count;
4fd9d074 216 while(pad--) {
c9987633 217 AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));
c9987633
VS
218 currlinkn->name = NULL;
219 currlinkn->type = LinkTypeOut;
220 currlinkn->filter = filter;
221 currlinkn->pad_idx = pad;
222 currlinkn->next = *currInputs;
223 *currInputs = currlinkn;
224 }
225
226 return 0;
227}
228
27afb09d 229/**
c9987633
VS
230 * Parse "filter=params"
231 * @arg name a pointer (that need to be free'd after use) to the name of the
232 * filter
233 * @arg ars a pointer (that need to be free'd after use) to the args of the
234 * filter
27afb09d 235 */
c9987633
VS
236static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph,
237 int index, AVClass *log_ctx)
238{
239 char *opts;
240 char *name = consume_string(buf);
241
242 if(**buf == '=') {
243 (*buf)++;
244 opts = consume_string(buf);
245 } else {
246 opts = NULL;
247 }
248
249 return create_filter(graph, index, name, opts, log_ctx);
250}
251
252static int parse_inputs(const char **buf, AVFilterInOut **currInputs,
253 AVFilterInOut **openLinks, AVClass *log_ctx)
27afb09d 254{
c9987633 255 int pad = 0;
c9987633 256
487c0e67 257 while(**buf == '[') {
2b7defc7 258 char *name;
443c10ef 259 AVFilterInOut *link_to_add;
b2ac16da 260 AVFilterInOut *match;
22260824 261
3a70bb2d 262 parse_link_name(buf, &name, log_ctx);
2b7defc7 263
a4501887 264 if(!name)
22260824 265 return -1;
22260824 266
c9987633 267 /* First check if the label is not in the openLinks list */
b2ac16da 268 match = extract_inout(name, openLinks);
cf4f7d38 269
b2ac16da 270 if(match) {
c9987633
VS
271 /* A label of a open link. Make it one of the inputs of the next
272 filter */
d9d086d9 273 if(match->type != LinkTypeOut) {
cf4f7d38 274 av_log(log_ctx, AV_LOG_ERROR,
b2ac16da 275 "Label \"%s\" appears twice as input!\n", match->name);
cf4f7d38
VS
276 return -1;
277 }
443c10ef
VS
278
279 link_to_add = match;
0de3407b
VS
280 } else {
281 /* Not in the list, so add it as an input */
443c10ef
VS
282 link_to_add = av_malloc(sizeof(AVFilterInOut));
283
284 link_to_add->name = name;
285 link_to_add->type = LinkTypeIn;
286 link_to_add->filter = NULL;
287 link_to_add->pad_idx = pad;
cf4f7d38 288 }
443c10ef
VS
289 link_to_add->next = *currInputs;
290 *currInputs = link_to_add;
8095a014 291 consume_whitespace(buf);
c9987633 292 pad++;
27afb09d 293 }
cf4f7d38 294
27afb09d
VS
295 return pad;
296}
297
c9987633
VS
298static int parse_outputs(const char **buf, AVFilterInOut **currInputs,
299 AVFilterInOut **openLinks, AVClass *log_ctx)
9710beaf 300{
c9987633
VS
301 int pad = 0;
302
487c0e67 303 while(**buf == '[') {
c9987633
VS
304 char *name;
305 AVFilterInOut *match;
306
443c10ef
VS
307 AVFilterInOut *input = *currInputs;
308 *currInputs = (*currInputs)->next;
309
c9987633
VS
310 parse_link_name(buf, &name, log_ctx);
311
312 if(!name)
313 return -1;
314
315 /* First check if the label is not in the openLinks list */
316 match = extract_inout(name, openLinks);
317
0de3407b 318 if(match) {
c9987633 319 /* A label of a open link. Link it. */
d9d086d9 320 if(match->type != LinkTypeIn) {
c9987633
VS
321 av_log(log_ctx, AV_LOG_ERROR,
322 "Label \"%s\" appears twice as output!\n", match->name);
323 return -1;
324 }
325
7baa6210 326 if(link_filter(input->filter, input->pad_idx,
c9987633
VS
327 match->filter, match->pad_idx, log_ctx) < 0)
328 return -1;
329 av_free(match);
7baa6210 330 av_free(input);
0de3407b
VS
331 } else {
332 /* Not in the list, so add the first input as a openLink */
7baa6210
VS
333 input->next = *openLinks;
334 input->type = LinkTypeOut;
335 input->name = name;
336 *openLinks = input;
c9987633
VS
337 }
338 consume_whitespace(buf);
339 pad++;
8095a014 340 }
9710beaf 341
c9987633
VS
342 return pad;
343}
9710beaf 344
27afb09d
VS
345/**
346 * Parse a string describing a filter graph.
347 */
92973a04 348int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
74c2f1fb 349 AVFilterInOut *openLinks, AVClass *log_ctx)
27afb09d 350{
27afb09d
VS
351 int index = 0;
352 char chr = 0;
353 int pad = 0;
27afb09d 354
d835680e 355 AVFilterInOut *currInputs = NULL;
27afb09d 356
27afb09d 357 do {
9710beaf 358 AVFilterContext *filter;
7a57c8d9 359 consume_whitespace(&filters);
27afb09d 360
c9987633 361 pad = parse_inputs(&filters, &currInputs, &openLinks, log_ctx);
27afb09d 362
c9987633 363 if(pad < 0)
27afb09d
VS
364 goto fail;
365
c9987633 366 if(!(filter = parse_filter(&filters, graph, index, log_ctx)))
22260824
VS
367 goto fail;
368
c9987633
VS
369 if(filter->input_count == 1 && !currInputs && !index) {
370 // First input can be ommitted if it is "[in]"
371 const char *tmp = "[in]";
372 pad = parse_inputs(&tmp, &currInputs, &openLinks, log_ctx);
d9d086d9 373 if(pad < 0)
27afb09d 374 goto fail;
27afb09d
VS
375 }
376
c9987633
VS
377 if(link_filter_inouts(filter, &currInputs, &openLinks, log_ctx) < 0)
378 goto fail;
da790674 379
c9987633 380 pad = parse_outputs(&filters, &currInputs, &openLinks, log_ctx);
7a57c8d9 381
c9987633 382 if(pad < 0)
e84f0b62
VS
383 goto fail;
384
7a57c8d9 385 consume_whitespace(&filters);
27afb09d 386 chr = *filters++;
27afb09d 387
d9d086d9 388 if(chr == ';' && currInputs) {
c9987633
VS
389 av_log(log_ctx, AV_LOG_ERROR,
390 "Could not find a output to link when parsing \"%s\"\n",
391 filters - 1);
392 goto fail;
27afb09d 393 }
c9987633 394 index++;
487c0e67 395 } while(chr == ',' || chr == ';');
27afb09d 396
c9987633
VS
397 if(openLinks && !strcmp(openLinks->name, "out") && currInputs) {
398 // Last output can be ommitted if it is "[out]"
399 const char *tmp = "[out]";
400 if(parse_outputs(&tmp, &currInputs, &openLinks, log_ctx) < 0)
27afb09d 401 goto fail;
27afb09d
VS
402 }
403
404 return 0;
405
406 fail:
27afb09d 407 avfilter_destroy_graph(graph);
c9987633
VS
408 free_inout(openLinks);
409 free_inout(currInputs);
27afb09d
VS
410 return -1;
411}