3 * copyright (c) 2008 Vitor Sessak
4 * copyright (c) 2007 Bobby Bingham
6 * This file is part of FFmpeg.
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.
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.
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
26 #include "graphparser.h"
28 #include "avfiltergraph.h"
30 static int link_filter(AVFilterContext
*src
, int srcpad
,
31 AVFilterContext
*dst
, int dstpad
,
34 if(avfilter_link(src
, srcpad
, dst
, dstpad
)) {
35 av_log(log_ctx
, AV_LOG_ERROR
,
36 "cannot create the link %s:%d -> %s:%d\n",
37 src
->filter
->name
, srcpad
, dst
->filter
->name
, dstpad
);
44 static int consume_whitespace(const char *buf
)
46 return strspn(buf
, " \n\t");
50 * Consumes a string from *buf.
51 * @return a copy of the consumed string, which should be free'd after use
53 static char *consume_string(const char **buf
)
55 char *out
= av_malloc(strlen(*buf
) + 1);
58 *buf
+= consume_whitespace(*buf
);
67 while(**buf
&& **buf
!= '\'')
87 *buf
+= consume_whitespace(*buf
);
94 * @param name a pointer (that need to be free'd after use) to the name between
97 static char *parse_link_name(const char **buf
, AVClass
*log_ctx
)
99 const char *start
= *buf
;
103 name
= consume_string(buf
);
106 av_log(log_ctx
, AV_LOG_ERROR
,
107 "Bad (empty?) label found in the following: \"%s\".\n", start
);
111 if(*(*buf
)++ != ']') {
112 av_log(log_ctx
, AV_LOG_ERROR
,
113 "Mismatched '[' found in the following: \"%s\".\n", start
);
121 static AVFilterContext
*create_filter(AVFilterGraph
*ctx
, int index
,
122 const char *name
, const char *args
,
125 AVFilterContext
*filt
;
130 snprintf(inst_name
, sizeof(inst_name
), "Parsed filter %d", index
);
132 filterdef
= avfilter_get_by_name(name
);
135 av_log(log_ctx
, AV_LOG_ERROR
,
136 "no such filter: '%s'\n", name
);
140 filt
= avfilter_open(filterdef
, inst_name
);
142 av_log(log_ctx
, AV_LOG_ERROR
,
143 "error creating filter '%s'\n", name
);
147 if(avfilter_graph_add_filter(ctx
, filt
) < 0) {
148 avfilter_destroy(filt
);
152 if(avfilter_init_filter(filt
, args
, NULL
)) {
153 av_log(log_ctx
, AV_LOG_ERROR
,
154 "error initializing filter '%s' with args '%s'\n", name
, args
);
162 * Parse "filter=params"
164 static AVFilterContext
*parse_filter(const char **buf
, AVFilterGraph
*graph
,
165 int index
, AVClass
*log_ctx
)
168 char *name
= consume_string(buf
);
169 AVFilterContext
*ret
;
173 opts
= consume_string(buf
);
176 ret
= create_filter(graph
, index
, name
, opts
, log_ctx
);
182 static void free_inout(AVFilterInOut
*head
)
185 AVFilterInOut
*next
= head
->next
;
192 static AVFilterInOut
*extract_inout(const char *label
, AVFilterInOut
**links
)
196 while(*links
&& strcmp((*links
)->name
, label
))
197 links
= &((*links
)->next
);
207 static void insert_inout(AVFilterInOut
**inouts
, AVFilterInOut
*element
)
209 element
->next
= *inouts
;
213 static int link_filter_inouts(AVFilterContext
*filter
,
214 AVFilterInOut
**currInputs
,
215 AVFilterInOut
**openInputs
, AVClass
*log_ctx
)
217 int pad
= filter
->input_count
;
220 AVFilterInOut
*p
= *currInputs
;
222 av_log(log_ctx
, AV_LOG_ERROR
,
223 "Not enough inputs specified for the \"%s\" filter.\n",
224 filter
->filter
->name
);
228 *currInputs
= (*currInputs
)->next
;
231 if(link_filter(p
->filter
, p
->pad_idx
, filter
, pad
, log_ctx
))
238 insert_inout(openInputs
, p
);
243 av_log(log_ctx
, AV_LOG_ERROR
,
244 "Too many inputs specified for the \"%s\" filter.\n",
245 filter
->filter
->name
);
249 pad
= filter
->output_count
;
251 AVFilterInOut
*currlinkn
= av_mallocz(sizeof(AVFilterInOut
));
252 currlinkn
->filter
= filter
;
253 currlinkn
->pad_idx
= pad
;
254 insert_inout(currInputs
, currlinkn
);
260 static int parse_inputs(const char **buf
, AVFilterInOut
**currInputs
,
261 AVFilterInOut
**openOutputs
, AVClass
*log_ctx
)
265 while(**buf
== '[') {
266 char *name
= parse_link_name(buf
, log_ctx
);
267 AVFilterInOut
*match
;
272 /* First check if the label is not in the openOutputs list */
273 match
= extract_inout(name
, openOutputs
);
278 /* Not in the list, so add it as an input */
279 match
= av_mallocz(sizeof(AVFilterInOut
));
281 match
->pad_idx
= pad
;
284 insert_inout(currInputs
, match
);
286 *buf
+= consume_whitespace(*buf
);
293 static int parse_outputs(const char **buf
, AVFilterInOut
**currInputs
,
294 AVFilterInOut
**openInputs
,
295 AVFilterInOut
**openOutputs
, AVClass
*log_ctx
)
299 while(**buf
== '[') {
300 char *name
= parse_link_name(buf
, log_ctx
);
301 AVFilterInOut
*match
;
303 AVFilterInOut
*input
= *currInputs
;
304 *currInputs
= (*currInputs
)->next
;
309 /* First check if the label is not in the openInputs list */
310 match
= extract_inout(name
, openInputs
);
313 if(link_filter(input
->filter
, input
->pad_idx
,
314 match
->filter
, match
->pad_idx
, log_ctx
) < 0)
316 av_free(match
->name
);
321 /* Not in the list, so add the first input as a openOutput */
323 insert_inout(openOutputs
, input
);
325 *buf
+= consume_whitespace(*buf
);
332 int avfilter_parse_graph(AVFilterGraph
*graph
, const char *filters
,
333 AVFilterInOut
*openInputs
,
334 AVFilterInOut
*openOutputs
, AVClass
*log_ctx
)
339 AVFilterInOut
*currInputs
= NULL
;
342 AVFilterContext
*filter
;
343 filters
+= consume_whitespace(filters
);
345 if(parse_inputs(&filters
, &currInputs
, &openOutputs
, log_ctx
) < 0)
348 filter
= parse_filter(&filters
, graph
, index
, log_ctx
);
353 if(filter
->input_count
== 1 && !currInputs
&& !index
) {
354 /* First input can be ommitted if it is "[in]" */
355 const char *tmp
= "[in]";
356 if(parse_inputs(&tmp
, &currInputs
, &openOutputs
, log_ctx
) < 0)
360 if(link_filter_inouts(filter
, &currInputs
, &openInputs
, log_ctx
) < 0)
363 if(parse_outputs(&filters
, &currInputs
, &openInputs
, &openOutputs
,
367 filters
+= consume_whitespace(filters
);
370 if(chr
== ';' && currInputs
) {
371 av_log(log_ctx
, AV_LOG_ERROR
,
372 "Could not find a output to link when parsing \"%s\"\n",
377 } while(chr
== ',' || chr
== ';');
379 if(openInputs
&& !strcmp(openInputs
->name
, "out") && currInputs
) {
380 /* Last output can be ommitted if it is "[out]" */
381 const char *tmp
= "[out]";
382 if(parse_outputs(&tmp
, &currInputs
, &openInputs
,
383 &openOutputs
, log_ctx
) < 0)
390 avfilter_destroy_graph(graph
);
391 free_inout(openInputs
);
392 free_inout(openOutputs
);
393 free_inout(currInputs
);