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 "libavutil/avstring.h" 00027 #include "avfilter.h" 00028 #include "avfiltergraph.h" 00029 00030 #define WHITESPACES " \n\t" 00031 00037 static int link_filter(AVFilterContext *src, int srcpad, 00038 AVFilterContext *dst, int dstpad, 00039 void *log_ctx) 00040 { 00041 int ret; 00042 if ((ret = avfilter_link(src, srcpad, dst, dstpad))) { 00043 av_log(log_ctx, AV_LOG_ERROR, 00044 "Cannot create the link %s:%d -> %s:%d\n", 00045 src->filter->name, srcpad, dst->filter->name, dstpad); 00046 return ret; 00047 } 00048 00049 return 0; 00050 } 00051 00058 static char *parse_link_name(const char **buf, void *log_ctx) 00059 { 00060 const char *start = *buf; 00061 char *name; 00062 (*buf)++; 00063 00064 name = av_get_token(buf, "]"); 00065 00066 if (!name[0]) { 00067 av_log(log_ctx, AV_LOG_ERROR, 00068 "Bad (empty?) label found in the following: \"%s\".\n", start); 00069 goto fail; 00070 } 00071 00072 if (*(*buf)++ != ']') { 00073 av_log(log_ctx, AV_LOG_ERROR, 00074 "Mismatched '[' found in the following: \"%s\".\n", start); 00075 fail: 00076 av_freep(&name); 00077 } 00078 00079 return name; 00080 } 00081 00094 static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index, 00095 const char *filt_name, const char *args, void *log_ctx) 00096 { 00097 AVFilter *filt; 00098 char inst_name[30]; 00099 char tmp_args[256]; 00100 int ret; 00101 00102 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d %s", index, filt_name); 00103 00104 filt = avfilter_get_by_name(filt_name); 00105 00106 if (!filt) { 00107 av_log(log_ctx, AV_LOG_ERROR, 00108 "No such filter: '%s'\n", filt_name); 00109 return AVERROR(EINVAL); 00110 } 00111 00112 ret = avfilter_open(filt_ctx, filt, inst_name); 00113 if (!*filt_ctx) { 00114 av_log(log_ctx, AV_LOG_ERROR, 00115 "Error creating filter '%s'\n", filt_name); 00116 return ret; 00117 } 00118 00119 if ((ret = avfilter_graph_add_filter(ctx, *filt_ctx)) < 0) { 00120 avfilter_free(*filt_ctx); 00121 return ret; 00122 } 00123 00124 if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags")) { 00125 snprintf(tmp_args, sizeof(tmp_args), "%s:%s", 00126 args, ctx->scale_sws_opts); 00127 args = tmp_args; 00128 } 00129 00130 if ((ret = avfilter_init_filter(*filt_ctx, args, NULL)) < 0) { 00131 av_log(log_ctx, AV_LOG_ERROR, 00132 "Error initializing filter '%s' with args '%s'\n", filt_name, args); 00133 return ret; 00134 } 00135 00136 return 0; 00137 } 00138 00153 static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph, 00154 int index, void *log_ctx) 00155 { 00156 char *opts = NULL; 00157 char *name = av_get_token(buf, "=,;[\n"); 00158 int ret; 00159 00160 if (**buf == '=') { 00161 (*buf)++; 00162 opts = av_get_token(buf, "[],;\n"); 00163 } 00164 00165 ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx); 00166 av_free(name); 00167 av_free(opts); 00168 return ret; 00169 } 00170 00171 AVFilterInOut *avfilter_inout_alloc(void) 00172 { 00173 return av_mallocz(sizeof(AVFilterInOut)); 00174 } 00175 00176 void avfilter_inout_free(AVFilterInOut **inout) 00177 { 00178 while (*inout) { 00179 AVFilterInOut *next = (*inout)->next; 00180 av_freep(&(*inout)->name); 00181 av_freep(inout); 00182 *inout = next; 00183 } 00184 } 00185 00186 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 00187 { 00188 AVFilterInOut *ret; 00189 00190 while (*links && strcmp((*links)->name, label)) 00191 links = &((*links)->next); 00192 00193 ret = *links; 00194 00195 if (ret) 00196 *links = ret->next; 00197 00198 return ret; 00199 } 00200 00201 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 00202 { 00203 element->next = *inouts; 00204 *inouts = element; 00205 } 00206 00207 static int link_filter_inouts(AVFilterContext *filt_ctx, 00208 AVFilterInOut **curr_inputs, 00209 AVFilterInOut **open_inputs, void *log_ctx) 00210 { 00211 int pad = filt_ctx->input_count, ret; 00212 00213 while (pad--) { 00214 AVFilterInOut *p = *curr_inputs; 00215 if (!p) { 00216 av_log(log_ctx, AV_LOG_ERROR, 00217 "Not enough inputs specified for the \"%s\" filter.\n", 00218 filt_ctx->filter->name); 00219 return AVERROR(EINVAL); 00220 } 00221 00222 *curr_inputs = (*curr_inputs)->next; 00223 00224 if (p->filter_ctx) { 00225 if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0) 00226 return ret; 00227 av_free(p->name); 00228 av_free(p); 00229 } else { 00230 p->filter_ctx = filt_ctx; 00231 p->pad_idx = pad; 00232 insert_inout(open_inputs, p); 00233 } 00234 } 00235 00236 if (*curr_inputs) { 00237 av_log(log_ctx, AV_LOG_ERROR, 00238 "Too many inputs specified for the \"%s\" filter.\n", 00239 filt_ctx->filter->name); 00240 return AVERROR(EINVAL); 00241 } 00242 00243 pad = filt_ctx->output_count; 00244 while (pad--) { 00245 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 00246 if (!currlinkn) 00247 return AVERROR(ENOMEM); 00248 currlinkn->filter_ctx = filt_ctx; 00249 currlinkn->pad_idx = pad; 00250 insert_inout(curr_inputs, currlinkn); 00251 } 00252 00253 return 0; 00254 } 00255 00256 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 00257 AVFilterInOut **open_outputs, void *log_ctx) 00258 { 00259 int pad = 0; 00260 00261 while (**buf == '[') { 00262 char *name = parse_link_name(buf, log_ctx); 00263 AVFilterInOut *match; 00264 00265 if (!name) 00266 return AVERROR(EINVAL); 00267 00268 /* First check if the label is not in the open_outputs list */ 00269 match = extract_inout(name, open_outputs); 00270 00271 if (match) { 00272 av_free(name); 00273 } else { 00274 /* Not in the list, so add it as an input */ 00275 if (!(match = av_mallocz(sizeof(AVFilterInOut)))) 00276 return AVERROR(ENOMEM); 00277 match->name = name; 00278 match->pad_idx = pad; 00279 } 00280 00281 insert_inout(curr_inputs, match); 00282 00283 *buf += strspn(*buf, WHITESPACES); 00284 pad++; 00285 } 00286 00287 return pad; 00288 } 00289 00290 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 00291 AVFilterInOut **open_inputs, 00292 AVFilterInOut **open_outputs, void *log_ctx) 00293 { 00294 int ret, pad = 0; 00295 00296 while (**buf == '[') { 00297 char *name = parse_link_name(buf, log_ctx); 00298 AVFilterInOut *match; 00299 00300 AVFilterInOut *input = *curr_inputs; 00301 if (!input) { 00302 av_log(log_ctx, AV_LOG_ERROR, 00303 "No output pad can be associated to link label '%s'.\n", 00304 name); 00305 return AVERROR(EINVAL); 00306 } 00307 *curr_inputs = (*curr_inputs)->next; 00308 00309 if (!name) 00310 return AVERROR(EINVAL); 00311 00312 /* First check if the label is not in the open_inputs list */ 00313 match = extract_inout(name, open_inputs); 00314 00315 if (match) { 00316 if ((ret = link_filter(input->filter_ctx, input->pad_idx, 00317 match->filter_ctx, match->pad_idx, log_ctx)) < 0) 00318 return ret; 00319 av_free(match->name); 00320 av_free(name); 00321 av_free(match); 00322 av_free(input); 00323 } else { 00324 /* Not in the list, so add the first input as a open_output */ 00325 input->name = name; 00326 insert_inout(open_outputs, input); 00327 } 00328 *buf += strspn(*buf, WHITESPACES); 00329 pad++; 00330 } 00331 00332 return pad; 00333 } 00334 00335 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 00336 AVFilterInOut **open_inputs, AVFilterInOut **open_outputs, 00337 void *log_ctx) 00338 { 00339 int index = 0, ret; 00340 char chr = 0; 00341 00342 AVFilterInOut *curr_inputs = NULL; 00343 00344 do { 00345 AVFilterContext *filter; 00346 const char *filterchain = filters; 00347 filters += strspn(filters, WHITESPACES); 00348 00349 if ((ret = parse_inputs(&filters, &curr_inputs, open_outputs, log_ctx)) < 0) 00350 goto fail; 00351 00352 if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) 00353 goto fail; 00354 00355 if (filter->input_count == 1 && !curr_inputs && !index) { 00356 /* First input can be omitted if it is "[in]" */ 00357 const char *tmp = "[in]"; 00358 if ((ret = parse_inputs(&tmp, &curr_inputs, open_outputs, log_ctx)) < 0) 00359 goto fail; 00360 } 00361 00362 if ((ret = link_filter_inouts(filter, &curr_inputs, open_inputs, log_ctx)) < 0) 00363 goto fail; 00364 00365 if ((ret = parse_outputs(&filters, &curr_inputs, open_inputs, open_outputs, 00366 log_ctx)) < 0) 00367 goto fail; 00368 00369 filters += strspn(filters, WHITESPACES); 00370 chr = *filters++; 00371 00372 if (chr == ';' && curr_inputs) { 00373 av_log(log_ctx, AV_LOG_ERROR, 00374 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", 00375 filterchain); 00376 ret = AVERROR(EINVAL); 00377 goto fail; 00378 } 00379 index++; 00380 } while (chr == ',' || chr == ';'); 00381 00382 if (chr) { 00383 av_log(log_ctx, AV_LOG_ERROR, 00384 "Unable to parse graph description substring: \"%s\"\n", 00385 filters - 1); 00386 ret = AVERROR(EINVAL); 00387 goto fail; 00388 } 00389 00390 if (open_inputs && *open_inputs && !strcmp((*open_inputs)->name, "out") && curr_inputs) { 00391 /* Last output can be omitted if it is "[out]" */ 00392 const char *tmp = "[out]"; 00393 if ((ret = parse_outputs(&tmp, &curr_inputs, open_inputs, open_outputs, 00394 log_ctx)) < 0) 00395 goto fail; 00396 } 00397 00398 return 0; 00399 00400 fail: 00401 for (; graph->filter_count > 0; graph->filter_count--) 00402 avfilter_free(graph->filters[graph->filter_count - 1]); 00403 av_freep(&graph->filters); 00404 avfilter_inout_free(open_inputs); 00405 avfilter_inout_free(open_outputs); 00406 avfilter_inout_free(&curr_inputs); 00407 return ret; 00408 }