Cosmetics: more function reordering
[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 int link_filter(AVFilterContext *src, int srcpad,
3a70bb2d
VS
31 AVFilterContext *dst, int dstpad,
32 AVClass *log_ctx)
27afb09d 33{
9710beaf 34 if(avfilter_link(src, srcpad, dst, dstpad)) {
3a70bb2d 35 av_log(log_ctx, AV_LOG_ERROR,
9710beaf
VS
36 "cannot create the link %s:%d -> %s:%d\n",
37 src->filter->name, srcpad, dst->filter->name, dstpad);
27afb09d
VS
38 return -1;
39 }
40
41 return 0;
42}
43
d424e138 44static int consume_whitespace(const char *buf)
27afb09d 45{
d424e138 46 return strspn(buf, " \n\t");
27afb09d
VS
47}
48
49/**
27afb09d
VS
50 * Consumes a string from *buf.
51 * @return a copy of the consumed string, which should be free'd after use
52 */
53static char *consume_string(const char **buf)
54{
2255026d 55 char *out = av_malloc(strlen(*buf) + 1);
93b7a0f6 56 char *ret = out;
27afb09d 57
d424e138 58 *buf += consume_whitespace(*buf);
27afb09d 59
93b7a0f6 60 do{
a64821f4 61 char c = *(*buf)++;
93b7a0f6
VS
62 switch (c) {
63 case '\\':
d835680e 64 *out++ = *(*buf)++;
27afb09d 65 break;
93b7a0f6 66 case '\'':
a64821f4 67 while(**buf && **buf != '\'')
d835680e 68 *out++ = *(*buf)++;
a64821f4 69 if(**buf) (*buf)++;
93b7a0f6
VS
70 break;
71 case 0:
d7ff2297
VS
72 case ']':
73 case '[':
93b7a0f6
VS
74 case '=':
75 case ',':
dbec351d 76 case ';':
a78f2ccd
VS
77 case ' ':
78 case '\n':
d835680e 79 *out++ = 0;
93b7a0f6
VS
80 break;
81 default:
d835680e 82 *out++ = c;
93b7a0f6
VS
83 }
84 } while(out[-1]);
27afb09d 85
a64821f4 86 (*buf)--;
d424e138 87 *buf += consume_whitespace(*buf);
a78f2ccd 88
27afb09d
VS
89 return ret;
90}
91
92/**
ffac8784 93 * Parse "[linkname]"
27afb09d
VS
94 * @arg name a pointer (that need to be free'd after use) to the name between
95 * parenthesis
96 */
3a70bb2d 97static void parse_link_name(const char **buf, char **name, AVClass *log_ctx)
27afb09d 98{
22260824 99 const char *start = *buf;
27afb09d
VS
100 (*buf)++;
101
102 *name = consume_string(buf);
103
22260824 104 if(!*name[0]) {
3a70bb2d 105 av_log(log_ctx, AV_LOG_ERROR,
22260824 106 "Bad (empty?) label found in the following: \"%s\".\n", start);
27afb09d 107 goto fail;
22260824 108 }
27afb09d 109
22260824 110 if(*(*buf)++ != ']') {
3a70bb2d 111 av_log(log_ctx, AV_LOG_ERROR,
22260824 112 "Mismatched '[' found in the following: \"%s\".\n", start);
85cb8af7
VS
113 fail:
114 av_freep(name);
22260824 115 }
27afb09d
VS
116}
117
8e74c889
VS
118static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
119 const char *name, const char *args,
120 AVClass *log_ctx)
121{
122 AVFilterContext *filt;
123
124 AVFilter *filterdef;
125 char inst_name[30];
126
127 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
128
129 if(!(filterdef = avfilter_get_by_name(name))) {
130 av_log(log_ctx, AV_LOG_ERROR,
131 "no such filter: '%s'\n", name);
132 return NULL;
133 }
134
135 if(!(filt = avfilter_open(filterdef, inst_name))) {
136 av_log(log_ctx, AV_LOG_ERROR,
137 "error creating filter '%s'\n", name);
138 return NULL;
139 }
140
141 if(avfilter_graph_add_filter(ctx, filt) < 0)
142 return NULL;
143
144 if(avfilter_init_filter(filt, args, NULL)) {
145 av_log(log_ctx, AV_LOG_ERROR,
146 "error initializing filter '%s' with args '%s'\n", name, args);
147 return NULL;
148 }
149
150 return filt;
151}
152
f5cbde2e
VS
153/**
154 * Parse "filter=params"
155 * @arg name a pointer (that need to be free'd after use) to the name of the
156 * filter
157 * @arg ars a pointer (that need to be free'd after use) to the args of the
158 * filter
159 */
160static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph,
161 int index, AVClass *log_ctx)
162{
163 char *opts;
164 char *name = consume_string(buf);
165
166 if(**buf == '=') {
167 (*buf)++;
168 opts = consume_string(buf);
169 } else {
170 opts = NULL;
171 }
172
173 return create_filter(graph, index, name, opts, log_ctx);
174}
175
27afb09d
VS
176static void free_inout(AVFilterInOut *head)
177{
487c0e67 178 while(head) {
55672c83 179 AVFilterInOut *next = head->next;
27afb09d
VS
180 av_free(head);
181 head = next;
182 }
183}
184
c9987633
VS
185static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
186{
187 AVFilterInOut *ret;
c9987633 188
fec2e513 189 while(*links && strcmp((*links)->name, label))
f6557d5e 190 links = &((*links)->next);
c9987633 191
f6557d5e 192 ret = *links;
c9987633 193
fec2e513 194 if(ret)
f6557d5e 195 *links = ret->next;
c9987633
VS
196
197 return ret;
198}
199
200
201static int link_filter_inouts(AVFilterContext *filter,
202 AVFilterInOut **currInputs,
203 AVFilterInOut **openLinks, AVClass *log_ctx)
204{
c9987633
VS
205 int pad = 0;
206
207 pad = filter->input_count;
4fd9d074 208 while(pad--) {
d835680e 209 AVFilterInOut *p = *currInputs;
eee68d96 210 *currInputs = (*currInputs)->next;
c9987633
VS
211 if(!p) {
212 av_log(log_ctx, AV_LOG_ERROR,
213 "Not enough inputs specified for the \"%s\" filter.\n",
214 filter->name);
215 return -1;
216 }
217
218 if(p->filter) {
219 if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx))
220 return -1;
c9987633
VS
221 av_free(p);
222 } else {
c9987633
VS
223 p->filter = filter;
224 p->pad_idx = pad;
225 p->next = *openLinks;
226 *openLinks = p;
227 }
228 }
229
230
231 if(*currInputs) {
232 av_log(log_ctx, AV_LOG_ERROR,
233 "Too many inputs specified for the \"%s\" filter.\n",
234 filter->name);
235 return -1;
236 }
237
238 pad = filter->output_count;
4fd9d074 239 while(pad--) {
c9987633 240 AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));
c9987633
VS
241 currlinkn->name = NULL;
242 currlinkn->type = LinkTypeOut;
243 currlinkn->filter = filter;
244 currlinkn->pad_idx = pad;
245 currlinkn->next = *currInputs;
246 *currInputs = currlinkn;
247 }
248
249 return 0;
250}
251
c9987633
VS
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;
d424e138 291 *buf += 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 337 }
d424e138 338 *buf += consume_whitespace(*buf);
c9987633 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;
d424e138 359 filters += 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
d424e138 385 filters += 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}