d0c678966fd0e60bc4a6e74fd46340930a2c6f47
[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                                       const char *name, const 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) + 1);
103     char *ret = out;
104
105     consume_whitespace(buf);
106
107     do{
108         char c = *(*buf)++;
109         switch (c) {
110         case '\\':
111             *out++= *(*buf)++;
112             break;
113         case '\'':
114             while(**buf && **buf != '\'')
115                 *out++= *(*buf)++;
116             if(**buf) (*buf)++;
117             break;
118         case 0:
119         case ']':
120         case '[':
121         case '=':
122         case ',':
123         case ' ':
124         case '\n':
125             *out++= 0;
126             break;
127         default:
128             *out++= c;
129         }
130     } while(out[-1]);
131
132     (*buf)--;
133     consume_whitespace(buf);
134
135     return ret;
136 }
137
138 /**
139  * Parse "[linkname]"
140  * @arg name a pointer (that need to be free'd after use) to the name between
141  *           parenthesis
142  */
143 static void parse_link_name(const char **buf, char **name)
144 {
145     const char *start = *buf;
146     (*buf)++;
147
148     *name = consume_string(buf);
149
150     if(!*name[0]) {
151         av_log(&log_ctx, AV_LOG_ERROR,
152                "Bad (empty?) label found in the following: \"%s\".\n", start);
153         goto fail;
154     }
155
156     if(*(*buf)++ != ']') {
157         av_log(&log_ctx, AV_LOG_ERROR,
158                "Mismatched '[' found in the following: \"%s\".\n", start);
159         goto fail;
160     }
161
162     return;
163
164  fail:
165     av_freep(name);
166 }
167
168 /**
169  * Parse "filter=params"
170  * @arg name a pointer (that need to be free'd after use) to the name of the
171  *           filter
172  * @arg ars  a pointer (that need to be free'd after use) to the args of the
173  *           filter
174  */
175 static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, int index)
176 {
177     char *name, *opts;
178     name = consume_string(buf);
179
180     if(**buf == '=') {
181         (*buf)++;
182         opts = consume_string(buf);
183     } else {
184         opts = NULL;
185     }
186
187     return create_filter(graph, index, name, opts);
188 }
189
190 enum LinkType {
191     LinkTypeIn,
192     LinkTypeOut,
193 };
194
195 /**
196  * A linked-list of the inputs/outputs of the filter chain.
197  */
198 typedef struct AVFilterInOut {
199     enum LinkType type;
200     char *name;
201     AVFilterContext *filter;
202     int pad_idx;
203
204     struct AVFilterInOut *next;
205 } AVFilterInOut;
206
207 static void free_inout(AVFilterInOut *head)
208 {
209     while (head) {
210         AVFilterInOut *next = head->next;
211         av_free(head);
212         head = next;
213     }
214 }
215
216 /**
217  * Process a link. This funcion looks for a matching label in the *inout
218  * linked list. If none is found, it adds this link to the list.
219  */
220 static int handle_link(char *name, AVFilterInOut **inout, int pad,
221                        enum LinkType type, AVFilterContext *filter)
222 {
223     AVFilterInOut *p = *inout;
224
225     for (; p && strcmp(p->name, name); p = p->next);
226
227     if(!p) {
228         // First label apearence, add it to the linked list
229         AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
230
231         inoutn->name    = name;
232         inoutn->type    = type;
233         inoutn->filter  = filter;
234         inoutn->pad_idx = pad;
235         inoutn->next    = *inout;
236         *inout = inoutn;
237          return 0;
238     }
239
240     if(p->type == LinkTypeIn && type == LinkTypeOut) {
241         if(link_filter(filter, pad, p->filter, p->pad_idx) < 0)
242             goto fail;
243     } else if(p->type == LinkTypeOut && type == LinkTypeIn) {
244         if(link_filter(p->filter, p->pad_idx, filter, pad) < 0)
245             goto fail;
246     } else {
247         av_log(&log_ctx, AV_LOG_ERROR,
248                "Two links named '%s' are either both input or both output\n",
249                name);
250         goto fail;
251     }
252
253     p->filter = NULL;
254
255     return 0;
256  fail:
257     return -1;
258 }
259
260
261 /**
262  * Parse "[a1][link2] ... [etc]"
263  */
264 static int parse_inouts(const char **buf, AVFilterInOut **inout, int pad,
265                         enum LinkType type, AVFilterContext *filter)
266 {
267     while (**buf == '[') {
268         char *name;
269
270         parse_link_name(buf, &name);
271
272         if (!name)
273             return -1;
274
275         handle_link(name, inout, pad++, type, filter);
276         consume_whitespace(buf);
277     }
278     return pad;
279 }
280
281 static const char *skip_inouts(const char *buf)
282 {
283     while (*buf == '[') {
284         buf += strcspn(buf, "]") + 1;
285         consume_whitespace(&buf);
286     }
287     return buf;
288 }
289
290
291 /**
292  * Parse a string describing a filter graph.
293  */
294 int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
295                          AVFilterContext *in, int inpad,
296                          AVFilterContext *out, int outpad)
297 {
298     AVFilterInOut *inout=NULL;
299     AVFilterInOut  *head=NULL;
300
301     int index = 0;
302     char chr = 0;
303     int pad = 0;
304     int has_out = 0;
305
306     AVFilterContext *last_filt = NULL;
307
308     do {
309         AVFilterContext *filter;
310         int oldpad = pad;
311         const char *inouts;
312
313         consume_whitespace(&filters);
314         inouts = filters;
315
316         // We need to parse the inputs of the filter after we create it, so
317         // skip it by now
318         filters = skip_inouts(filters);
319
320         if(!(filter = parse_filter(&filters, graph, index)))
321             goto fail;
322
323         pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter);
324
325         if(pad < 0)
326             goto fail;
327
328         // If the first filter has an input and none was given, it is
329         // implicitly the input of the whole graph.
330         if(pad == 0 && filter->input_count == 1) {
331             if(link_filter(in, inpad, filter, 0))
332                 goto fail;
333         }
334
335         if(chr == ',') {
336             if(link_filter(last_filt, oldpad, filter, 0) < 0)
337                 goto fail;
338         }
339
340         pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter);
341
342         consume_whitespace(&filters);
343
344         chr = *filters++;
345         index++;
346         last_filt = filter;
347     } while (chr == ',' || chr == ';');
348
349     head = inout;
350     // Process remaining labels. Only inputs and outputs should be left.
351     for (; inout; inout = inout->next) {
352         if(!inout->filter)
353             continue; // Already processed
354
355         if(!strcmp(inout->name, "in")) {
356             if(link_filter(in, inpad, inout->filter, inout->pad_idx))
357                 goto fail;
358
359         } else if(!strcmp(inout->name, "out")) {
360             has_out = 1;
361
362             if(link_filter(inout->filter, inout->pad_idx, out, outpad))
363                 goto fail;
364
365         } else {
366             av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n",
367                    inout->name);
368                 goto fail;
369         }
370     }
371
372     free_inout(head);
373
374     if(!has_out) {
375         if(link_filter(last_filt, pad, out, outpad))
376             goto fail;
377     }
378
379     return 0;
380
381  fail:
382     free_inout(head);
383     avfilter_destroy_graph(graph);
384     return -1;
385 }