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 <stdio.h> 16 #include <stdlib.h> 17 #include <stdarg.h> 18 #include <string.h> 19 #define VPX_CODEC_DISABLE_COMPAT 1 20 #include "vpx_config.h" 21 #include "vpx/vpx_decoder.h" 22 #include "vpx_ports/vpx_timer.h" 23 #if CONFIG_VP8_DECODER 24 #include "vpx/vp8dx.h" 25 #endif 26 #if CONFIG_MD5 27 #include "md5_utils.h" 28 #endif 29 30 static const char *exec_name; 31 32 static const struct 33 { 34 char const *name; 35 const vpx_codec_iface_t *iface; 36 unsigned int fourcc; 37 unsigned int fourcc_mask; 38 } ifaces[] = 39 { 40 #if CONFIG_VP8_DECODER 41 {"vp8", &vpx_codec_vp8_dx_algo, 0x00385056, 0x00FFFFFF}, 42 #endif 43 }; 44 45 #include "args.h" 46 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, 47 "Codec to use"); 48 static const arg_def_t prefixarg = ARG_DEF("p", "prefix", 1, 49 "Prefix to use when saving frames"); 50 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, 51 "Output file is YV12 "); 52 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, 53 "Output file is I420 (default)"); 54 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0, 55 "Synonym for --yv12"); 56 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0, 57 "Don't process the decoded frames"); 58 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0, 59 "Show progress after each frame decodes"); 60 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1, 61 "Stop decoding after n frames"); 62 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0, 63 "Postprocess decoded frames"); 64 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0, 65 "Show timing summary"); 66 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, 67 "Output raw yv12 file instead of images"); 68 static const arg_def_t usey4marg = ARG_DEF("y", "y4m", 0, 69 "Output file is YUV4MPEG2"); 70 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1, 71 "Max threads to use"); 72 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0, 73 "Suppress version string"); 74 75 #if CONFIG_MD5 76 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0, 77 "Compute the MD5 sum of the decoded frame"); 78 #endif 79 static const arg_def_t *all_args[] = 80 { 81 &codecarg, &prefixarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg, 82 &progressarg, &limitarg, &postprocarg, &summaryarg, &outputfile, 83 &usey4marg, &threadsarg, &quietarg, 84 #if CONFIG_MD5 85 &md5arg, 86 #endif 87 NULL 88 }; 89 90 #if CONFIG_VP8_DECODER 91 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1, 92 "Enable VP8 postproc add noise"); 93 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0, 94 "Enable VP8 deblocking"); 95 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1, 96 "Enable VP8 demacroblocking, w/ level"); 97 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1, 98 "Enable VP8 visible debug info"); 99 100 101 static const arg_def_t *vp8_pp_args[] = 102 { 103 &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info, 104 NULL 105 }; 106 #endif 107 108 static void usage_exit() 109 { 110 int i; 111 112 fprintf(stderr, "Usage: %s <options> filename\n\n" 113 "Options:\n", exec_name); 114 arg_show_usage(stderr, all_args); 115 #if CONFIG_VP8_DECODER 116 fprintf(stderr, "\nvp8 Postprocessing Options:\n"); 117 arg_show_usage(stderr, vp8_pp_args); 118 #endif 119 fprintf(stderr, "\nIncluded decoders:\n\n"); 120 121 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 122 fprintf(stderr, " %-6s - %s\n", 123 ifaces[i].name, 124 vpx_codec_iface_name(ifaces[i].iface)); 125 126 exit(EXIT_FAILURE); 127 } 128 129 void die(const char *fmt, ...) 130 { 131 va_list ap; 132 va_start(ap, fmt); 133 vfprintf(stderr, fmt, ap); 134 fprintf(stderr, "\n"); 135 usage_exit(); 136 } 137 138 static unsigned int mem_get_le16(const void *vmem) 139 { 140 unsigned int val; 141 const unsigned char *mem = (const unsigned char *)vmem; 142 143 val = mem[1] << 8; 144 val |= mem[0]; 145 return val; 146 } 147 148 static unsigned int mem_get_le32(const void *vmem) 149 { 150 unsigned int val; 151 const unsigned char *mem = (const unsigned char *)vmem; 152 153 val = mem[3] << 24; 154 val |= mem[2] << 16; 155 val |= mem[1] << 8; 156 val |= mem[0]; 157 return val; 158 } 159 160 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t)) 161 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t)) 162 static int read_frame(FILE *infile, 163 uint8_t **buf, 164 uint32_t *buf_sz, 165 uint32_t *buf_alloc_sz, 166 int is_ivf) 167 { 168 char raw_hdr[IVF_FRAME_HDR_SZ]; 169 uint32_t new_buf_sz; 170 171 /* For both the raw and ivf formats, the frame size is the first 4 bytes 172 * of the frame header. We just need to special case on the header 173 * size. 174 */ 175 if (fread(raw_hdr, is_ivf ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, 176 infile) != 1) 177 { 178 if (!feof(infile)) 179 fprintf(stderr, "Failed to read frame size\n"); 180 181 new_buf_sz = 0; 182 } 183 else 184 { 185 new_buf_sz = mem_get_le32(raw_hdr); 186 187 if (new_buf_sz > 256 * 1024 * 1024) 188 { 189 fprintf(stderr, "Error: Read invalid frame size (%u)\n", 190 new_buf_sz); 191 new_buf_sz = 0; 192 } 193 194 if (!is_ivf && new_buf_sz > 256 * 1024) 195 fprintf(stderr, "Warning: Read invalid frame size (%u)" 196 " - not a raw file?\n", new_buf_sz); 197 198 if (new_buf_sz > *buf_alloc_sz) 199 { 200 uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz); 201 202 if (new_buf) 203 { 204 *buf = new_buf; 205 *buf_alloc_sz = 2 * new_buf_sz; 206 } 207 else 208 { 209 fprintf(stderr, "Failed to allocate compressed data buffer\n"); 210 new_buf_sz = 0; 211 } 212 } 213 } 214 215 *buf_sz = new_buf_sz; 216 217 if (*buf_sz) 218 { 219 if (fread(*buf, 1, *buf_sz, infile) != *buf_sz) 220 { 221 fprintf(stderr, "Failed to read full frame\n"); 222 return 1; 223 } 224 225 return 0; 226 } 227 228 return 1; 229 } 230 231 void *out_open(const char *out_fn, int do_md5) 232 { 233 void *out = NULL; 234 235 if (do_md5) 236 { 237 #if CONFIG_MD5 238 MD5Context *md5_ctx = out = malloc(sizeof(MD5Context)); 239 (void)out_fn; 240 MD5Init(md5_ctx); 241 #endif 242 } 243 else 244 { 245 FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") : stdout; 246 247 if (!outfile) 248 { 249 fprintf(stderr, "Failed to output file"); 250 exit(EXIT_FAILURE); 251 } 252 } 253 254 return out; 255 } 256 257 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5) 258 { 259 if (do_md5) 260 { 261 #if CONFIG_MD5 262 MD5Update(out, buf, len); 263 #endif 264 } 265 else 266 { 267 fwrite(buf, 1, len, out); 268 } 269 } 270 271 void out_close(void *out, const char *out_fn, int do_md5) 272 { 273 if (do_md5) 274 { 275 #if CONFIG_MD5 276 uint8_t md5[16]; 277 int i; 278 279 MD5Final(md5, out); 280 free(out); 281 282 for (i = 0; i < 16; i++) 283 printf("%02x", md5[i]); 284 285 printf(" %s\n", out_fn); 286 #endif 287 } 288 else 289 { 290 fclose(out); 291 } 292 } 293 294 unsigned int file_is_ivf(FILE *infile, 295 unsigned int *fourcc, 296 unsigned int *width, 297 unsigned int *height, 298 unsigned int *timebase_num, 299 unsigned int *timebase_den) 300 { 301 char raw_hdr[32]; 302 int is_ivf = 0; 303 304 if (fread(raw_hdr, 1, 32, infile) == 32) 305 { 306 if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K' 307 && raw_hdr[2] == 'I' && raw_hdr[3] == 'F') 308 { 309 is_ivf = 1; 310 311 if (mem_get_le16(raw_hdr + 4) != 0) 312 fprintf(stderr, "Error: Unrecognized IVF version! This file may not" 313 " decode properly."); 314 315 *fourcc = mem_get_le32(raw_hdr + 8); 316 *width = mem_get_le16(raw_hdr + 12); 317 *height = mem_get_le16(raw_hdr + 14); 318 *timebase_den = mem_get_le32(raw_hdr + 16); 319 *timebase_num = mem_get_le32(raw_hdr + 20); 320 } 321 } 322 323 if (!is_ivf) 324 rewind(infile); 325 326 return is_ivf; 327 } 328 329 int main(int argc, const char **argv_) 330 { 331 vpx_codec_ctx_t decoder; 332 char *prefix = NULL, *fn = NULL; 333 int i; 334 uint8_t *buf = NULL; 335 uint32_t buf_sz = 0, buf_alloc_sz = 0; 336 FILE *infile; 337 int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0; 338 int stop_after = 0, postproc = 0, summary = 0, quiet = 0; 339 vpx_codec_iface_t *iface = NULL; 340 unsigned int is_ivf, fourcc; 341 unsigned long dx_time = 0; 342 struct arg arg; 343 char **argv, **argi, **argj; 344 const char *fn2 = 0; 345 int use_y4m = 0; 346 unsigned int width; 347 unsigned int height; 348 unsigned int timebase_num; 349 unsigned int timebase_den; 350 void *out = NULL; 351 vpx_codec_dec_cfg_t cfg = {0}; 352 #if CONFIG_VP8_DECODER 353 vp8_postproc_cfg_t vp8_pp_cfg = {0}; 354 #endif 355 356 /* Parse command line */ 357 exec_name = argv_[0]; 358 argv = argv_dup(argc - 1, argv_ + 1); 359 360 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) 361 { 362 memset(&arg, 0, sizeof(arg)); 363 arg.argv_step = 1; 364 365 if (arg_match(&arg, &codecarg, argi)) 366 { 367 int j, k = -1; 368 369 for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++) 370 if (!strcmp(ifaces[j].name, arg.val)) 371 k = j; 372 373 if (k >= 0) 374 iface = ifaces[k].iface; 375 else 376 die("Error: Unrecognized argument (%s) to --codec\n", 377 arg.val); 378 } 379 else if (arg_match(&arg, &outputfile, argi)) 380 fn2 = arg.val; 381 else if (arg_match(&arg, &usey4marg, argi)) 382 use_y4m = 1; 383 else if (arg_match(&arg, &prefixarg, argi)) 384 prefix = strdup(arg.val); 385 else if (arg_match(&arg, &use_yv12, argi)) 386 flipuv = 1; 387 else if (arg_match(&arg, &use_i420, argi)) 388 flipuv = 0; 389 else if (arg_match(&arg, &flipuvarg, argi)) 390 flipuv = 1; 391 else if (arg_match(&arg, &noblitarg, argi)) 392 noblit = 1; 393 else if (arg_match(&arg, &progressarg, argi)) 394 progress = 1; 395 else if (arg_match(&arg, &limitarg, argi)) 396 stop_after = arg_parse_uint(&arg); 397 else if (arg_match(&arg, &postprocarg, argi)) 398 postproc = 1; 399 else if (arg_match(&arg, &md5arg, argi)) 400 do_md5 = 1; 401 else if (arg_match(&arg, &summaryarg, argi)) 402 summary = 1; 403 else if (arg_match(&arg, &threadsarg, argi)) 404 cfg.threads = arg_parse_uint(&arg); 405 else if (arg_match(&arg, &quietarg, argi)) 406 quiet = 1; 407 408 #if CONFIG_VP8_DECODER 409 else if (arg_match(&arg, &addnoise_level, argi)) 410 { 411 postproc = 1; 412 vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE; 413 vp8_pp_cfg.noise_level = arg_parse_uint(&arg); 414 } 415 else if (arg_match(&arg, &demacroblock_level, argi)) 416 { 417 postproc = 1; 418 vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK; 419 vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg); 420 } 421 else if (arg_match(&arg, &deblock, argi)) 422 { 423 postproc = 1; 424 vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK; 425 } 426 else if (arg_match(&arg, &pp_debug_info, argi)) 427 { 428 unsigned int level = arg_parse_uint(&arg); 429 430 postproc = 1; 431 vp8_pp_cfg.post_proc_flag &= ~0x7; 432 433 if (level) 434 vp8_pp_cfg.post_proc_flag |= 8 << (level - 1); 435 } 436 437 #endif 438 else 439 argj++; 440 } 441 442 /* Check for unrecognized options */ 443 for (argi = argv; *argi; argi++) 444 if (argi[0][0] == '-' && strlen(argi[0]) > 1) 445 die("Error: Unrecognized option %s\n", *argi); 446 447 /* Handle non-option arguments */ 448 fn = argv[0]; 449 450 if (!fn) 451 usage_exit(); 452 453 if (!prefix) 454 prefix = strdup("img"); 455 456 /* Open file */ 457 infile = strcmp(fn, "-") ? fopen(fn, "rb") : stdin; 458 459 if (!infile) 460 { 461 fprintf(stderr, "Failed to open file"); 462 return EXIT_FAILURE; 463 } 464 465 if (fn2) 466 out = out_open(fn2, do_md5); 467 468 is_ivf = file_is_ivf(infile, &fourcc, &width, &height, 469 &timebase_num, &timebase_den); 470 471 if (is_ivf) 472 { 473 if (use_y4m) 474 { 475 char buffer[128]; 476 if (!fn2) 477 { 478 fprintf(stderr, "YUV4MPEG2 output only supported with -o.\n"); 479 return EXIT_FAILURE; 480 } 481 /*Correct for the factor of 2 applied to the timebase in the 482 encoder.*/ 483 if(timebase_den&1)timebase_num<<=1; 484 else timebase_den>>=1; 485 /*Note: We can't output an aspect ratio here because IVF doesn't 486 store one, and neither does VP8. 487 That will have to wait until these tools support WebM natively.*/ 488 sprintf(buffer, "YUV4MPEG2 C%s W%u H%u F%u:%u I%c\n", 489 "420jpeg", width, height, timebase_den, timebase_num, 'p'); 490 out_put(out, (unsigned char *)buffer, strlen(buffer), do_md5); 491 } 492 493 /* Try to determine the codec from the fourcc. */ 494 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 495 if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc) 496 { 497 vpx_codec_iface_t *ivf_iface = ifaces[i].iface; 498 499 if (iface && iface != ivf_iface) 500 fprintf(stderr, "Notice -- IVF header indicates codec: %s\n", 501 ifaces[i].name); 502 else 503 iface = ivf_iface; 504 505 break; 506 } 507 } 508 else if(use_y4m) 509 { 510 fprintf(stderr, "YUV4MPEG2 output only supported from IVF input.\n"); 511 return EXIT_FAILURE; 512 } 513 514 if (vpx_codec_dec_init(&decoder, iface ? iface : ifaces[0].iface, &cfg, 515 postproc ? VPX_CODEC_USE_POSTPROC : 0)) 516 { 517 fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder)); 518 return EXIT_FAILURE; 519 } 520 521 if (!quiet) 522 fprintf(stderr, "%s\n", decoder.name); 523 524 #if CONFIG_VP8_DECODER 525 526 if (vp8_pp_cfg.post_proc_flag 527 && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) 528 { 529 fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder)); 530 return EXIT_FAILURE; 531 } 532 533 #endif 534 535 /* Decode file */ 536 while (!read_frame(infile, &buf, &buf_sz, &buf_alloc_sz, is_ivf)) 537 { 538 vpx_codec_iter_t iter = NULL; 539 vpx_image_t *img; 540 struct vpx_usec_timer timer; 541 542 vpx_usec_timer_start(&timer); 543 544 if (vpx_codec_decode(&decoder, buf, buf_sz, NULL, 0)) 545 { 546 const char *detail = vpx_codec_error_detail(&decoder); 547 fprintf(stderr, "Failed to decode frame: %s\n", vpx_codec_error(&decoder)); 548 549 if (detail) 550 fprintf(stderr, " Additional information: %s\n", detail); 551 552 goto fail; 553 } 554 555 vpx_usec_timer_mark(&timer); 556 dx_time += vpx_usec_timer_elapsed(&timer); 557 558 ++frame_in; 559 560 if (progress) 561 fprintf(stderr, "decoded frame %d.\n", frame_in); 562 563 if ((img = vpx_codec_get_frame(&decoder, &iter))) 564 ++frame_out; 565 566 if (!noblit) 567 { 568 if (img) 569 { 570 unsigned int y; 571 char out_fn[128+24]; 572 uint8_t *buf; 573 const char *sfx = flipuv ? "yv12" : "i420"; 574 575 if (!fn2) 576 { 577 sprintf(out_fn, "%s-%dx%d-%04d.%s", 578 prefix, img->d_w, img->d_h, frame_in, sfx); 579 out = out_open(out_fn, do_md5); 580 } 581 else if(use_y4m) 582 out_put(out, (unsigned char *)"FRAME\n", 6, do_md5); 583 584 buf = img->planes[VPX_PLANE_Y]; 585 586 for (y = 0; y < img->d_h; y++) 587 { 588 out_put(out, buf, img->d_w, do_md5); 589 buf += img->stride[VPX_PLANE_Y]; 590 } 591 592 buf = img->planes[flipuv?VPX_PLANE_V:VPX_PLANE_U]; 593 594 for (y = 0; y < (1 + img->d_h) / 2; y++) 595 { 596 out_put(out, buf, (1 + img->d_w) / 2, do_md5); 597 buf += img->stride[VPX_PLANE_U]; 598 } 599 600 buf = img->planes[flipuv?VPX_PLANE_U:VPX_PLANE_V]; 601 602 for (y = 0; y < (1 + img->d_h) / 2; y++) 603 { 604 out_put(out, buf, (1 + img->d_w) / 2, do_md5); 605 buf += img->stride[VPX_PLANE_V]; 606 } 607 608 if (!fn2) 609 out_close(out, out_fn, do_md5); 610 } 611 } 612 613 if (stop_after && frame_in >= stop_after) 614 break; 615 } 616 617 if (summary) 618 { 619 fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\n", 620 frame_in, frame_out, dx_time, (float)frame_out * 1000000.0 / (float)dx_time); 621 } 622 623 fail: 624 625 if (vpx_codec_destroy(&decoder)) 626 { 627 fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder)); 628 return EXIT_FAILURE; 629 } 630 631 if (fn2) 632 out_close(out, fn2, do_md5); 633 634 free(buf); 635 fclose(infile); 636 free(prefix); 637 free(argv); 638 639 return EXIT_SUCCESS; 640 } 641