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