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 *filt_name
, const char *args
,
125 AVFilterContext
*filt_ctx
;
130 snprintf(inst_name
, sizeof(inst_name
), "Parsed filter %d", index
);
132 filt
= avfilter_get_by_name(filt_name
);
135 av_log(log_ctx
, AV_LOG_ERROR
,
136 "no such filter: '%s'\n", filt_name
);
140 filt_ctx
= avfilter_open(filt
, inst_name
);
142 av_log(log_ctx
, AV_LOG_ERROR
,
143 "error creating filter '%s'\n", filt_name
);
147 if(avfilter_graph_add_filter(ctx
, filt_ctx
) < 0) {
148 avfilter_destroy(filt_ctx
);
152 if(avfilter_init_filter(filt_ctx
, args
, NULL
)) {
153 av_log(log_ctx
, AV_LOG_ERROR
,
154 "error initializing filter '%s' with args '%s'\n", filt_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
**curr_inputs
,
215 AVFilterInOut
**open_inputs
, AVClass
*log_ctx
)
217 int pad
= filter
->input_count
;
220 AVFilterInOut
*p
= *curr_inputs
;
222 av_log(log_ctx
, AV_LOG_ERROR
,
223 "Not enough inputs specified for the \"%s\" filter.\n",
224 filter
->filter
->name
);
228 *curr_inputs
= (*curr_inputs
)->next
;
231 if(link_filter(p
->filter
, p
->pad_idx
, filter
, pad
, log_ctx
))
238 insert_inout(open_inputs
, 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(curr_inputs
, currlinkn
);
260 static int parse_inputs(const char **buf
, AVFilterInOut
**curr_inputs
,
261 AVFilterInOut
**open_outputs
, 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 open_outputs list */
273 match
= extract_inout(name
, open_outputs
);
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(curr_inputs
, match
);
286 *buf
+= consume_whitespace(*buf
);
293 static int parse_outputs(const char **buf
, AVFilterInOut
**curr_inputs
,
294 AVFilterInOut
**open_inputs
,
295 AVFilterInOut
**open_outputs
, AVClass
*log_ctx
)
299 while(**buf
== '[') {
300 char *name
= parse_link_name(buf
, log_ctx
);
301 AVFilterInOut
*match
;
303 AVFilterInOut
*input
= *curr_inputs
;
304 *curr_inputs
= (*curr_inputs
)->next
;
309 /* First check if the label is not in the open_inputs list */
310 match
= extract_inout(name
, open_inputs
);
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 open_output */
323 insert_inout(open_outputs
, input
);
325 *buf
+= consume_whitespace(*buf
);
332 int avfilter_graph_parse(AVFilterGraph
*graph
, const char *filters
,
333 AVFilterInOut
*open_inputs
,
334 AVFilterInOut
*open_outputs
, AVClass
*log_ctx
)
339 AVFilterInOut
*curr_inputs
= NULL
;
342 AVFilterContext
*filter
;
343 filters
+= consume_whitespace(filters
);
345 if(parse_inputs(&filters
, &curr_inputs
, &open_outputs
, log_ctx
) < 0)
348 filter
= parse_filter(&filters
, graph
, index
, log_ctx
);
353 if(filter
->input_count
== 1 && !curr_inputs
&& !index
) {
354 /* First input can be omitted if it is "[in]" */
355 const char *tmp
= "[in]";
356 if(parse_inputs(&tmp
, &curr_inputs
, &open_outputs
, log_ctx
) < 0)
360 if(link_filter_inouts(filter
, &curr_inputs
, &open_inputs
, log_ctx
) < 0)
363 if(parse_outputs(&filters
, &curr_inputs
, &open_inputs
, &open_outputs
,
367 filters
+= consume_whitespace(filters
);
370 if(chr
== ';' && curr_inputs
) {
371 av_log(log_ctx
, AV_LOG_ERROR
,
372 "Could not find a output to link when parsing \"%s\"\n",
377 } while(chr
== ',' || chr
== ';');
380 av_log(log_ctx
, AV_LOG_ERROR
,
381 "Unable to parse graph description substring: \"%s\"\n",
386 if(open_inputs
&& !strcmp(open_inputs
->name
, "out") && curr_inputs
) {
387 /* Last output can be omitted if it is "[out]" */
388 const char *tmp
= "[out]";
389 if(parse_outputs(&tmp
, &curr_inputs
, &open_inputs
,
390 &open_outputs
, log_ctx
) < 0)
397 avfilter_graph_destroy(graph
);
398 free_inout(open_inputs
);
399 free_inout(open_outputs
);
400 free_inout(curr_inputs
);