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