Originally committed as revision 12735 to svn://svn.ffmpeg.org/ffmpeg/trunk
Showing the changes to libavfilter/avfiltergraph.c (1 of 1 file). Show all
Unified · Split
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * filter graphs |
| 3 | + * copyright (c) 2008 Vitor Sessak | |
| 3 | 4 | * copyright (c) 2007 Bobby Bingham |
| 4 | 5 | * |
| 5 | 6 | * This file is part of FFmpeg. |
| @@ -19,6 +20,9 @@ · 13 unmodified lines | ||
| 19 | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | 21 | */ |
| 21 | 22 | |
| 23 | +#include <ctype.h> | |
| 24 | +#include <string.h> | |
| 25 | + | |
| 22 | 26 | #include "avfilter.h" |
| 23 | 27 | #include "avfiltergraph.h" |
| 24 | 28 | |
| @@ -249,3 +253,337 @@ fail: · 224 unmodified lines | ||
| 249 | 253 | uninit(graph); |
| 250 | 254 | return -1; |
| 251 | 255 | } |
| 256 | + | |
| 257 | + | |
| 258 | +static void consume_whitespace(const char **buf) | |
| 259 | +{ | |
| 260 | + *buf += strspn(*buf, " \n\t"); | |
| 261 | +} | |
| 262 | + | |
| 263 | +/** | |
| 264 | + * get the next non-whitespace char | |
| 265 | + */ | |
| 266 | +static char consume_char(const char **buf) | |
| 267 | +{ | |
| 268 | + char out; | |
| 269 | + consume_whitespace(buf); | |
| 270 | + | |
| 271 | + out = **buf; | |
| 272 | + | |
| 273 | + if (out) | |
| 274 | + (*buf)++; | |
| 275 | + | |
| 276 | + return out; | |
| 277 | +} | |
| 278 | + | |
| 279 | +/** | |
| 280 | + * remove the quotation marks from a string. Ex: "aaa'bb'cc" -> "aaabbcc" | |
| 281 | + */ | |
| 282 | +static void unquote(char *str) | |
| 283 | +{ | |
| 284 | + char *p1, *p2; | |
| 285 | + p1=p2=str; | |
| 286 | + while (*p1 != 0) { | |
| 287 | + if (*p1 != '\'') | |
| 288 | + *p2++ = *p1; | |
| 289 | + p1++; | |
| 290 | + } | |
| 291 | + | |
| 292 | + *p2 = 0; | |
| 293 | +} | |
| 294 | + | |
| 295 | +/** | |
| 296 | + * Consumes a string from *buf. | |
| 297 | + * @return a copy of the consumed string, which should be free'd after use | |
| 298 | + */ | |
| 299 | +static char *consume_string(const char **buf) | |
| 300 | +{ | |
| 301 | + const char *start; | |
| 302 | + char *ret; | |
| 303 | + int size; | |
| 304 | + | |
| 305 | + consume_whitespace(buf); | |
| 306 | + | |
| 307 | + if (!(**buf)) | |
| 308 | + return av_mallocz(1); | |
| 309 | + | |
| 310 | + start = *buf; | |
| 311 | + | |
| 312 | + *buf += strcspn(*buf, " ()=,'"); | |
| 313 | + | |
| 314 | + if (**buf == '\'') { | |
| 315 | + char *p = strchr(*buf + 1, '\''); | |
| 316 | + if (p) | |
| 317 | + *buf = p + 1; | |
| 318 | + else | |
| 319 | + *buf += strlen(*buf); // Move the pointer to the null end byte | |
| 320 | + } | |
| 321 | + | |
| 322 | + size = *buf - start + 1; | |
| 323 | + ret = av_malloc(size); | |
| 324 | + memcpy(ret, start, size - 1); | |
| 325 | + ret[size-1] = 0; | |
| 326 | + | |
| 327 | + unquote(ret); | |
| 328 | + | |
| 329 | + return ret; | |
| 330 | +} | |
| 331 | + | |
| 332 | +/** | |
| 333 | + * Parse "(linkname)" | |
| 334 | + * @arg name a pointer (that need to be free'd after use) to the name between | |
| 335 | + * parenthesis | |
| 336 | + */ | |
| 337 | +static void parse_link_name(const char **buf, char **name) | |
| 338 | +{ | |
| 339 | + consume_char(buf); | |
| 340 | + | |
| 341 | + *name = consume_string(buf); | |
| 342 | + | |
| 343 | + if (!*name[0]) | |
| 344 | + goto fail; | |
| 345 | + | |
| 346 | + if (consume_char(buf) != ')') | |
| 347 | + goto fail; | |
| 348 | + | |
| 349 | + return; | |
| 350 | + fail: | |
| 351 | + av_freep(name); | |
| 352 | + av_log(&log_ctx, AV_LOG_ERROR, "Could not parse link name!\n"); | |
| 353 | +} | |
| 354 | + | |
| 355 | +/** | |
| 356 | + * Parse "filter=params" | |
| 357 | + * @arg name a pointer (that need to be free'd after use) to the name of the | |
| 358 | + * filter | |
| 359 | + * @arg ars a pointer (that need to be free'd after use) to the args of the | |
| 360 | + * filter | |
| 361 | + */ | |
| 362 | +static void parse_filter(const char **buf, char **name, char **opts) | |
| 363 | +{ | |
| 364 | + *name = consume_string(buf); | |
| 365 | + | |
| 366 | + if (**buf == '=') { | |
| 367 | + consume_char(buf); | |
| 368 | + *opts = consume_string(buf); | |
| 369 | + } else { | |
| 370 | + *opts = NULL; | |
| 371 | + } | |
| 372 | + | |
| 373 | +} | |
| 374 | + | |
| 375 | +enum LinkType { | |
| 376 | + LinkTypeIn, | |
| 377 | + LinkTypeOut, | |
| 378 | +}; | |
| 379 | + | |
| 380 | +/** | |
| 381 | + * A linked-list of the inputs/outputs of the filter chain. | |
| 382 | + */ | |
| 383 | +typedef struct AVFilterInOut { | |
| 384 | + enum LinkType type; | |
| 385 | + char *name; | |
| 386 | + int instance; | |
| 387 | + int pad_idx; | |
| 388 | + | |
| 389 | + struct AVFilterInOut *next; | |
| 390 | +} AVFilterInOut; | |
| 391 | + | |
| 392 | +static void free_inout(AVFilterInOut *head) | |
| 393 | +{ | |
| 394 | + while (head) { | |
| 395 | + AVFilterInOut *next; | |
| 396 | + next = head->next; | |
| 397 | + av_free(head); | |
| 398 | + head = next; | |
| 399 | + } | |
| 400 | +} | |
| 401 | + | |
| 402 | +/** | |
| 403 | + * Parse "(a1)(link2) ... (etc)" | |
| 404 | + */ | |
| 405 | +static int parse_inouts(const char **buf, AVFilterInOut **inout, int firstpad, | |
| 406 | + enum LinkType type, int instance) | |
| 407 | +{ | |
| 408 | + int pad = firstpad; | |
| 409 | + while (**buf == '(') { | |
| 410 | + AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut)); | |
| 411 | + parse_link_name(buf, &inoutn->name); | |
| 412 | + inoutn->type = type; | |
| 413 | + inoutn->instance = instance; | |
| 414 | + inoutn->pad_idx = pad++; | |
| 415 | + inoutn->next = *inout; | |
| 416 | + *inout = inoutn; | |
| 417 | + } | |
| 418 | + return pad; | |
| 419 | +} | |
| 420 | + | |
| 421 | +static AVFilterGraphDesc *parse_chain(const char *filters, int has_in) | |
| 422 | +{ | |
| 423 | + AVFilterGraphDesc *ret; | |
| 424 | + AVFilterGraphDescFilter **filterp, *filtern; | |
| 425 | + AVFilterGraphDescLink **linkp, *linkn; | |
| 426 | + AVFilterInOut *inout=NULL; | |
| 427 | + AVFilterInOut *head; | |
| 428 | + | |
| 429 | + int index = 0; | |
| 430 | + char chr = 0; | |
| 431 | + int pad = 0; | |
| 432 | + int has_out = 0; | |
| 433 | + | |
| 434 | + consume_whitespace(&filters); | |
| 435 | + | |
| 436 | + if(!(ret = av_mallocz(sizeof(AVFilterGraphDesc)))) | |
| 437 | + return NULL; | |
| 438 | + | |
| 439 | + filterp = &ret->filters; | |
| 440 | + linkp = &ret->links; | |
| 441 | + | |
| 442 | + do { | |
| 443 | + if(chr == ',') { | |
| 444 | + linkn = av_mallocz(sizeof(AVFilterGraphDescLink)); | |
| 445 | + linkn->src = index-1; | |
| 446 | + linkn->srcpad = pad; | |
| 447 | + linkn->dst = index; | |
| 448 | + linkn->dstpad = 0; | |
| 449 | + | |
| 450 | + *linkp = linkn; | |
| 451 | + linkp = &linkn->next; | |
| 452 | + } | |
| 453 | + pad = parse_inouts(&filters, &inout, chr == ',' || (!has_in), | |
| 454 | + LinkTypeIn, index); | |
| 455 | + | |
| 456 | + filtern = av_mallocz(sizeof(AVFilterGraphDescFilter)); | |
| 457 | + filtern->index = index; | |
| 458 | + parse_filter(&filters, &filtern->filter, &filtern->args); | |
| 459 | + *filterp = filtern; | |
| 460 | + filterp = &filtern->next; | |
| 461 | + | |
| 462 | + pad = parse_inouts(&filters, &inout, 0, | |
| 463 | + LinkTypeOut, index); | |
| 464 | + chr = consume_char(&filters); | |
| 465 | + index++; | |
| 466 | + } while (chr == ',' || chr == ';'); | |
| 467 | + | |
| 468 | + head = inout; | |
| 469 | + for (; inout != NULL; inout = inout->next) { | |
| 470 | + if (inout->instance == -1) | |
| 471 | + continue; // Already processed | |
| 472 | + | |
| 473 | + if (!strcmp(inout->name, "in")) { | |
| 474 | + if (!has_in) | |
| 475 | + goto fail; | |
| 476 | + ret->inputs = av_mallocz(sizeof(AVFilterGraphDescExport)); | |
| 477 | + ret->inputs->filter = inout->instance; | |
| 478 | + ret->inputs->pad = inout->pad_idx; | |
| 479 | + } else if (!strcmp(inout->name, "out")) { | |
| 480 | + has_out = 1; | |
| 481 | + ret->outputs = av_mallocz(sizeof(AVFilterGraphDescExport)); | |
| 482 | + ret->outputs->filter = inout->instance; | |
| 483 | + ret->outputs->pad = inout->pad_idx; | |
| 484 | + } else { | |
| 485 | + AVFilterInOut *p, *src, *dst; | |
| 486 | + for (p = inout->next; | |
| 487 | + p && strcmp(p->name,inout->name); p = p->next); | |
| 488 | + | |
| 489 | + if (!p) { | |
| 490 | + av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n", | |
| 491 | + inout->name); | |
| 492 | + goto fail; | |
| 493 | + } | |
| 494 | + | |
| 495 | + if (p->type == LinkTypeIn && inout->type == LinkTypeOut) { | |
| 496 | + src = inout; | |
| 497 | + dst = p; | |
| 498 | + } else if (p->type == LinkTypeOut && inout->type == LinkTypeIn) { | |
| 499 | + src = p; | |
| 500 | + dst = inout; | |
| 501 | + } else { | |
| 502 | + av_log(&log_ctx, AV_LOG_ERROR, "Two links named '%s' are either both input or both output\n", | |
| 503 | + inout->name); | |
| 504 | + goto fail; | |
| 505 | + } | |
| 506 | + linkn = av_mallocz(sizeof(AVFilterGraphDescLink)); | |
| 507 | + | |
| 508 | + linkn->src = src->instance; | |
| 509 | + linkn->srcpad = src->pad_idx; | |
| 510 | + linkn->dst = dst->instance; | |
| 511 | + linkn->dstpad = dst->pad_idx; | |
| 512 | + | |
| 513 | + *linkp = linkn; | |
| 514 | + linkp = &linkn->next; | |
| 515 | + | |
| 516 | + src->instance = -1; | |
| 517 | + dst->instance = -1; | |
| 518 | + } | |
| 519 | + } | |
| 520 | + | |
| 521 | + free_inout(head); | |
| 522 | + | |
| 523 | + if (!has_in) { | |
| 524 | + ret->inputs = av_mallocz(sizeof(AVFilterGraphDescExport)); | |
| 525 | + ret->inputs->filter = 0; | |
| 526 | + } | |
| 527 | + if (!has_out) { | |
| 528 | + ret->outputs = av_mallocz(sizeof(AVFilterGraphDescExport)); | |
| 529 | + ret->outputs->filter = index-1; | |
| 530 | + } | |
| 531 | + | |
| 532 | + return ret; | |
| 533 | + | |
| 534 | + fail: | |
| 535 | + free_inout(head); | |
| 536 | + | |
| 537 | + avfilter_graph_free_desc(ret); | |
| 538 | + return NULL; | |
| 539 | +} | |
| 540 | + | |
| 541 | +/** | |
| 542 | + * Parse a string describing a filter graph. | |
| 543 | + */ | |
| 544 | +AVFilterGraphDesc *avfilter_graph_parse_chain(const char *filters) | |
| 545 | +{ | |
| 546 | + AVFilterGraphDesc *ret; | |
| 547 | + | |
| 548 | + /* Try first to parse supposing there is no (in) element */ | |
| 549 | + if ((ret = parse_chain(filters, 0))) | |
| 550 | + return ret; | |
| 551 | + | |
| 552 | + /* Parse supposing there is an (in) element */ | |
| 553 | + return parse_chain(filters, 1); | |
| 554 | +} | |
| 555 | + | |
| 556 | +/** | |
| 557 | + * Free a graph description. | |
| 558 | + */ | |
| 559 | +void avfilter_graph_free_desc(AVFilterGraphDesc *desc) | |
| 560 | +{ | |
| 561 | + void *next; | |
| 562 | + | |
| 563 | + while(desc->filters) { | |
| 564 | + next = desc->filters->next; | |
| 565 | + av_free(desc->filters->filter); | |
| 566 | + av_free(desc->filters->args); | |
| 567 | + av_free(desc->filters); | |
| 568 | + desc->filters = next; | |
| 569 | + } | |
| 570 | + | |
| 571 | + while(desc->links) { | |
| 572 | + next = desc->links->next; | |
| 573 | + av_free(desc->links); | |
| 574 | + desc->links = next; | |
| 575 | + } | |
| 576 | + | |
| 577 | + while(desc->inputs) { | |
| 578 | + next = desc->inputs->next; | |
| 579 | + av_free(desc->inputs); | |
| 580 | + desc->inputs = next; | |
| 581 | + } | |
| 582 | + | |
| 583 | + while(desc->outputs) { | |
| 584 | + next = desc->outputs->next; | |
| 585 | + av_free(desc->outputs); | |
| 586 | + desc->outputs = next; | |
| 587 | + } | |
| 588 | +} | |
| 589 | + | |