Use '[' and ']' for label naming
[ffmpeg.git] / libavfilter / graphparser.c
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
26 #include "avfilter.h"
27 #include "avfiltergraph.h"
28
29 /**
30  * For use in av_log
31  */
32 static const char *log_name(void *p)
33 {
34     return "Filter parser";
35 }
36
37 static const AVClass filter_parser_class = {
38     "Filter parser",
39     log_name
40 };
41
42 static const AVClass *log_ctx = &filter_parser_class;
43
44 static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
45                                       char *name, char *args)
46 {
47     AVFilterContext *filt;
48
49     AVFilter *filterdef;
50     char inst_name[30];
51
52     snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
53
54     if(!(filterdef = avfilter_get_by_name(name))) {
55         av_log(&log_ctx, AV_LOG_ERROR,
56                "no such filter: '%s'\n", name);
57         return NULL;
58     }
59
60     if(!(filt = avfilter_open(filterdef, inst_name))) {
61         av_log(&log_ctx, AV_LOG_ERROR,
62                "error creating filter '%s'\n", name);
63         return NULL;
64     }
65
66     if (avfilter_graph_add_filter(ctx, filt) < 0)
67         return NULL;
68
69     if(avfilter_init_filter(filt, args, NULL)) {
70         av_log(&log_ctx, AV_LOG_ERROR,
71                "error initializing filter '%s' with args '%s'\n", name, args);
72         return NULL;
73     }
74
75     return filt;
76 }
77
78 static int link_filter(AVFilterContext *src, int srcpad,
79                        AVFilterContext *dst, int dstpad)
80 {
81     if(avfilter_link(src, srcpad, dst, dstpad)) {
82         av_log(&log_ctx, AV_LOG_ERROR,
83                "cannot create the link %s:%d -> %s:%d\n",
84                src->filter->name, srcpad, dst->filter->name, dstpad);
85         return -1;
86     }
87
88     return 0;
89 }
90
91 static void consume_whitespace(const char **buf)
92 {
93     *buf += strspn(*buf, " \n\t");
94 }
95
96 /**
97  * Consumes a string from *buf.
98  * @return a copy of the consumed string, which should be free'd after use
99  */
100 static char *consume_string(const char **buf)
101 {
102     char *out = av_malloc(strlen(*buf));
103     const char *in = *buf;
104     char *ret = out;
105
106     consume_whitespace(buf);
107
108     do{
109         char c = *in++;
110         switch (c) {
111         case '\\':
112             *out++= *in++;
113             break;
114         case '\'':
115             while(*in && *in != '\'')
116                 *out++= *in++;
117             if(*in) in++;
118             break;
119         case 0:
120         case ']':
121         case '[':
122         case '=':
123         case ',':
124             *out++= 0;
125             break;
126         default:
127             *out++= c;
128         }
129     } while(out[-1]);
130
131     *buf = in-1;
132     return ret;
133 }
134
135 /**
136  * Parse "(linkname)"
137  * @arg name a pointer (that need to be free'd after use) to the name between
138  *           parenthesis
139  */
140 static void parse_link_name(const char **buf, char **name)
141 {
142     (*buf)++;
143
144     *name = consume_string(buf);
145
146     if (!*name[0])
147         goto fail;
148
149     if (*(*buf)++ != ']')
150         goto fail;
151
152     return;
153  fail:
154     av_freep(name);
155     av_log(&log_ctx, AV_LOG_ERROR, "Could not parse link name!\n");
156 }
157
158 /**
159  * Parse "filter=params"
160  * @arg name a pointer (that need to be free'd after use) to the name of the
161  *           filter
162  * @arg ars  a pointer (that need to be free'd after use) to the args of the
163  *           filter
164  */
165 static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, int index)
166 {
167     char *name, *opts;
168     name = consume_string(buf);
169
170     if (**buf == '=') {
171         (*buf)++;
172         opts = consume_string(buf);
173     } else {
174         opts = NULL;
175     }
176
177     return create_filter(graph, index, name, opts);
178 }
179
180 enum LinkType {
181     LinkTypeIn,
182     LinkTypeOut,
183 };
184
185 /**
186  * A linked-list of the inputs/outputs of the filter chain.
187  */
188 typedef struct AVFilterInOut {
189     enum LinkType type;
190     char *name;
191     AVFilterContext *filter;
192     int pad_idx;
193
194     struct AVFilterInOut *next;
195 } AVFilterInOut;
196
197 static void free_inout(AVFilterInOut *head)
198 {
199     while (head) {
200         AVFilterInOut *next;
201         next = head->next;
202         av_free(head);
203         head = next;
204     }
205 }
206
207 /**
208  * Parse "(a1)(link2) ... (etc)"
209  */
210 static int parse_inouts(const char **buf, AVFilterInOut **inout, int firstpad,
211                         enum LinkType type, AVFilterContext *filter)
212 {
213     int pad = firstpad;
214     while (**buf == '[') {
215         AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
216         parse_link_name(buf, &inoutn->name);
217         inoutn->type = type;
218         inoutn->filter = filter;
219         inoutn->pad_idx = pad++;
220         inoutn->next = *inout;
221         *inout = inoutn;
222     }
223     return pad;
224 }
225
226 static const char *skip_inouts(const char *buf)
227 {
228     while (*buf == '[') {
229         buf += strcspn(buf, "]");
230         buf++;
231     }
232     return buf;
233 }
234
235
236 /**
237  * Parse a string describing a filter graph.
238  */
239 int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
240                          AVFilterContext *in, int inpad,
241                          AVFilterContext *out, int outpad)
242 {
243     AVFilterInOut *inout=NULL;
244     AVFilterInOut  *head=NULL;
245
246     int index = 0;
247     char chr = 0;
248     int pad = 0;
249     int has_out = 0;
250
251     AVFilterContext *last_filt = NULL;
252
253     consume_whitespace(&filters);
254
255     do {
256         AVFilterContext *filter;
257         int oldpad = pad;
258         const char *inouts = filters;
259
260         // We need to parse the inputs of the filter after we create it, so
261         // skip it by now
262         filters = skip_inouts(filters);
263
264         if (!(filter = parse_filter(&filters, graph, index)))
265             goto fail;
266
267         pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter);
268
269         // If the first filter has an input and none was given, it is
270         // implicitly the input of the whole graph.
271         if (pad == 0 && filter->input_count == 1) {
272             if(link_filter(in, inpad, filter, 0))
273                 goto fail;
274         }
275
276         if(chr == ',') {
277             if (link_filter(last_filt, oldpad, filter, 0) < 0)
278                 goto fail;
279         }
280
281         pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter);
282         chr = *filters++;
283         index++;
284         last_filt = filter;
285     } while (chr == ',' || chr == ';');
286
287     head = inout;
288     for (; inout != NULL; inout = inout->next) {
289         if (inout->filter == NULL)
290             continue; // Already processed
291
292         if (!strcmp(inout->name, "in")) {
293             if(link_filter(in, inpad, inout->filter, inout->pad_idx))
294                 goto fail;
295
296         } else if (!strcmp(inout->name, "out")) {
297             has_out = 1;
298
299             if(link_filter(inout->filter, inout->pad_idx, out, outpad))
300                 goto fail;
301
302         } else {
303             AVFilterInOut *p, *src, *dst;
304             for (p = inout->next;
305                  p && strcmp(p->name,inout->name); p = p->next);
306
307             if (!p) {
308                 av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n",
309                        inout->name);
310                 goto fail;
311             }
312
313             if (p->type == LinkTypeIn && inout->type == LinkTypeOut) {
314                 src = inout;
315                 dst = p;
316             } else if (p->type == LinkTypeOut && inout->type == LinkTypeIn) {
317                 src = p;
318                 dst = inout;
319             } else {
320                 av_log(&log_ctx, AV_LOG_ERROR, "Two links named '%s' are either both input or both output\n",
321                        inout->name);
322                 goto fail;
323             }
324
325             if (link_filter(src->filter, src->pad_idx, dst->filter, dst->pad_idx) < 0)
326                 goto fail;
327
328             src->filter = NULL;
329             dst->filter = NULL;
330         }
331     }
332
333     free_inout(head);
334
335     if (!has_out) {
336         if(link_filter(last_filt, pad, out, outpad))
337             goto fail;
338     }
339
340     return 0;
341
342  fail:
343     free_inout(head);
344     avfilter_destroy_graph(graph);
345     return -1;
346 }