Libav
|
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 #include "parseutils.h" 00030 00031 #define WHITESPACES " \n\t" 00032 00033 static int link_filter(AVFilterContext *src, int srcpad, 00034 AVFilterContext *dst, int dstpad, 00035 AVClass *log_ctx) 00036 { 00037 if(avfilter_link(src, srcpad, dst, dstpad)) { 00038 av_log(log_ctx, AV_LOG_ERROR, 00039 "cannot create the link %s:%d -> %s:%d\n", 00040 src->filter->name, srcpad, dst->filter->name, dstpad); 00041 return -1; 00042 } 00043 00044 return 0; 00045 } 00046 00052 static char *parse_link_name(const char **buf, AVClass *log_ctx) 00053 { 00054 const char *start = *buf; 00055 char *name; 00056 (*buf)++; 00057 00058 name = av_get_token(buf, "]"); 00059 00060 if(!name[0]) { 00061 av_log(log_ctx, AV_LOG_ERROR, 00062 "Bad (empty?) label found in the following: \"%s\".\n", start); 00063 goto fail; 00064 } 00065 00066 if(*(*buf)++ != ']') { 00067 av_log(log_ctx, AV_LOG_ERROR, 00068 "Mismatched '[' found in the following: \"%s\".\n", start); 00069 fail: 00070 av_freep(&name); 00071 } 00072 00073 return name; 00074 } 00075 00076 static AVFilterContext *create_filter(AVFilterGraph *ctx, int index, 00077 const char *filt_name, const char *args, 00078 AVClass *log_ctx) 00079 { 00080 AVFilterContext *filt_ctx; 00081 00082 AVFilter *filt; 00083 char inst_name[30]; 00084 00085 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index); 00086 00087 filt = avfilter_get_by_name(filt_name); 00088 00089 if(!filt) { 00090 av_log(log_ctx, AV_LOG_ERROR, 00091 "no such filter: '%s'\n", filt_name); 00092 return NULL; 00093 } 00094 00095 filt_ctx = avfilter_open(filt, inst_name); 00096 if(!filt_ctx) { 00097 av_log(log_ctx, AV_LOG_ERROR, 00098 "error creating filter '%s'\n", filt_name); 00099 return NULL; 00100 } 00101 00102 if(avfilter_graph_add_filter(ctx, filt_ctx) < 0) { 00103 avfilter_destroy(filt_ctx); 00104 return NULL; 00105 } 00106 00107 if(avfilter_init_filter(filt_ctx, args, NULL)) { 00108 av_log(log_ctx, AV_LOG_ERROR, 00109 "error initializing filter '%s' with args '%s'\n", filt_name, args); 00110 return NULL; 00111 } 00112 00113 return filt_ctx; 00114 } 00115 00119 static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, 00120 int index, AVClass *log_ctx) 00121 { 00122 char *opts = NULL; 00123 char *name = av_get_token(buf, "=,;[\n"); 00124 AVFilterContext *ret; 00125 00126 if(**buf == '=') { 00127 (*buf)++; 00128 opts = av_get_token(buf, "[],;\n"); 00129 } 00130 00131 ret = create_filter(graph, index, name, opts, log_ctx); 00132 av_free(name); 00133 av_free(opts); 00134 return ret; 00135 } 00136 00137 static void free_inout(AVFilterInOut *head) 00138 { 00139 while(head) { 00140 AVFilterInOut *next = head->next; 00141 av_free(head->name); 00142 av_free(head); 00143 head = next; 00144 } 00145 } 00146 00147 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 00148 { 00149 AVFilterInOut *ret; 00150 00151 while(*links && strcmp((*links)->name, label)) 00152 links = &((*links)->next); 00153 00154 ret = *links; 00155 00156 if(ret) 00157 *links = ret->next; 00158 00159 return ret; 00160 } 00161 00162 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 00163 { 00164 element->next = *inouts; 00165 *inouts = element; 00166 } 00167 00168 static int link_filter_inouts(AVFilterContext *filter, 00169 AVFilterInOut **curr_inputs, 00170 AVFilterInOut **open_inputs, AVClass *log_ctx) 00171 { 00172 int pad = filter->input_count; 00173 00174 while(pad--) { 00175 AVFilterInOut *p = *curr_inputs; 00176 if(!p) { 00177 av_log(log_ctx, AV_LOG_ERROR, 00178 "Not enough inputs specified for the \"%s\" filter.\n", 00179 filter->filter->name); 00180 return -1; 00181 } 00182 00183 *curr_inputs = (*curr_inputs)->next; 00184 00185 if(p->filter) { 00186 if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx)) 00187 return -1; 00188 av_free(p->name); 00189 av_free(p); 00190 } else { 00191 p->filter = filter; 00192 p->pad_idx = pad; 00193 insert_inout(open_inputs, p); 00194 } 00195 } 00196 00197 if(*curr_inputs) { 00198 av_log(log_ctx, AV_LOG_ERROR, 00199 "Too many inputs specified for the \"%s\" filter.\n", 00200 filter->filter->name); 00201 return -1; 00202 } 00203 00204 pad = filter->output_count; 00205 while(pad--) { 00206 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 00207 currlinkn->filter = filter; 00208 currlinkn->pad_idx = pad; 00209 insert_inout(curr_inputs, currlinkn); 00210 } 00211 00212 return 0; 00213 } 00214 00215 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 00216 AVFilterInOut **open_outputs, AVClass *log_ctx) 00217 { 00218 int pad = 0; 00219 00220 while(**buf == '[') { 00221 char *name = parse_link_name(buf, log_ctx); 00222 AVFilterInOut *match; 00223 00224 if(!name) 00225 return -1; 00226 00227 /* First check if the label is not in the open_outputs list */ 00228 match = extract_inout(name, open_outputs); 00229 00230 if(match) { 00231 av_free(name); 00232 } else { 00233 /* Not in the list, so add it as an input */ 00234 match = av_mallocz(sizeof(AVFilterInOut)); 00235 match->name = name; 00236 match->pad_idx = pad; 00237 } 00238 00239 insert_inout(curr_inputs, match); 00240 00241 *buf += strspn(*buf, WHITESPACES); 00242 pad++; 00243 } 00244 00245 return pad; 00246 } 00247 00248 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 00249 AVFilterInOut **open_inputs, 00250 AVFilterInOut **open_outputs, AVClass *log_ctx) 00251 { 00252 int pad = 0; 00253 00254 while(**buf == '[') { 00255 char *name = parse_link_name(buf, log_ctx); 00256 AVFilterInOut *match; 00257 00258 AVFilterInOut *input = *curr_inputs; 00259 *curr_inputs = (*curr_inputs)->next; 00260 00261 if(!name) 00262 return -1; 00263 00264 /* First check if the label is not in the open_inputs list */ 00265 match = extract_inout(name, open_inputs); 00266 00267 if(match) { 00268 if(link_filter(input->filter, input->pad_idx, 00269 match->filter, match->pad_idx, log_ctx) < 0) 00270 return -1; 00271 av_free(match->name); 00272 av_free(name); 00273 av_free(match); 00274 av_free(input); 00275 } else { 00276 /* Not in the list, so add the first input as a open_output */ 00277 input->name = name; 00278 insert_inout(open_outputs, input); 00279 } 00280 *buf += strspn(*buf, WHITESPACES); 00281 pad++; 00282 } 00283 00284 return pad; 00285 } 00286 00287 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 00288 AVFilterInOut *open_inputs, 00289 AVFilterInOut *open_outputs, AVClass *log_ctx) 00290 { 00291 int index = 0; 00292 char chr = 0; 00293 00294 AVFilterInOut *curr_inputs = NULL; 00295 00296 do { 00297 AVFilterContext *filter; 00298 filters += strspn(filters, WHITESPACES); 00299 00300 if(parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx) < 0) 00301 goto fail; 00302 00303 filter = parse_filter(&filters, graph, index, log_ctx); 00304 00305 if(!filter) 00306 goto fail; 00307 00308 if(filter->input_count == 1 && !curr_inputs && !index) { 00309 /* First input can be omitted if it is "[in]" */ 00310 const char *tmp = "[in]"; 00311 if(parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx) < 0) 00312 goto fail; 00313 } 00314 00315 if(link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx) < 0) 00316 goto fail; 00317 00318 if(parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 00319 log_ctx) < 0) 00320 goto fail; 00321 00322 filters += strspn(filters, WHITESPACES); 00323 chr = *filters++; 00324 00325 if(chr == ';' && curr_inputs) { 00326 av_log(log_ctx, AV_LOG_ERROR, 00327 "Could not find a output to link when parsing \"%s\"\n", 00328 filters - 1); 00329 goto fail; 00330 } 00331 index++; 00332 } while(chr == ',' || chr == ';'); 00333 00334 if (chr) { 00335 av_log(log_ctx, AV_LOG_ERROR, 00336 "Unable to parse graph description substring: \"%s\"\n", 00337 filters - 1); 00338 goto fail; 00339 } 00340 00341 if(open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { 00342 /* Last output can be omitted if it is "[out]" */ 00343 const char *tmp = "[out]"; 00344 if(parse_outputs(&tmp, &curr_inputs, &open_inputs, 00345 &open_outputs, log_ctx) < 0) 00346 goto fail; 00347 } 00348 00349 return 0; 00350 00351 fail: 00352 avfilter_graph_destroy(graph); 00353 free_inout(open_inputs); 00354 free_inout(open_outputs); 00355 free_inout(curr_inputs); 00356 return -1; 00357 }