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