1/* 2 * filter graph parser 3 * Copyright (c) 2008 Vitor Sessak 4 * Copyright (c) 2007 Bobby Bingham 5 * 6 * This file is part of FFmpeg. 7 * 8 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23#include <string.h> 24#include <stdio.h> 25 26#include "libavutil/avstring.h" 27#include "libavutil/mem.h" 28#include "avfilter.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 = NULL; 100 int ret; 101 102 snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%d", filt_name, index); 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 *filt_ctx = avfilter_graph_alloc_filter(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 AVERROR(ENOMEM); 117 } 118 119 if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags") && 120 ctx->scale_sws_opts) { 121 tmp_args = av_asprintf("%s:%s", 122 args, ctx->scale_sws_opts); 123 if (!tmp_args) 124 return AVERROR(ENOMEM); 125 args = tmp_args; 126 } 127 128 ret = avfilter_init_str(*filt_ctx, args); 129 if (ret < 0) { 130 av_log(log_ctx, AV_LOG_ERROR, 131 "Error initializing filter '%s'", filt_name); 132 if (args) 133 av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args); 134 av_log(log_ctx, AV_LOG_ERROR, "\n"); 135 avfilter_free(*filt_ctx); 136 *filt_ctx = NULL; 137 } 138 139 av_free(tmp_args); 140 return ret; 141} 142 143/** 144 * Parse a string of the form FILTER_NAME[=PARAMS], and create a 145 * corresponding filter instance which is added to graph with 146 * create_filter(). 147 * 148 * @param filt_ctx Pointer that is set to the created and configured filter 149 * context on success, set to NULL on failure. 150 * @param filt_ctx put here a pointer to the created filter context on 151 * success, NULL otherwise 152 * @param buf pointer to the buffer to parse, *buf will be updated to 153 * point to the char next after the parsed string 154 * @param index an index which is assigned to the created filter 155 * instance, and which is supposed to be unique for each filter 156 * instance added to the filtergraph 157 * @return >= 0 in case of success, a negative AVERROR code otherwise 158 */ 159static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph, 160 int index, void *log_ctx) 161{ 162 char *opts = NULL; 163 char *name = av_get_token(buf, "=,;[\n"); 164 int ret; 165 166 if (**buf == '=') { 167 (*buf)++; 168 opts = av_get_token(buf, "[],;\n"); 169 } 170 171 ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx); 172 av_free(name); 173 av_free(opts); 174 return ret; 175} 176 177AVFilterInOut *avfilter_inout_alloc(void) 178{ 179 return av_mallocz(sizeof(AVFilterInOut)); 180} 181 182void avfilter_inout_free(AVFilterInOut **inout) 183{ 184 while (*inout) { 185 AVFilterInOut *next = (*inout)->next; 186 av_freep(&(*inout)->name); 187 av_freep(inout); 188 *inout = next; 189 } 190} 191 192static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links) 193{ 194 AVFilterInOut *ret; 195 196 while (*links && (!(*links)->name || strcmp((*links)->name, label))) 197 links = &((*links)->next); 198 199 ret = *links; 200 201 if (ret) { 202 *links = ret->next; 203 ret->next = NULL; 204 } 205 206 return ret; 207} 208 209static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element) 210{ 211 element->next = *inouts; 212 *inouts = element; 213} 214 215static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element) 216{ 217 while (*inouts && (*inouts)->next) 218 inouts = &((*inouts)->next); 219 220 if (!*inouts) 221 *inouts = *element; 222 else 223 (*inouts)->next = *element; 224 *element = NULL; 225} 226 227static int link_filter_inouts(AVFilterContext *filt_ctx, 228 AVFilterInOut **curr_inputs, 229 AVFilterInOut **open_inputs, void *log_ctx) 230{ 231 int pad, ret; 232 233 for (pad = 0; pad < filt_ctx->nb_inputs; pad++) { 234 AVFilterInOut *p = *curr_inputs; 235 236 if (p) { 237 *curr_inputs = (*curr_inputs)->next; 238 p->next = NULL; 239 } else if (!(p = av_mallocz(sizeof(*p)))) 240 return AVERROR(ENOMEM); 241 242 if (p->filter_ctx) { 243 ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx); 244 av_free(p->name); 245 av_free(p); 246 if (ret < 0) 247 return ret; 248 } else { 249 p->filter_ctx = filt_ctx; 250 p->pad_idx = pad; 251 append_inout(open_inputs, &p); 252 } 253 } 254 255 if (*curr_inputs) { 256 av_log(log_ctx, AV_LOG_ERROR, 257 "Too many inputs specified for the \"%s\" filter.\n", 258 filt_ctx->filter->name); 259 return AVERROR(EINVAL); 260 } 261 262 pad = filt_ctx->nb_outputs; 263 while (pad--) { 264 AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut)); 265 if (!currlinkn) 266 return AVERROR(ENOMEM); 267 currlinkn->filter_ctx = filt_ctx; 268 currlinkn->pad_idx = pad; 269 insert_inout(curr_inputs, currlinkn); 270 } 271 272 return 0; 273} 274 275static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs, 276 AVFilterInOut **open_outputs, void *log_ctx) 277{ 278 AVFilterInOut *parsed_inputs = NULL; 279 int pad = 0; 280 281 while (**buf == '[') { 282 char *name = parse_link_name(buf, log_ctx); 283 AVFilterInOut *match; 284 285 if (!name) 286 return AVERROR(EINVAL); 287 288 /* First check if the label is not in the open_outputs list */ 289 match = extract_inout(name, open_outputs); 290 291 if (match) { 292 av_free(name); 293 } else { 294 /* Not in the list, so add it as an input */ 295 if (!(match = av_mallocz(sizeof(AVFilterInOut)))) { 296 av_free(name); 297 return AVERROR(ENOMEM); 298 } 299 match->name = name; 300 match->pad_idx = pad; 301 } 302 303 append_inout(&parsed_inputs, &match); 304 305 *buf += strspn(*buf, WHITESPACES); 306 pad++; 307 } 308 309 append_inout(&parsed_inputs, curr_inputs); 310 *curr_inputs = parsed_inputs; 311 312 return pad; 313} 314 315static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs, 316 AVFilterInOut **open_inputs, 317 AVFilterInOut **open_outputs, void *log_ctx) 318{ 319 int ret, pad = 0; 320 321 while (**buf == '[') { 322 char *name = parse_link_name(buf, log_ctx); 323 AVFilterInOut *match; 324 325 AVFilterInOut *input = *curr_inputs; 326 327 if (!name) 328 return AVERROR(EINVAL); 329 330 if (!input) { 331 av_log(log_ctx, AV_LOG_ERROR, 332 "No output pad can be associated to link label '%s'.\n", name); 333 av_free(name); 334 return AVERROR(EINVAL); 335 } 336 *curr_inputs = (*curr_inputs)->next; 337 338 /* First check if the label is not in the open_inputs list */ 339 match = extract_inout(name, open_inputs); 340 341 if (match) { 342 if ((ret = link_filter(input->filter_ctx, input->pad_idx, 343 match->filter_ctx, match->pad_idx, log_ctx)) < 0) { 344 av_free(name); 345 return ret; 346 } 347 av_free(match->name); 348 av_free(name); 349 av_free(match); 350 av_free(input); 351 } else { 352 /* Not in the list, so add the first input as a open_output */ 353 input->name = name; 354 insert_inout(open_outputs, input); 355 } 356 *buf += strspn(*buf, WHITESPACES); 357 pad++; 358 } 359 360 return pad; 361} 362 363static int parse_sws_flags(const char **buf, AVFilterGraph *graph) 364{ 365 char *p = strchr(*buf, ';'); 366 367 if (strncmp(*buf, "sws_flags=", 10)) 368 return 0; 369 370 if (!p) { 371 av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n"); 372 return AVERROR(EINVAL); 373 } 374 375 *buf += 4; // keep the 'flags=' part 376 377 av_freep(&graph->scale_sws_opts); 378 if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1))) 379 return AVERROR(ENOMEM); 380 av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1); 381 382 *buf = p + 1; 383 return 0; 384} 385 386int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters, 387 AVFilterInOut **inputs, 388 AVFilterInOut **outputs) 389{ 390 int index = 0, ret = 0; 391 char chr = 0; 392 393 AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL; 394 395 filters += strspn(filters, WHITESPACES); 396 397 if ((ret = parse_sws_flags(&filters, graph)) < 0) 398 goto fail; 399 400 do { 401 AVFilterContext *filter; 402 filters += strspn(filters, WHITESPACES); 403 404 if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0) 405 goto end; 406 if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0) 407 goto end; 408 409 410 if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0) 411 goto end; 412 413 if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 414 graph)) < 0) 415 goto end; 416 417 filters += strspn(filters, WHITESPACES); 418 chr = *filters++; 419 420 if (chr == ';' && curr_inputs) 421 append_inout(&open_outputs, &curr_inputs); 422 index++; 423 } while (chr == ',' || chr == ';'); 424 425 if (chr) { 426 av_log(graph, AV_LOG_ERROR, 427 "Unable to parse graph description substring: \"%s\"\n", 428 filters - 1); 429 ret = AVERROR(EINVAL); 430 goto end; 431 } 432 433 append_inout(&open_outputs, &curr_inputs); 434 435 436 *inputs = open_inputs; 437 *outputs = open_outputs; 438 return 0; 439 440 fail:end: 441 while (graph->nb_filters) 442 avfilter_free(graph->filters[0]); 443 av_freep(&graph->filters); 444 avfilter_inout_free(&open_inputs); 445 avfilter_inout_free(&open_outputs); 446 avfilter_inout_free(&curr_inputs); 447 448 *inputs = NULL; 449 *outputs = NULL; 450 451 return ret; 452} 453 454#if HAVE_INCOMPATIBLE_LIBAV_ABI || !FF_API_OLD_GRAPH_PARSE 455int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 456 AVFilterInOut *open_inputs, 457 AVFilterInOut *open_outputs, void *log_ctx) 458{ 459 int ret; 460 AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL; 461 462 if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0) 463 goto fail; 464 465 /* First input can be omitted if it is "[in]" */ 466 if (inputs && !inputs->name) 467 inputs->name = av_strdup("in"); 468 for (cur = inputs; cur; cur = cur->next) { 469 if (!cur->name) { 470 av_log(log_ctx, AV_LOG_ERROR, 471 "Not enough inputs specified for the \"%s\" filter.\n", 472 cur->filter_ctx->filter->name); 473 ret = AVERROR(EINVAL); 474 goto fail; 475 } 476 if (!(match = extract_inout(cur->name, &open_outputs))) 477 continue; 478 ret = avfilter_link(match->filter_ctx, match->pad_idx, 479 cur->filter_ctx, cur->pad_idx); 480 avfilter_inout_free(&match); 481 if (ret < 0) 482 goto fail; 483 } 484 485 /* Last output can be omitted if it is "[out]" */ 486 if (outputs && !outputs->name) 487 outputs->name = av_strdup("out"); 488 for (cur = outputs; cur; cur = cur->next) { 489 if (!cur->name) { 490 av_log(log_ctx, AV_LOG_ERROR, 491 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", 492 filters); 493 ret = AVERROR(EINVAL); 494 goto fail; 495 } 496 if (!(match = extract_inout(cur->name, &open_inputs))) 497 continue; 498 ret = avfilter_link(cur->filter_ctx, cur->pad_idx, 499 match->filter_ctx, match->pad_idx); 500 avfilter_inout_free(&match); 501 if (ret < 0) 502 goto fail; 503 } 504 505 fail: 506 if (ret < 0) { 507 while (graph->nb_filters) 508 avfilter_free(graph->filters[0]); 509 av_freep(&graph->filters); 510 } 511 avfilter_inout_free(&inputs); 512 avfilter_inout_free(&outputs); 513 avfilter_inout_free(&open_inputs); 514 avfilter_inout_free(&open_outputs); 515 return ret; 516#else 517int avfilter_graph_parse(AVFilterGraph *graph, const char *filters, 518 AVFilterInOut **inputs, AVFilterInOut **outputs, 519 void *log_ctx) 520{ 521 return avfilter_graph_parse_ptr(graph, filters, inputs, outputs, log_ctx); 522#endif 523} 524 525int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, 526 AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr, 527 void *log_ctx) 528{ 529 int index = 0, ret = 0; 530 char chr = 0; 531 532 AVFilterInOut *curr_inputs = NULL; 533 AVFilterInOut *open_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL; 534 AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL; 535 536 if ((ret = parse_sws_flags(&filters, graph)) < 0) 537 goto end; 538 539 do { 540 AVFilterContext *filter; 541 const char *filterchain = filters; 542 filters += strspn(filters, WHITESPACES); 543 544 if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0) 545 goto end; 546 547 if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0) 548 goto end; 549 550 if (filter->nb_inputs == 1 && !curr_inputs && !index) { 551 /* First input pad, assume it is "[in]" if not specified */ 552 const char *tmp = "[in]"; 553 if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0) 554 goto end; 555 } 556 557 if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0) 558 goto end; 559 560 if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs, 561 log_ctx)) < 0) 562 goto end; 563 564 filters += strspn(filters, WHITESPACES); 565 chr = *filters++; 566 567 if (chr == ';' && curr_inputs) { 568 av_log(log_ctx, AV_LOG_ERROR, 569 "Invalid filterchain containing an unlabelled output pad: \"%s\"\n", 570 filterchain); 571 ret = AVERROR(EINVAL); 572 goto end; 573 } 574 index++; 575 } while (chr == ',' || chr == ';'); 576 577 if (chr) { 578 av_log(log_ctx, AV_LOG_ERROR, 579 "Unable to parse graph description substring: \"%s\"\n", 580 filters - 1); 581 ret = AVERROR(EINVAL); 582 goto end; 583 } 584 585 if (curr_inputs) { 586 /* Last output pad, assume it is "[out]" if not specified */ 587 const char *tmp = "[out]"; 588 if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs, 589 log_ctx)) < 0) 590 goto end; 591 } 592 593end: 594 /* clear open_in/outputs only if not passed as parameters */ 595 if (open_inputs_ptr) *open_inputs_ptr = open_inputs; 596 else avfilter_inout_free(&open_inputs); 597 if (open_outputs_ptr) *open_outputs_ptr = open_outputs; 598 else avfilter_inout_free(&open_outputs); 599 avfilter_inout_free(&curr_inputs); 600 601 if (ret < 0) { 602 while (graph->nb_filters) 603 avfilter_free(graph->filters[0]); 604 av_freep(&graph->filters); 605 } 606 return ret; 607} 608