00001 /* 00002 * filter graph parser 00003 * copyright (c) 2008 Vitor Sessak 00004 * copyright (c) 2007 Bobby Bingham 00005 * 00006 * This file is part of FFmpeg. 00007 * 00008 * FFmpeg is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * FFmpeg is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with FFmpeg; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 #include <ctype.h> 00024 #include <string.h> 00025 00026 #include "graphparser.h" 00027 #include "avfilter.h" 00028 #include "avfiltergraph.h" 00029 00030 static int link_filter(AVFilterContext *src, int srcpad, 00031 AVFilterContext *dst, int dstpad, 00032 AVClass *log_ctx) 00033 { 00034 if(avfilter_link(src, srcpad, dst, dstpad)) { 00035 av_log(log_ctx, AV_LOG_ERROR, 00036 "cannot create the link %s:%d -> %s:%d\n", 00037 src->filter->name, srcpad, dst->filter->name, dstpad); 00038 return -1; 00039 } 00040 00041 return 0; 00042 } 00043 00044 static int consume_whitespace(const char *buf) 00045 { 00046 return strspn(buf, " \n\t"); 00047 } 00048 00053 static char *consume_string(const char **buf) 00054 { 00055 char *out = av_malloc(strlen(*buf) + 1); 00056 char *ret = out; 00057 00058 *buf += consume_whitespace(*buf); 00059 00060 do{ 00061 char c = *(*buf)++; 00062 switch (c) { 00063 case '\\': 00064 *out++ = *(*buf)++; 00065 break; 00066 case '\'': 00067 while(**buf && **buf != '\'') 00068 *out++ = *(*buf)++; 00069 if(**buf) (*buf)++; 00070 break; 00071 case 0: 00072 case ']': 00073 case '[': 00074 case '=': 00075 case ',': 00076 case ';': 00077 case ' ': 00078 case '\n': 00079 *out++ = 0; 00080 break; 00081 default: 00082 *out++ = c; 00083 } 00084 } while(out[-1]); 00085 00086 (*buf)--; 00087 *buf += consume_whitespace(*buf); 00088 00089 return ret; 00090 } 00091 00097 static char *parse_link_name(const char **buf, AVClass *log_ctx) 00098 { 00099 const char *start = *buf; 00100 char *name; 00101 (*buf)++; 00102 00103 name = consume_string(buf); 00104 00105 if(!name[0]) { 00106 av_log(log_ctx, AV_LOG_ERROR, 00107 "Bad (empty?) label found in the following: \"%s\".\n", start); 00108 goto fail; 00109 } 00110 00111 if(*(*buf)++ != ']') { 00112 av_log(log_ctx, AV_LOG_ERROR, 00113 "Mismatched '[' found in the following: \"%s\".\n", start); 00114 fail: 00115 av_freep(&name); 00116 } 00117 00118 return name; 00119 } 00120 00121 static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, 00122 const char *filt_name, const char *args, 00123 AVClass *log_ctx) 00124 { 00125 AVFilterContext *filt_ctx; 00126 00127 AVFilter *filt; 00128 char inst_name[30]; 00129 00130 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index); 00131 00132 filt = avfilter_get_by_name(filt_name); 00133 00134 if(!filt) { 00135 av_log(log_ctx, AV_LOG_ERROR, 00136 "no such filter: '%s'\n", filt_name); 00137 return NULL; 00138 } 00139 00140 filt_ctx = avfilter_open(filt, inst_name); 00141 if(!filt_ctx) { 00142 av_log(log_ctx, AV_LOG_ERROR, 00143 "error creating filter '%s'\n", filt_name); 00144 return NULL; 00145 } 00146 00147 if(avfilter_graph_add_filter(ctx, filt_ctx) < 0) { 00148 avfilter_destroy(filt_ctx); 00149 return NULL; 00150 } 00151 00152 if(avfilter_init_filter(filt_ctx, args, NULL)) { 00153 av_log(log_ctx, AV_LOG_ERROR, 00154 "error initializing filter '%s' with args '%s'\n", filt_name, args); 00155 return NULL; 00156 } 00157 00158 return filt_ctx; 00159 } 00160 00164 static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, 00165 int index, AVClass *log_ctx) 00166 { 00167 char *opts = NULL; 00168 char *name = consume_string(buf); 00169 AVFilterContext *ret; 00170 00171 if(**buf == '=') { 00172 (*buf)++; 00173 opts = consume_string(buf); 00174 } 00175 00176 ret = create_filter(graph, index, name, opts, log_ctx); 00177 av_free(name); 00178 av_free(opts); 00179 return ret; 00180 } 00181 00182 static void free_inout(AVFilterInOut *head) 00183 { 00184 while(head) { 00185 AVFilterInOut *next = head->next; 00186 av_free(head->name); 00187 av_free(head); 00188 head = next; 00189 } 00190 } 00191 00192 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 00193 { 00194 AVFilterInOut *ret; 00195 00196 while(*links && strcmp((*links)->name, label)) 00197 links = &((*links)->next); 00198 00199 ret = *links; 00200 00201 if(ret) 00202 *links = ret->next; 00203 00204 return ret; 00205 } 00206 00207 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 00208 { 00209 element->next = *inouts; 00210 *inouts = element; 00211 } 00212 00213 static int link_filter_inouts(AVFilterContext *filter, 00214 AVFilterInOut **curr_inputs, 00215 AVFilterInOut **open_inputs, AVClass *log_ctx) 00216 { 00217 int pad = filter->input_count; 00218 00219 while(pad--) { 00220 AVFilterInOut *p = *curr_inputs; 00221 if(!p) { 00222 av_log(log_ctx, AV_LOG_ERROR, 00223 "Not enough inputs specified for the \"%s\" filter.\n", 00224 filter->filter->name); 00225 return -1; 00226 } 00227 00228 *curr_inputs = (*curr_inputs)->next; 00229 00230 if(p->filter) { 00231 if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx)) 00232 return -1; 00233 av_free(p->name); 00234 av_free(p); 00235 } else { 00236 p->filter = filter; 00237 p->pad_idx = pad; 00238 insert_inout(open_inputs, p); 00239 } 00240 } 00241 00242 if(*curr_inputs) { 00243 av_log(log_ctx, AV_LOG_ERROR, 00244 "Too many inputs specified for the \"%s\" filter.\n", 00245 filter->filter->name); 00246 return -1; 00247 } 00248 00249 pad = filter->output_count; 00250 while(pad--) { 00251 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 00252 currlinkn->filter = filter; 00253 currlinkn->pad_idx = pad; 00254 insert_inout(curr_inputs, currlinkn); 00255 } 00256 00257 return 0; 00258 } 00259 00260 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 00261 AVFilterInOut **open_outputs, AVClass *log_ctx) 00262 { 00263 int pad = 0; 00264 00265 while(**buf == '[') { 00266 char *name = parse_link_name(buf, log_ctx); 00267 AVFilterInOut *match; 00268 00269 if(!name) 00270 return -1; 00271 00272 /* First check if the label is not in the open_outputs list */ 00273 match = extract_inout(name, open_outputs); 00274 00275 if(match) { 00276 av_free(name); 00277 } else { 00278 /* Not in the list, so add it as an input */ 00279 match = av_mallocz(sizeof(AVFilterInOut)); 00280 match->name = name; 00281 match->pad_idx = pad; 00282 } 00283 00284 insert_inout(curr_inputs, match); 00285 00286 *buf += consume_whitespace(*buf); 00287 pad++; 00288 } 00289 00290 return pad; 00291 } 00292 00293 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 00294 AVFilterInOut **open_inputs, 00295 AVFilterInOut **open_outputs, AVClass *log_ctx) 00296 { 00297 int pad = 0; 00298 00299 while(**buf == '[') { 00300 char *name = parse_link_name(buf, log_ctx); 00301 AVFilterInOut *match; 00302 00303 AVFilterInOut *input = *curr_inputs; 00304 *curr_inputs = (*curr_inputs)->next; 00305 00306 if(!name) 00307 return -1; 00308 00309 /* First check if the label is not in the open_inputs list */ 00310 match = extract_inout(name, open_inputs); 00311 00312 if(match) { 00313 if(link_filter(input->filter, input->pad_idx, 00314 match->filter, match->pad_idx, log_ctx) < 0) 00315 return -1; 00316 av_free(match->name); 00317 av_free(name); 00318 av_free(match); 00319 av_free(input); 00320 } else { 00321 /* Not in the list, so add the first input as a open_output */ 00322 input->name = name; 00323 insert_inout(open_outputs, input); 00324 } 00325 *buf += consume_whitespace(*buf); 00326 pad++; 00327 } 00328 00329 return pad; 00330 } 00331 00332 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 00333 AVFilterInOut *open_inputs, 00334 AVFilterInOut *open_outputs, AVClass *log_ctx) 00335 { 00336 int index = 0; 00337 char chr = 0; 00338 00339 AVFilterInOut *curr_inputs = NULL; 00340 00341 do { 00342 AVFilterContext *filter; 00343 filters += consume_whitespace(filters); 00344 00345 if(parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx) < 0) 00346 goto fail; 00347 00348 filter = parse_filter(&filters, graph, index, log_ctx); 00349 00350 if(!filter) 00351 goto fail; 00352 00353 if(filter->input_count == 1 && !curr_inputs && !index) { 00354 /* First input can be omitted if it is "[in]" */ 00355 const char *tmp = "[in]"; 00356 if(parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx) < 0) 00357 goto fail; 00358 } 00359 00360 if(link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx) < 0) 00361 goto fail; 00362 00363 if(parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 00364 log_ctx) < 0) 00365 goto fail; 00366 00367 filters += consume_whitespace(filters); 00368 chr = *filters++; 00369 00370 if(chr == ';' && curr_inputs) { 00371 av_log(log_ctx, AV_LOG_ERROR, 00372 "Could not find a output to link when parsing \"%s\"\n", 00373 filters - 1); 00374 goto fail; 00375 } 00376 index++; 00377 } while(chr == ',' || chr == ';'); 00378 00379 if (chr) { 00380 av_log(log_ctx, AV_LOG_ERROR, 00381 "Unable to parse graph description substring: \"%s\"\n", 00382 filters - 1); 00383 goto fail; 00384 } 00385 00386 if(open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { 00387 /* Last output can be omitted if it is "[out]" */ 00388 const char *tmp = "[out]"; 00389 if(parse_outputs(&tmp, &curr_inputs, &open_inputs, 00390 &open_outputs, log_ctx) < 0) 00391 goto fail; 00392 } 00393 00394 return 0; 00395 00396 fail: 00397 avfilter_graph_destroy(graph); 00398 free_inout(open_inputs); 00399 free_inout(open_outputs); 00400 free_inout(curr_inputs); 00401 return -1; 00402 }