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