1 /* 2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 12 /* This is a simple program that reads ivf files and decodes them 13 * using the new interface. Decoded frames are output as YV12 raw. 14 */ 15 #include <assert.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <stdarg.h> 19 #include <string.h> 20 #include <limits.h> 21 22 #define VPX_CODEC_DISABLE_COMPAT 1 23 #include "vpx_config.h" 24 #include "vpx/vpx_decoder.h" 25 #include "vpx_ports/vpx_timer.h" 26 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER 27 #include "vpx/vp8dx.h" 28 #endif 29 #if CONFIG_MD5 30 #include "md5_utils.h" 31 #endif 32 #include "tools_common.h" 33 #include "nestegg/include/nestegg/nestegg.h" 34 #include "third_party/libyuv/include/libyuv/scale.h" 35 36 static const char *exec_name; 37 38 static const struct { 39 char const *name; 40 const vpx_codec_iface_t *(*iface)(void); 41 unsigned int fourcc; 42 unsigned int fourcc_mask; 43 } ifaces[] = { 44 #if CONFIG_VP8_DECODER 45 {"vp8", vpx_codec_vp8_dx, VP8_FOURCC_MASK, 0x00FFFFFF}, 46 #endif 47 #if CONFIG_VP9_DECODER 48 {"vp9", vpx_codec_vp9_dx, VP9_FOURCC_MASK, 0x00FFFFFF}, 49 #endif 50 }; 51 52 #include "args.h" 53 static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1, 54 "Number of times to decode the file"); 55 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, 56 "Codec to use"); 57 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, 58 "Output raw YV12 frames"); 59 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, 60 "Output raw I420 frames"); 61 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0, 62 "Flip the chroma planes in the output"); 63 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0, 64 "Don't process the decoded frames"); 65 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0, 66 "Show progress after each frame decodes"); 67 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1, 68 "Stop decoding after n frames"); 69 static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1, 70 "Skip the first n input frames"); 71 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0, 72 "Postprocess decoded frames"); 73 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0, 74 "Show timing summary"); 75 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, 76 "Output file name pattern (see below)"); 77 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1, 78 "Max threads to use"); 79 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, 80 "Show version string"); 81 static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0, 82 "Enable decoder error-concealment"); 83 static const arg_def_t scalearg = ARG_DEF("S", "scale", 0, 84 "Scale output frames uniformly"); 85 86 87 #if CONFIG_MD5 88 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0, 89 "Compute the MD5 sum of the decoded frame"); 90 #endif 91 static const arg_def_t *all_args[] = { 92 &codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg, 93 &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile, 94 &threadsarg, &verbosearg, &scalearg, 95 #if CONFIG_MD5 96 &md5arg, 97 #endif 98 &error_concealment, 99 NULL 100 }; 101 102 #if CONFIG_VP8_DECODER 103 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1, 104 "Enable VP8 postproc add noise"); 105 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0, 106 "Enable VP8 deblocking"); 107 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1, 108 "Enable VP8 demacroblocking, w/ level"); 109 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1, 110 "Enable VP8 visible debug info"); 111 static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1, 112 "Display only selected reference frame per macro block"); 113 static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1, 114 "Display only selected macro block modes"); 115 static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1, 116 "Display only selected block modes"); 117 static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1, 118 "Draw only selected motion vectors"); 119 static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0, 120 "Enable multiframe quality enhancement"); 121 122 static const arg_def_t *vp8_pp_args[] = { 123 &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info, 124 &pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe, 125 NULL 126 }; 127 #endif 128 129 void usage_exit() { 130 int i; 131 132 fprintf(stderr, "Usage: %s <options> filename\n\n" 133 "Options:\n", exec_name); 134 arg_show_usage(stderr, all_args); 135 #if CONFIG_VP8_DECODER 136 fprintf(stderr, "\nVP8 Postprocessing Options:\n"); 137 arg_show_usage(stderr, vp8_pp_args); 138 #endif 139 fprintf(stderr, 140 "\nOutput File Patterns:\n\n" 141 " The -o argument specifies the name of the file(s) to " 142 "write to. If the\n argument does not include any escape " 143 "characters, the output will be\n written to a single file. " 144 "Otherwise, the filename will be calculated by\n expanding " 145 "the following escape characters:\n"); 146 fprintf(stderr, 147 "\n\t%%w - Frame width" 148 "\n\t%%h - Frame height" 149 "\n\t%%<n> - Frame number, zero padded to <n> places (1..9)" 150 "\n\n Pattern arguments are only supported in conjunction " 151 "with the --yv12 and\n --i420 options. If the -o option is " 152 "not specified, the output will be\n directed to stdout.\n" 153 ); 154 fprintf(stderr, "\nIncluded decoders:\n\n"); 155 156 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 157 fprintf(stderr, " %-6s - %s\n", 158 ifaces[i].name, 159 vpx_codec_iface_name(ifaces[i].iface())); 160 161 exit(EXIT_FAILURE); 162 } 163 164 static unsigned int mem_get_le16(const void *vmem) { 165 unsigned int val; 166 const unsigned char *mem = (const unsigned char *)vmem; 167 168 val = mem[1] << 8; 169 val |= mem[0]; 170 return val; 171 } 172 173 static unsigned int mem_get_le32(const void *vmem) { 174 unsigned int val; 175 const unsigned char *mem = (const unsigned char *)vmem; 176 177 val = mem[3] << 24; 178 val |= mem[2] << 16; 179 val |= mem[1] << 8; 180 val |= mem[0]; 181 return val; 182 } 183 184 enum file_kind { 185 RAW_FILE, 186 IVF_FILE, 187 WEBM_FILE 188 }; 189 190 struct input_ctx { 191 enum file_kind kind; 192 FILE *infile; 193 nestegg *nestegg_ctx; 194 nestegg_packet *pkt; 195 unsigned int chunk; 196 unsigned int chunks; 197 unsigned int video_track; 198 }; 199 200 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t)) 201 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t)) 202 static int read_frame(struct input_ctx *input, 203 uint8_t **buf, 204 size_t *buf_sz, 205 size_t *buf_alloc_sz) { 206 char raw_hdr[IVF_FRAME_HDR_SZ]; 207 size_t new_buf_sz; 208 FILE *infile = input->infile; 209 enum file_kind kind = input->kind; 210 if (kind == WEBM_FILE) { 211 if (input->chunk >= input->chunks) { 212 unsigned int track; 213 214 do { 215 /* End of this packet, get another. */ 216 if (input->pkt) 217 nestegg_free_packet(input->pkt); 218 219 if (nestegg_read_packet(input->nestegg_ctx, &input->pkt) <= 0 220 || nestegg_packet_track(input->pkt, &track)) 221 return 1; 222 223 } while (track != input->video_track); 224 225 if (nestegg_packet_count(input->pkt, &input->chunks)) 226 return 1; 227 input->chunk = 0; 228 } 229 230 if (nestegg_packet_data(input->pkt, input->chunk, buf, buf_sz)) 231 return 1; 232 input->chunk++; 233 234 return 0; 235 } 236 /* For both the raw and ivf formats, the frame size is the first 4 bytes 237 * of the frame header. We just need to special case on the header 238 * size. 239 */ 240 else if (fread(raw_hdr, kind == IVF_FILE 241 ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, infile) != 1) { 242 if (!feof(infile)) 243 fprintf(stderr, "Failed to read frame size\n"); 244 245 new_buf_sz = 0; 246 } else { 247 new_buf_sz = mem_get_le32(raw_hdr); 248 249 if (new_buf_sz > 256 * 1024 * 1024) { 250 fprintf(stderr, "Error: Read invalid frame size (%u)\n", 251 (unsigned int)new_buf_sz); 252 new_buf_sz = 0; 253 } 254 255 if (kind == RAW_FILE && new_buf_sz > 256 * 1024) 256 fprintf(stderr, "Warning: Read invalid frame size (%u)" 257 " - not a raw file?\n", (unsigned int)new_buf_sz); 258 259 if (new_buf_sz > *buf_alloc_sz) { 260 uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz); 261 262 if (new_buf) { 263 *buf = new_buf; 264 *buf_alloc_sz = 2 * new_buf_sz; 265 } else { 266 fprintf(stderr, "Failed to allocate compressed data buffer\n"); 267 new_buf_sz = 0; 268 } 269 } 270 } 271 272 *buf_sz = new_buf_sz; 273 274 if (!feof(infile)) { 275 if (fread(*buf, 1, *buf_sz, infile) != *buf_sz) { 276 fprintf(stderr, "Failed to read full frame\n"); 277 return 1; 278 } 279 280 return 0; 281 } 282 283 return 1; 284 } 285 286 void *out_open(const char *out_fn, int do_md5) { 287 void *out = NULL; 288 289 if (do_md5) { 290 #if CONFIG_MD5 291 MD5Context *md5_ctx = out = malloc(sizeof(MD5Context)); 292 (void)out_fn; 293 MD5Init(md5_ctx); 294 #endif 295 } else { 296 FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") 297 : set_binary_mode(stdout); 298 299 if (!outfile) { 300 fprintf(stderr, "Failed to output file"); 301 exit(EXIT_FAILURE); 302 } 303 } 304 305 return out; 306 } 307 308 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5) { 309 if (do_md5) { 310 #if CONFIG_MD5 311 MD5Update(out, buf, len); 312 #endif 313 } else { 314 (void) fwrite(buf, 1, len, out); 315 } 316 } 317 318 void out_close(void *out, const char *out_fn, int do_md5) { 319 if (do_md5) { 320 #if CONFIG_MD5 321 uint8_t md5[16]; 322 int i; 323 324 MD5Final(md5, out); 325 free(out); 326 327 for (i = 0; i < 16; i++) 328 printf("%02x", md5[i]); 329 330 printf(" %s\n", out_fn); 331 #endif 332 } else { 333 fclose(out); 334 } 335 } 336 337 unsigned int file_is_ivf(FILE *infile, 338 unsigned int *fourcc, 339 unsigned int *width, 340 unsigned int *height, 341 unsigned int *fps_den, 342 unsigned int *fps_num) { 343 char raw_hdr[32]; 344 int is_ivf = 0; 345 346 if (fread(raw_hdr, 1, 32, infile) == 32) { 347 if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K' 348 && raw_hdr[2] == 'I' && raw_hdr[3] == 'F') { 349 is_ivf = 1; 350 351 if (mem_get_le16(raw_hdr + 4) != 0) 352 fprintf(stderr, "Error: Unrecognized IVF version! This file may not" 353 " decode properly."); 354 355 *fourcc = mem_get_le32(raw_hdr + 8); 356 *width = mem_get_le16(raw_hdr + 12); 357 *height = mem_get_le16(raw_hdr + 14); 358 *fps_num = mem_get_le32(raw_hdr + 16); 359 *fps_den = mem_get_le32(raw_hdr + 20); 360 361 /* Some versions of vpxenc used 1/(2*fps) for the timebase, so 362 * we can guess the framerate using only the timebase in this 363 * case. Other files would require reading ahead to guess the 364 * timebase, like we do for webm. 365 */ 366 if (*fps_num < 1000) { 367 /* Correct for the factor of 2 applied to the timebase in the 368 * encoder. 369 */ 370 if (*fps_num & 1)*fps_den <<= 1; 371 else *fps_num >>= 1; 372 } else { 373 /* Don't know FPS for sure, and don't have readahead code 374 * (yet?), so just default to 30fps. 375 */ 376 *fps_num = 30; 377 *fps_den = 1; 378 } 379 } 380 } 381 382 if (!is_ivf) 383 rewind(infile); 384 385 return is_ivf; 386 } 387 388 389 unsigned int file_is_raw(FILE *infile, 390 unsigned int *fourcc, 391 unsigned int *width, 392 unsigned int *height, 393 unsigned int *fps_den, 394 unsigned int *fps_num) { 395 unsigned char buf[32]; 396 int is_raw = 0; 397 vpx_codec_stream_info_t si; 398 399 si.sz = sizeof(si); 400 401 if (fread(buf, 1, 32, infile) == 32) { 402 int i; 403 404 if (mem_get_le32(buf) < 256 * 1024 * 1024) 405 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 406 if (!vpx_codec_peek_stream_info(ifaces[i].iface(), 407 buf + 4, 32 - 4, &si)) { 408 is_raw = 1; 409 *fourcc = ifaces[i].fourcc; 410 *width = si.w; 411 *height = si.h; 412 *fps_num = 30; 413 *fps_den = 1; 414 break; 415 } 416 } 417 418 rewind(infile); 419 return is_raw; 420 } 421 422 423 static int 424 nestegg_read_cb(void *buffer, size_t length, void *userdata) { 425 FILE *f = userdata; 426 427 if (fread(buffer, 1, length, f) < length) { 428 if (ferror(f)) 429 return -1; 430 if (feof(f)) 431 return 0; 432 } 433 return 1; 434 } 435 436 437 static int 438 nestegg_seek_cb(int64_t offset, int whence, void *userdata) { 439 switch (whence) { 440 case NESTEGG_SEEK_SET: 441 whence = SEEK_SET; 442 break; 443 case NESTEGG_SEEK_CUR: 444 whence = SEEK_CUR; 445 break; 446 case NESTEGG_SEEK_END: 447 whence = SEEK_END; 448 break; 449 }; 450 return fseek(userdata, (long)offset, whence) ? -1 : 0; 451 } 452 453 454 static int64_t 455 nestegg_tell_cb(void *userdata) { 456 return ftell(userdata); 457 } 458 459 460 static void 461 nestegg_log_cb(nestegg *context, unsigned int severity, char const *format, 462 ...) { 463 va_list ap; 464 465 va_start(ap, format); 466 vfprintf(stderr, format, ap); 467 fprintf(stderr, "\n"); 468 va_end(ap); 469 } 470 471 472 static int 473 webm_guess_framerate(struct input_ctx *input, 474 unsigned int *fps_den, 475 unsigned int *fps_num) { 476 unsigned int i; 477 uint64_t tstamp = 0; 478 479 /* Check to see if we can seek before we parse any data. */ 480 if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) { 481 fprintf(stderr, 482 "WARNING: Failed to guess framerate (no Cues), set to 30fps.\n"); 483 *fps_num = 30; 484 *fps_den = 1; 485 return 0; 486 } 487 488 /* Guess the framerate. Read up to 1 second, or 50 video packets, 489 * whichever comes first. 490 */ 491 for (i = 0; tstamp < 1000000000 && i < 50;) { 492 nestegg_packet *pkt; 493 unsigned int track; 494 495 if (nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0) 496 break; 497 498 nestegg_packet_track(pkt, &track); 499 if (track == input->video_track) { 500 nestegg_packet_tstamp(pkt, &tstamp); 501 i++; 502 } 503 504 nestegg_free_packet(pkt); 505 } 506 507 if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) 508 goto fail; 509 510 *fps_num = (i - 1) * 1000000; 511 *fps_den = (unsigned int)(tstamp / 1000); 512 return 0; 513 fail: 514 nestegg_destroy(input->nestegg_ctx); 515 input->nestegg_ctx = NULL; 516 rewind(input->infile); 517 return 1; 518 } 519 520 521 static int 522 file_is_webm(struct input_ctx *input, 523 unsigned int *fourcc, 524 unsigned int *width, 525 unsigned int *height, 526 unsigned int *fps_den, 527 unsigned int *fps_num) { 528 unsigned int i, n; 529 int track_type = -1; 530 int codec_id; 531 532 nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; 533 nestegg_video_params params; 534 535 io.userdata = input->infile; 536 if (nestegg_init(&input->nestegg_ctx, io, NULL)) 537 goto fail; 538 539 if (nestegg_track_count(input->nestegg_ctx, &n)) 540 goto fail; 541 542 for (i = 0; i < n; i++) { 543 track_type = nestegg_track_type(input->nestegg_ctx, i); 544 545 if (track_type == NESTEGG_TRACK_VIDEO) 546 break; 547 else if (track_type < 0) 548 goto fail; 549 } 550 551 codec_id = nestegg_track_codec_id(input->nestegg_ctx, i); 552 if (codec_id == NESTEGG_CODEC_VP8) { 553 *fourcc = VP8_FOURCC_MASK; 554 } else if (codec_id == NESTEGG_CODEC_VP9) { 555 *fourcc = VP9_FOURCC_MASK; 556 } else { 557 fprintf(stderr, "Not VPx video, quitting.\n"); 558 exit(1); 559 } 560 561 input->video_track = i; 562 563 if (nestegg_track_video_params(input->nestegg_ctx, i, ¶ms)) 564 goto fail; 565 566 *fps_den = 0; 567 *fps_num = 0; 568 *width = params.width; 569 *height = params.height; 570 return 1; 571 fail: 572 input->nestegg_ctx = NULL; 573 rewind(input->infile); 574 return 0; 575 } 576 577 578 void show_progress(int frame_in, int frame_out, unsigned long dx_time) { 579 fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\r", 580 frame_in, frame_out, dx_time, 581 (float)frame_out * 1000000.0 / (float)dx_time); 582 } 583 584 585 void generate_filename(const char *pattern, char *out, size_t q_len, 586 unsigned int d_w, unsigned int d_h, 587 unsigned int frame_in) { 588 const char *p = pattern; 589 char *q = out; 590 591 do { 592 char *next_pat = strchr(p, '%'); 593 594 if (p == next_pat) { 595 size_t pat_len; 596 597 /* parse the pattern */ 598 q[q_len - 1] = '\0'; 599 switch (p[1]) { 600 case 'w': 601 snprintf(q, q_len - 1, "%d", d_w); 602 break; 603 case 'h': 604 snprintf(q, q_len - 1, "%d", d_h); 605 break; 606 case '1': 607 snprintf(q, q_len - 1, "%d", frame_in); 608 break; 609 case '2': 610 snprintf(q, q_len - 1, "%02d", frame_in); 611 break; 612 case '3': 613 snprintf(q, q_len - 1, "%03d", frame_in); 614 break; 615 case '4': 616 snprintf(q, q_len - 1, "%04d", frame_in); 617 break; 618 case '5': 619 snprintf(q, q_len - 1, "%05d", frame_in); 620 break; 621 case '6': 622 snprintf(q, q_len - 1, "%06d", frame_in); 623 break; 624 case '7': 625 snprintf(q, q_len - 1, "%07d", frame_in); 626 break; 627 case '8': 628 snprintf(q, q_len - 1, "%08d", frame_in); 629 break; 630 case '9': 631 snprintf(q, q_len - 1, "%09d", frame_in); 632 break; 633 default: 634 die("Unrecognized pattern %%%c\n", p[1]); 635 } 636 637 pat_len = strlen(q); 638 if (pat_len >= q_len - 1) 639 die("Output filename too long.\n"); 640 q += pat_len; 641 p += 2; 642 q_len -= pat_len; 643 } else { 644 size_t copy_len; 645 646 /* copy the next segment */ 647 if (!next_pat) 648 copy_len = strlen(p); 649 else 650 copy_len = next_pat - p; 651 652 if (copy_len >= q_len - 1) 653 die("Output filename too long.\n"); 654 655 memcpy(q, p, copy_len); 656 q[copy_len] = '\0'; 657 q += copy_len; 658 p += copy_len; 659 q_len -= copy_len; 660 } 661 } while (*p); 662 } 663 664 665 int main_loop(int argc, const char **argv_) { 666 vpx_codec_ctx_t decoder; 667 char *fn = NULL; 668 int i; 669 uint8_t *buf = NULL; 670 size_t buf_sz = 0, buf_alloc_sz = 0; 671 FILE *infile; 672 int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0; 673 int stop_after = 0, postproc = 0, summary = 0, quiet = 1; 674 int arg_skip = 0; 675 int ec_enabled = 0; 676 vpx_codec_iface_t *iface = NULL; 677 unsigned int fourcc; 678 unsigned long dx_time = 0; 679 struct arg arg; 680 char **argv, **argi, **argj; 681 const char *outfile_pattern = 0; 682 char outfile[PATH_MAX]; 683 int single_file; 684 int use_y4m = 1; 685 unsigned int width; 686 unsigned int height; 687 unsigned int fps_den; 688 unsigned int fps_num; 689 void *out = NULL; 690 vpx_codec_dec_cfg_t cfg = {0}; 691 #if CONFIG_VP8_DECODER 692 vp8_postproc_cfg_t vp8_pp_cfg = {0}; 693 int vp8_dbg_color_ref_frame = 0; 694 int vp8_dbg_color_mb_modes = 0; 695 int vp8_dbg_color_b_modes = 0; 696 int vp8_dbg_display_mv = 0; 697 #endif 698 struct input_ctx input = {0}; 699 int frames_corrupted = 0; 700 int dec_flags = 0; 701 int do_scale = 0; 702 int stream_w = 0, stream_h = 0; 703 vpx_image_t *scaled_img = NULL; 704 int frame_avail, got_data; 705 706 /* Parse command line */ 707 exec_name = argv_[0]; 708 argv = argv_dup(argc - 1, argv_ + 1); 709 710 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { 711 memset(&arg, 0, sizeof(arg)); 712 arg.argv_step = 1; 713 714 if (arg_match(&arg, &codecarg, argi)) { 715 int j, k = -1; 716 717 for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++) 718 if (!strcmp(ifaces[j].name, arg.val)) 719 k = j; 720 721 if (k >= 0) 722 iface = ifaces[k].iface(); 723 else 724 die("Error: Unrecognized argument (%s) to --codec\n", 725 arg.val); 726 } else if (arg_match(&arg, &looparg, argi)) { 727 // no-op 728 } else if (arg_match(&arg, &outputfile, argi)) 729 outfile_pattern = arg.val; 730 else if (arg_match(&arg, &use_yv12, argi)) { 731 use_y4m = 0; 732 flipuv = 1; 733 } else if (arg_match(&arg, &use_i420, argi)) { 734 use_y4m = 0; 735 flipuv = 0; 736 } else if (arg_match(&arg, &flipuvarg, argi)) 737 flipuv = 1; 738 else if (arg_match(&arg, &noblitarg, argi)) 739 noblit = 1; 740 else if (arg_match(&arg, &progressarg, argi)) 741 progress = 1; 742 else if (arg_match(&arg, &limitarg, argi)) 743 stop_after = arg_parse_uint(&arg); 744 else if (arg_match(&arg, &skiparg, argi)) 745 arg_skip = arg_parse_uint(&arg); 746 else if (arg_match(&arg, &postprocarg, argi)) 747 postproc = 1; 748 else if (arg_match(&arg, &md5arg, argi)) 749 do_md5 = 1; 750 else if (arg_match(&arg, &summaryarg, argi)) 751 summary = 1; 752 else if (arg_match(&arg, &threadsarg, argi)) 753 cfg.threads = arg_parse_uint(&arg); 754 else if (arg_match(&arg, &verbosearg, argi)) 755 quiet = 0; 756 else if (arg_match(&arg, &scalearg, argi)) 757 do_scale = 1; 758 759 #if CONFIG_VP8_DECODER 760 else if (arg_match(&arg, &addnoise_level, argi)) { 761 postproc = 1; 762 vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE; 763 vp8_pp_cfg.noise_level = arg_parse_uint(&arg); 764 } else if (arg_match(&arg, &demacroblock_level, argi)) { 765 postproc = 1; 766 vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK; 767 vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg); 768 } else if (arg_match(&arg, &deblock, argi)) { 769 postproc = 1; 770 vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK; 771 } else if (arg_match(&arg, &mfqe, argi)) { 772 postproc = 1; 773 vp8_pp_cfg.post_proc_flag |= VP8_MFQE; 774 } else if (arg_match(&arg, &pp_debug_info, argi)) { 775 unsigned int level = arg_parse_uint(&arg); 776 777 postproc = 1; 778 vp8_pp_cfg.post_proc_flag &= ~0x7; 779 780 if (level) 781 vp8_pp_cfg.post_proc_flag |= level; 782 } else if (arg_match(&arg, &pp_disp_ref_frame, argi)) { 783 unsigned int flags = arg_parse_int(&arg); 784 if (flags) { 785 postproc = 1; 786 vp8_dbg_color_ref_frame = flags; 787 } 788 } else if (arg_match(&arg, &pp_disp_mb_modes, argi)) { 789 unsigned int flags = arg_parse_int(&arg); 790 if (flags) { 791 postproc = 1; 792 vp8_dbg_color_mb_modes = flags; 793 } 794 } else if (arg_match(&arg, &pp_disp_b_modes, argi)) { 795 unsigned int flags = arg_parse_int(&arg); 796 if (flags) { 797 postproc = 1; 798 vp8_dbg_color_b_modes = flags; 799 } 800 } else if (arg_match(&arg, &pp_disp_mvs, argi)) { 801 unsigned int flags = arg_parse_int(&arg); 802 if (flags) { 803 postproc = 1; 804 vp8_dbg_display_mv = flags; 805 } 806 } else if (arg_match(&arg, &error_concealment, argi)) { 807 ec_enabled = 1; 808 } 809 810 #endif 811 else 812 argj++; 813 } 814 815 /* Check for unrecognized options */ 816 for (argi = argv; *argi; argi++) 817 if (argi[0][0] == '-' && strlen(argi[0]) > 1) 818 die("Error: Unrecognized option %s\n", *argi); 819 820 /* Handle non-option arguments */ 821 fn = argv[0]; 822 823 if (!fn) 824 usage_exit(); 825 826 /* Open file */ 827 infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin); 828 829 if (!infile) { 830 fprintf(stderr, "Failed to open file '%s'", 831 strcmp(fn, "-") ? fn : "stdin"); 832 return EXIT_FAILURE; 833 } 834 #if CONFIG_OS_SUPPORT 835 /* Make sure we don't dump to the terminal, unless forced to with -o - */ 836 if (!outfile_pattern && isatty(fileno(stdout)) && !do_md5 && !noblit) { 837 fprintf(stderr, 838 "Not dumping raw video to your terminal. Use '-o -' to " 839 "override.\n"); 840 return EXIT_FAILURE; 841 } 842 #endif 843 input.infile = infile; 844 if (file_is_ivf(infile, &fourcc, &width, &height, &fps_den, 845 &fps_num)) 846 input.kind = IVF_FILE; 847 else if (file_is_webm(&input, &fourcc, &width, &height, &fps_den, &fps_num)) 848 input.kind = WEBM_FILE; 849 else if (file_is_raw(infile, &fourcc, &width, &height, &fps_den, &fps_num)) 850 input.kind = RAW_FILE; 851 else { 852 fprintf(stderr, "Unrecognized input file type.\n"); 853 return EXIT_FAILURE; 854 } 855 856 /* If the output file is not set or doesn't have a sequence number in 857 * it, then we only open it once. 858 */ 859 outfile_pattern = outfile_pattern ? outfile_pattern : "-"; 860 single_file = 1; 861 { 862 const char *p = outfile_pattern; 863 do { 864 p = strchr(p, '%'); 865 if (p && p[1] >= '1' && p[1] <= '9') { 866 /* pattern contains sequence number, so it's not unique. */ 867 single_file = 0; 868 break; 869 } 870 if (p) 871 p++; 872 } while (p); 873 } 874 875 if (single_file && !noblit) { 876 generate_filename(outfile_pattern, outfile, sizeof(outfile) - 1, 877 width, height, 0); 878 out = out_open(outfile, do_md5); 879 } 880 881 if (use_y4m && !noblit) { 882 char buffer[128]; 883 884 if (!single_file) { 885 fprintf(stderr, "YUV4MPEG2 not supported with output patterns," 886 " try --i420 or --yv12.\n"); 887 return EXIT_FAILURE; 888 } 889 890 if (input.kind == WEBM_FILE) 891 if (webm_guess_framerate(&input, &fps_den, &fps_num)) { 892 fprintf(stderr, "Failed to guess framerate -- error parsing " 893 "webm file?\n"); 894 return EXIT_FAILURE; 895 } 896 897 898 /*Note: We can't output an aspect ratio here because IVF doesn't 899 store one, and neither does VP8. 900 That will have to wait until these tools support WebM natively.*/ 901 snprintf(buffer, sizeof(buffer), "YUV4MPEG2 W%u H%u F%u:%u I%c ", 902 width, height, fps_num, fps_den, 'p'); 903 out_put(out, (unsigned char *)buffer, 904 (unsigned int)strlen(buffer), do_md5); 905 } 906 907 /* Try to determine the codec from the fourcc. */ 908 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 909 if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc) { 910 vpx_codec_iface_t *ivf_iface = ifaces[i].iface(); 911 912 if (iface && iface != ivf_iface) 913 fprintf(stderr, "Notice -- IVF header indicates codec: %s\n", 914 ifaces[i].name); 915 else 916 iface = ivf_iface; 917 918 break; 919 } 920 921 dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) | 922 (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0); 923 if (vpx_codec_dec_init(&decoder, iface ? iface : ifaces[0].iface(), &cfg, 924 dec_flags)) { 925 fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder)); 926 return EXIT_FAILURE; 927 } 928 929 if (!quiet) 930 fprintf(stderr, "%s\n", decoder.name); 931 932 #if CONFIG_VP8_DECODER 933 934 if (vp8_pp_cfg.post_proc_flag 935 && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) { 936 fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder)); 937 return EXIT_FAILURE; 938 } 939 940 if (vp8_dbg_color_ref_frame 941 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_REF_FRAME, vp8_dbg_color_ref_frame)) { 942 fprintf(stderr, "Failed to configure reference block visualizer: %s\n", vpx_codec_error(&decoder)); 943 return EXIT_FAILURE; 944 } 945 946 if (vp8_dbg_color_mb_modes 947 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_MB_MODES, vp8_dbg_color_mb_modes)) { 948 fprintf(stderr, "Failed to configure macro block visualizer: %s\n", vpx_codec_error(&decoder)); 949 return EXIT_FAILURE; 950 } 951 952 if (vp8_dbg_color_b_modes 953 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_B_MODES, vp8_dbg_color_b_modes)) { 954 fprintf(stderr, "Failed to configure block visualizer: %s\n", vpx_codec_error(&decoder)); 955 return EXIT_FAILURE; 956 } 957 958 if (vp8_dbg_display_mv 959 && vpx_codec_control(&decoder, VP8_SET_DBG_DISPLAY_MV, vp8_dbg_display_mv)) { 960 fprintf(stderr, "Failed to configure motion vector visualizer: %s\n", vpx_codec_error(&decoder)); 961 return EXIT_FAILURE; 962 } 963 #endif 964 965 966 if(arg_skip) 967 fprintf(stderr, "Skiping first %d frames.\n", arg_skip); 968 while (arg_skip) { 969 if (read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) 970 break; 971 arg_skip--; 972 } 973 974 frame_avail = 1; 975 got_data = 0; 976 977 /* Decode file */ 978 while (frame_avail || got_data) { 979 vpx_codec_iter_t iter = NULL; 980 vpx_image_t *img; 981 struct vpx_usec_timer timer; 982 int corrupted; 983 984 frame_avail = 0; 985 if (!stop_after || frame_in < stop_after) { 986 if(!read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) { 987 frame_avail = 1; 988 frame_in++; 989 990 vpx_usec_timer_start(&timer); 991 992 if (vpx_codec_decode(&decoder, buf, (unsigned int)buf_sz, NULL, 0)) { 993 const char *detail = vpx_codec_error_detail(&decoder); 994 fprintf(stderr, "Failed to decode frame: %s\n", 995 vpx_codec_error(&decoder)); 996 997 if (detail) 998 fprintf(stderr, " Additional information: %s\n", detail); 999 goto fail; 1000 } 1001 1002 vpx_usec_timer_mark(&timer); 1003 dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); 1004 } 1005 } 1006 1007 vpx_usec_timer_start(&timer); 1008 1009 got_data = 0; 1010 if ((img = vpx_codec_get_frame(&decoder, &iter))) { 1011 ++frame_out; 1012 got_data = 1; 1013 } 1014 1015 vpx_usec_timer_mark(&timer); 1016 dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); 1017 1018 if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) { 1019 fprintf(stderr, "Failed VP8_GET_FRAME_CORRUPTED: %s\n", 1020 vpx_codec_error(&decoder)); 1021 goto fail; 1022 } 1023 frames_corrupted += corrupted; 1024 1025 if (progress) 1026 show_progress(frame_in, frame_out, dx_time); 1027 1028 if (!noblit) { 1029 if (frame_out == 1 && img && use_y4m) { 1030 /* Write out the color format to terminate the header line */ 1031 const char *color = 1032 img->fmt == VPX_IMG_FMT_444A ? "C444alpha\n" : 1033 img->fmt == VPX_IMG_FMT_I444 ? "C444\n" : 1034 img->fmt == VPX_IMG_FMT_I422 ? "C422\n" : 1035 "C420jpeg\n"; 1036 1037 out_put(out, (const unsigned char*)color, strlen(color), do_md5); 1038 } 1039 1040 if (do_scale) { 1041 if (img && frame_out == 1) { 1042 stream_w = img->d_w; 1043 stream_h = img->d_h; 1044 scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 1045 stream_w, stream_h, 16); 1046 } 1047 if (img && (img->d_w != stream_w || img->d_h != stream_h)) { 1048 assert(img->fmt == VPX_IMG_FMT_I420); 1049 I420Scale(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], 1050 img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U], 1051 img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V], 1052 img->d_w, img->d_h, 1053 scaled_img->planes[VPX_PLANE_Y], 1054 scaled_img->stride[VPX_PLANE_Y], 1055 scaled_img->planes[VPX_PLANE_U], 1056 scaled_img->stride[VPX_PLANE_U], 1057 scaled_img->planes[VPX_PLANE_V], 1058 scaled_img->stride[VPX_PLANE_V], 1059 stream_w, stream_h, 1060 kFilterBox); 1061 img = scaled_img; 1062 } 1063 } 1064 1065 if (img) { 1066 unsigned int y; 1067 char out_fn[PATH_MAX]; 1068 uint8_t *buf; 1069 unsigned int c_w = 1070 img->x_chroma_shift ? (1 + img->d_w) >> img->x_chroma_shift 1071 : img->d_w; 1072 unsigned int c_h = 1073 img->y_chroma_shift ? (1 + img->d_h) >> img->y_chroma_shift 1074 : img->d_h; 1075 1076 if (!single_file) { 1077 size_t len = sizeof(out_fn) - 1; 1078 1079 out_fn[len] = '\0'; 1080 generate_filename(outfile_pattern, out_fn, len - 1, 1081 img->d_w, img->d_h, frame_in); 1082 out = out_open(out_fn, do_md5); 1083 } else if (use_y4m) 1084 out_put(out, (unsigned char *)"FRAME\n", 6, do_md5); 1085 1086 buf = img->planes[VPX_PLANE_Y]; 1087 1088 for (y = 0; y < img->d_h; y++) { 1089 out_put(out, buf, img->d_w, do_md5); 1090 buf += img->stride[VPX_PLANE_Y]; 1091 } 1092 1093 buf = img->planes[flipuv ? VPX_PLANE_V : VPX_PLANE_U]; 1094 1095 for (y = 0; y < c_h; y++) { 1096 out_put(out, buf, c_w, do_md5); 1097 buf += img->stride[VPX_PLANE_U]; 1098 } 1099 1100 buf = img->planes[flipuv ? VPX_PLANE_U : VPX_PLANE_V]; 1101 1102 for (y = 0; y < c_h; y++) { 1103 out_put(out, buf, c_w, do_md5); 1104 buf += img->stride[VPX_PLANE_V]; 1105 } 1106 1107 if (!single_file) 1108 out_close(out, out_fn, do_md5); 1109 } 1110 } 1111 1112 if (stop_after && frame_in >= stop_after) 1113 break; 1114 } 1115 1116 if (summary || progress) { 1117 show_progress(frame_in, frame_out, dx_time); 1118 fprintf(stderr, "\n"); 1119 } 1120 1121 if (frames_corrupted) 1122 fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted); 1123 1124 fail: 1125 1126 if (vpx_codec_destroy(&decoder)) { 1127 fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder)); 1128 return EXIT_FAILURE; 1129 } 1130 1131 if (single_file && !noblit) 1132 out_close(out, outfile, do_md5); 1133 1134 if (input.nestegg_ctx) 1135 nestegg_destroy(input.nestegg_ctx); 1136 if (input.kind != WEBM_FILE) 1137 free(buf); 1138 fclose(infile); 1139 free(argv); 1140 1141 return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS; 1142 } 1143 1144 int main(int argc, const char **argv_) { 1145 unsigned int loops = 1, i; 1146 char **argv, **argi, **argj; 1147 struct arg arg; 1148 int error = 0; 1149 1150 argv = argv_dup(argc - 1, argv_ + 1); 1151 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { 1152 memset(&arg, 0, sizeof(arg)); 1153 arg.argv_step = 1; 1154 1155 if (arg_match(&arg, &looparg, argi)) { 1156 loops = arg_parse_uint(&arg); 1157 break; 1158 } 1159 } 1160 free(argv); 1161 for (i = 0; !error && i < loops; i++) 1162 error = main_loop(argc, argv_); 1163 return error; 1164 } 1165