1/* 2 * filter graph parser 3 * Copyright (c) 2008 Vitor Sessak 4 * Copyright (c) 2007 Bobby Bingham 5 * 6 * This file is part of Libav. 7 * 8 * Libav 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. 12 * 13 * Libav 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. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with Libav; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23#include <ctype.h> 24#include <string.h> 25 26#include "libavutil/avstring.h" 27#include "avfilter.h" 28#include "avfiltergraph.h" 29 30#define WHITESPACES " \n\t" 31 32/** 33 * Link two filters together. 34 * 35 * @see avfilter_link() 36 */ 37static int link_filter(AVFilterContext *src, int srcpad, 38 AVFilterContext *dst, int dstpad, 39 void *log_ctx) 40{ 41 int ret; 42 if ((ret = avfilter_link(src, srcpad, dst, dstpad))) { 43 av_log(log_ctx, AV_LOG_ERROR, 44 "Cannot create the link %s:%d -> %s:%d\n", 45 src->filter->name, srcpad, dst->filter->name, dstpad); 46 return ret; 47 } 48 49 return 0; 50} 51 52/** 53 * Parse the name of a link, which has the format "[linkname]". 54 * 55 * @return a pointer (that need to be freed after use) to the name 56 * between parenthesis 57 */ 58static char *parse_link_name(const char **buf, void *log_ctx) 59{ 60 const char *start = *buf; 61 char *name; 62 (*buf)++; 63 64 name = av_get_token(buf, "]"); 65 66 if (!name[0]) { 67 av_log(log_ctx, AV_LOG_ERROR, 68 "Bad (empty?) label found in the following: \"%s\".\n", start); 69 goto fail; 70 } 71 72 if (*(*buf)++ != ']') { 73 av_log(log_ctx, AV_LOG_ERROR, 74 "Mismatched '[' found in the following: \"%s\".\n", start); 75 fail: 76 av_freep(&name); 77 } 78 79 return name; 80} 81 82/** 83 * Create an instance of a filter, initialize and insert it in the 84 * filtergraph in *ctx. 85 * 86 * @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise. 87 * @param ctx the filtergraph context 88 * @param index an index which is supposed to be unique for each filter instance added to the filtergraph 89 * @param filt_name the name of the filter to create 90 * @param args the arguments provided to the filter during its initialization 91 * @param log_ctx the log context to use 92 * @return 0 in case of success, a negative AVERROR code otherwise 93 */ 94static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index, 95 const char *filt_name, const char *args, void *log_ctx) 96{ 97 AVFilter *filt; 98 char inst_name[30]; 99 char tmp_args[256]; 100 int ret; 101 102 snprintf(inst_name, sizeof(inst_name), "Parsed filter %d %s", index, filt_name); 103 104 filt = avfilter_get_by_name(filt_name); 105 106 if (!filt) { 107 av_log(log_ctx, AV_LOG_ERROR, 108 "No such filter: '%s'\n", filt_name); 109 return AVERROR(EINVAL); 110 } 111 112 ret = avfilter_open(filt_ctx, filt, inst_name); 113 if (!*filt_ctx) { 114 av_log(log_ctx, AV_LOG_ERROR, 115 "Error creating filter '%s'\n", filt_name); 116 return ret; 117 } 118 119 if ((ret = avfilter_graph_add_filter(ctx, *filt_ctx)) < 0) { 120 avfilter_free(*filt_ctx); 121 return ret; 122 } 123 124 if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags") && 125 ctx->scale_sws_opts) { 126 snprintf(tmp_args, sizeof(tmp_args), "%s:%s", 127 args, ctx->scale_sws_opts); 128 args = tmp_args; 129 } 130 131 if ((ret = avfilter_init_filter(*filt_ctx, args, NULL)) < 0) { 132 av_log(log_ctx, AV_LOG_ERROR, 133 "Error initializing filter '%s' with args '%s'\n", filt_name, args); 134 return ret; 135 } 136 137 return 0; 138} 139 140/** 141 * Parse a string of the form FILTER_NAME[=PARAMS], and create a 142 * corresponding filter instance which is added to graph with 143 * create_filter(). 144 * 145 * @param filt_ctx Pointer that is set to the created and configured filter 146 * context on success, set to NULL on failure. 147 * @param filt_ctx put here a pointer to the created filter context on 148 * success, NULL otherwise 149 * @param buf pointer to the buffer to parse, *buf will be updated to 150 * point to the char next after the parsed string 151 * @param index an index which is assigned to the created filter 152 * instance, and which is supposed to be unique for each filter 153 * instance added to the filtergraph 154 * @return 0 in case of success, a negative AVERROR code otherwise 155 */ 156static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph, 157 int index, void *log_ctx) 158{ 159 char *opts = NULL; 160 char *name = av_get_token(buf, "=,;[\n"); 161 int ret; 162 163 if (**buf == '=') { 164 (*buf)++; 165 opts = av_get_token(buf, "[],;\n"); 166 } 167 168 ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx); 169 av_free(name); 170 av_free(opts); 171 return ret; 172} 173 174static void free_inout(AVFilterInOut *head) 175{ 176 while (head) { 177 AVFilterInOut *next = head->next; 178 av_free(head->name); 179 av_free(head); 180 head = next; 181 } 182} 183 184static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 185{ 186 AVFilterInOut *ret; 187 188 while (*links && strcmp((*links)->name, label)) 189 links = &((*links)->next); 190 191 ret = *links; 192 193 if (ret) 194 *links = ret->next; 195 196 return ret; 197} 198 199static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 200{ 201 element->next = *inouts; 202 *inouts = element; 203} 204 205static int link_filter_inouts(AVFilterContext *filt_ctx, 206 AVFilterInOut **curr_inputs, 207 AVFilterInOut **open_inputs, void *log_ctx) 208{ 209 int pad = filt_ctx->input_count, ret; 210 211 while (pad--) { 212 AVFilterInOut *p = *curr_inputs; 213 if (!p) { 214 av_log(log_ctx, AV_LOG_ERROR, 215 "Not enough inputs specified for the \"%s\" filter.\n", 216 filt_ctx->filter->name); 217 return AVERROR(EINVAL); 218 } 219 220 *curr_inputs = (*curr_inputs)->next; 221 222 if (p->filter_ctx) { 223 if ((ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx)) < 0) 224 return ret; 225 av_free(p->name); 226 av_free(p); 227 } else { 228 p->filter_ctx = filt_ctx; 229 p->pad_idx = pad; 230 insert_inout(open_inputs, p); 231 } 232 } 233 234 if (*curr_inputs) { 235 av_log(log_ctx, AV_LOG_ERROR, 236 "Too many inputs specified for the \"%s\" filter.\n", 237 filt_ctx->filter->name); 238 return AVERROR(EINVAL); 239 } 240 241 pad = filt_ctx->output_count; 242 while (pad--) { 243 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 244 if (!currlinkn) 245 return AVERROR(ENOMEM); 246 currlinkn->filter_ctx = filt_ctx; 247 currlinkn->pad_idx = pad; 248 insert_inout(curr_inputs, currlinkn); 249 } 250 251 return 0; 252} 253 254static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 255 AVFilterInOut **open_outputs, void *log_ctx) 256{ 257 int pad = 0; 258 259 while (**buf == '[') { 260 char *name = parse_link_name(buf, log_ctx); 261 AVFilterInOut *match; 262 263 if (!name) 264 return AVERROR(EINVAL); 265 266 /* First check if the label is not in the open_outputs list */ 267 match = extract_inout(name, open_outputs); 268 269 if (match) { 270 av_free(name); 271 } else { 272 /* Not in the list, so add it as an input */ 273 if (!(match = av_mallocz(sizeof(AVFilterInOut)))) 274 return AVERROR(ENOMEM); 275 match->name = name; 276 match->pad_idx = pad; 277 } 278 279 insert_inout(curr_inputs, match); 280 281 *buf += strspn(*buf, WHITESPACES); 282 pad++; 283 } 284 285 return pad; 286} 287 288static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 289 AVFilterInOut **open_inputs, 290 AVFilterInOut **open_outputs, void *log_ctx) 291{ 292 int ret, pad = 0; 293 294 while (**buf == '[') { 295 char *name = parse_link_name(buf, log_ctx); 296 AVFilterInOut *match; 297 298 AVFilterInOut *input = *curr_inputs; 299 if (!input) { 300 av_log(log_ctx, AV_LOG_ERROR, 301 "No output pad can be associated to link label '%s'.\n", 302 name); 303 return AVERROR(EINVAL); 304 } 305 *curr_inputs = (*curr_inputs)->next; 306 307 if (!name) 308 return AVERROR(EINVAL); 309 310 /* First check if the label is not in the open_inputs list */ 311 match = extract_inout(name, open_inputs); 312 313 if (match) { 314 if ((ret = link_filter(input->filter_ctx, input->pad_idx, 315 match->filter_ctx, match->pad_idx, log_ctx)) < 0) 316 return ret; 317 av_free(match->name); 318 av_free(name); 319 av_free(match); 320 av_free(input); 321 } else { 322 /* Not in the list, so add the first input as a open_output */ 323 input->name = name; 324 insert_inout(open_outputs, input); 325 } 326 *buf += strspn(*buf, WHITESPACES); 327 pad++; 328 } 329 330 return pad; 331} 332 333int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 334 AVFilterInOut *open_inputs, 335 AVFilterInOut *open_outputs, void *log_ctx) 336{ 337 int index = 0, ret; 338 char chr = 0; 339 340 AVFilterInOut *curr_inputs = NULL; 341 342 do { 343 AVFilterContext *filter; 344 const char *filterchain = filters; 345 filters += strspn(filters, WHITESPACES); 346 347 if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) 348 goto fail; 349 350 if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) 351 goto fail; 352 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 ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) 357 goto fail; 358 } 359 360 if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) 361 goto fail; 362 363 if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 364 log_ctx)) < 0) 365 goto fail; 366 367 filters += strspn(filters, WHITESPACES); 368 chr = *filters++; 369 370 if (chr == ';' && curr_inputs) { 371 av_log(log_ctx, AV_LOG_ERROR, 372 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", 373 filterchain); 374 ret = AVERROR(EINVAL); 375 goto fail; 376 } 377 index++; 378 } while (chr == ',' || chr == ';'); 379 380 if (chr) { 381 av_log(log_ctx, AV_LOG_ERROR, 382 "Unable to parse graph description substring: \"%s\"\n", 383 filters - 1); 384 ret = AVERROR(EINVAL); 385 goto fail; 386 } 387 388 if (open_inputs && !strcmp(open_inputs->name, "out") && curr_inputs) { 389 /* Last output can be omitted if it is "[out]" */ 390 const char *tmp = "[out]"; 391 if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, 392 log_ctx)) < 0) 393 goto fail; 394 } 395 396 return 0; 397 398 fail: 399 for (; graph->filter_count > 0; graph->filter_count--) 400 avfilter_free(graph->filters[graph->filter_count - 1]); 401 av_freep(&graph->filters); 402 free_inout(open_inputs); 403 free_inout(open_outputs); 404 free_inout(curr_inputs); 405 return ret; 406} 407