Libav 0.7.1
|
00001 /* 00002 * filter graph parser 00003 * Copyright (c) 2008 Vitor Sessak 00004 * Copyright (c) 2007 Bobby Bingham 00005 * 00006 * This file is part of Libav. 00007 * 00008 * Libav 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 * Libav 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 Libav; 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 AVClass *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, AVClass *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, AVClass *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, AVClass *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 static void free_inout(AVFilterInOut *head) 00172 { 00173 while (head) { 00174 AVFilterInOut *next = head->next; 00175 av_free(head->name); 00176 av_free(head); 00177 head = next; 00178 } 00179 } 00180 00181 static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 00182 { 00183 AVFilterInOut *ret; 00184 00185 while (*links && strcmp((*links)->name, label)) 00186 links = &((*links)->next); 00187 00188 ret = *links; 00189 00190 if (ret) 00191 *links = ret->next; 00192 00193 return ret; 00194 } 00195 00196 static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 00197 { 00198 element->next = *inouts; 00199 *inouts = element; 00200 } 00201 00202 static int link_filter_inouts(AVFilterContext *filt_ctx, 00203 AVFilterInOut **curr_inputs, 00204 AVFilterInOut **open_inputs, AVClass *log_ctx) 00205 { 00206 int pad = filt_ctx->input_count, ret; 00207 00208 while (pad--) { 00209 AVFilterInOut *p = *curr_inputs; 00210 if (!p) { 00211 av_log(log_ctx, AV_LOG_ERROR, 00212 "Not enough inputs specified for the \"%s\" filter.\n", 00213 filt_ctx->filter->name); 00214 return AVERROR(EINVAL); 00215 } 00216 00217 *curr_inputs = (*curr_inputs)->next; 00218 00219 if (p->filter_ctx) { 00220 if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0) 00221 return ret; 00222 av_free(p->name); 00223 av_free(p); 00224 } else { 00225 p->filter_ctx = filt_ctx; 00226 p->pad_idx = pad; 00227 insert_inout(open_inputs, p); 00228 } 00229 } 00230 00231 if (*curr_inputs) { 00232 av_log(log_ctx, AV_LOG_ERROR, 00233 "Too many inputs specified for the \"%s\" filter.\n", 00234 filt_ctx->filter->name); 00235 return AVERROR(EINVAL); 00236 } 00237 00238 pad = filt_ctx->output_count; 00239 while (pad--) { 00240 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 00241 if (!currlinkn) 00242 return AVERROR(ENOMEM); 00243 currlinkn->filter_ctx = filt_ctx; 00244 currlinkn->pad_idx = pad; 00245 insert_inout(curr_inputs, currlinkn); 00246 } 00247 00248 return 0; 00249 } 00250 00251 static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 00252 AVFilterInOut **open_outputs, AVClass *log_ctx) 00253 { 00254 int pad = 0; 00255 00256 while (**buf == '[') { 00257 char *name = parse_link_name(buf, log_ctx); 00258 AVFilterInOut *match; 00259 00260 if (!name) 00261 return AVERROR(EINVAL); 00262 00263 /* First check if the label is not in the open_outputs list */ 00264 match = extract_inout(name, open_outputs); 00265 00266 if (match) { 00267 av_free(name); 00268 } else { 00269 /* Not in the list, so add it as an input */ 00270 if (!(match = av_mallocz(sizeof(AVFilterInOut)))) 00271 return AVERROR(ENOMEM); 00272 match->name = name; 00273 match->pad_idx = pad; 00274 } 00275 00276 insert_inout(curr_inputs, match); 00277 00278 *buf += strspn(*buf, WHITESPACES); 00279 pad++; 00280 } 00281 00282 return pad; 00283 } 00284 00285 static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 00286 AVFilterInOut **open_inputs, 00287 AVFilterInOut **open_outputs, AVClass *log_ctx) 00288 { 00289 int ret, pad = 0; 00290 00291 while (**buf == '[') { 00292 char *name = parse_link_name(buf, log_ctx); 00293 AVFilterInOut *match; 00294 00295 AVFilterInOut *input = *curr_inputs; 00296 if (!input) { 00297 av_log(log_ctx, AV_LOG_ERROR, 00298 "No output pad can be associated to link label '%s'.\n", 00299 name); 00300 return AVERROR(EINVAL); 00301 } 00302 *curr_inputs = (*curr_inputs)->next; 00303 00304 if (!name) 00305 return AVERROR(EINVAL); 00306 00307 /* First check if the label is not in the open_inputs list */ 00308 match = extract_inout(name, open_inputs); 00309 00310 if (match) { 00311 if ((ret = link_filter(input->filter_ctx, input->pad_idx, 00312 match->filter_ctx, match->pad_idx, log_ctx)) < 0) 00313 return ret; 00314 av_free(match->name); 00315 av_free(name); 00316 av_free(match); 00317 av_free(input); 00318 } else { 00319 /* Not in the list, so add the first input as a open_output */ 00320 input->name = name; 00321 insert_inout(open_outputs, input); 00322 } 00323 *buf += strspn(*buf, WHITESPACES); 00324 pad++; 00325 } 00326 00327 return pad; 00328 } 00329 00330 int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 00331 AVFilterInOut *open_inputs, 00332 AVFilterInOut *open_outputs, AVClass *log_ctx) 00333 { 00334 int index = 0, ret; 00335 char chr = 0; 00336 00337 AVFilterInOut *curr_inputs = NULL; 00338 00339 do { 00340 AVFilterContext *filter; 00341 const char *filterchain = filters; 00342 filters += strspn(filters, WHITESPACES); 00343 00344 if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) 00345 goto fail; 00346 00347 if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) 00348 goto fail; 00349 00350 if (filter->input_count == 1 && !curr_inputs && !index) { 00351 /* First input can be omitted if it is "[in]" */ 00352 const char *tmp = "[in]"; 00353 if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) 00354 goto fail; 00355 } 00356 00357 if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) 00358 goto fail; 00359 00360 if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 00361 log_ctx)) < 0) 00362 goto fail; 00363 00364 filters += strspn(filters, WHITESPACES); 00365 chr = *filters++; 00366 00367 if (chr == ';' && curr_inputs) { 00368 av_log(log_ctx, AV_LOG_ERROR, 00369 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", 00370 filterchain); 00371 ret = AVERROR(EINVAL); 00372 goto fail; 00373 } 00374 index++; 00375 } while (chr == ',' || chr == ';'); 00376 00377 if (chr) { 00378 av_log(log_ctx, AV_LOG_ERROR, 00379 "Unable to parse graph description substring: \"%s\"\n", 00380 filters - 1); 00381 ret = AVERROR(EINVAL); 00382 goto fail; 00383 } 00384 00385 if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { 00386 /* Last output can be omitted if it is "[out]" */ 00387 const char *tmp = "[out]"; 00388 if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, 00389 log_ctx)) < 0) 00390 goto fail; 00391 } 00392 00393 return 0; 00394 00395 fail: 00396 for (; graph->filter_count > 0; graph->filter_count--) 00397 avfilter_free(graph->filters[graph->filter_count - 1]); 00398 av_freep(&graph->filters); 00399 free_inout(open_inputs); 00400 free_inout(open_outputs); 00401 free_inout(curr_inputs); 00402 return ret; 00403 }