00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <stdarg.h>
00018 #include <string.h>
00019 #define VPX_CODEC_DISABLE_COMPAT 1
00020 #include "vpx_config.h"
00021 #include "vpx/vpx_decoder.h"
00022 #include "vpx_ports/vpx_timer.h"
00023 #if CONFIG_VP8_DECODER
00024 #include "vpx/vp8dx.h"
00025 #endif
00026 #if CONFIG_MD5
00027 #include "md5_utils.h"
00028 #endif
00029
00030 static const char *exec_name;
00031
00032 static const struct
00033 {
00034 char const *name;
00035 const vpx_codec_iface_t *iface;
00036 unsigned int fourcc;
00037 unsigned int fourcc_mask;
00038 } ifaces[] =
00039 {
00040 #if CONFIG_VP8_DECODER
00041 {"vp8", &vpx_codec_vp8_dx_algo, 0x00385056, 0x00FFFFFF},
00042 #endif
00043 };
00044
00045 #include "args.h"
00046 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
00047 "Codec to use");
00048 static const arg_def_t prefixarg = ARG_DEF("p", "prefix", 1,
00049 "Prefix to use when saving frames");
00050 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
00051 "Output file is YV12 ");
00052 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
00053 "Output file is I420 (default)");
00054 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
00055 "Synonym for --yv12");
00056 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
00057 "Don't process the decoded frames");
00058 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
00059 "Show progress after each frame decodes");
00060 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
00061 "Stop decoding after n frames");
00062 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
00063 "Postprocess decoded frames");
00064 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
00065 "Show timing summary");
00066 static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
00067 "Output raw yv12 file instead of images");
00068 static const arg_def_t usey4marg = ARG_DEF("y", "y4m", 0,
00069 "Output file is YUV4MPEG2");
00070 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
00071 "Max threads to use");
00072 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0,
00073 "Suppress version string");
00074
00075 #if CONFIG_MD5
00076 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
00077 "Compute the MD5 sum of the decoded frame");
00078 #endif
00079 static const arg_def_t *all_args[] =
00080 {
00081 &codecarg, &prefixarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg,
00082 &progressarg, &limitarg, &postprocarg, &summaryarg, &outputfile,
00083 &usey4marg, &threadsarg, &quietarg,
00084 #if CONFIG_MD5
00085 &md5arg,
00086 #endif
00087 NULL
00088 };
00089
00090 #if CONFIG_VP8_DECODER
00091 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
00092 "Enable VP8 postproc add noise");
00093 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
00094 "Enable VP8 deblocking");
00095 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
00096 "Enable VP8 demacroblocking, w/ level");
00097 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
00098 "Enable VP8 visible debug info");
00099
00100
00101 static const arg_def_t *vp8_pp_args[] =
00102 {
00103 &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
00104 NULL
00105 };
00106 #endif
00107
00108 static void usage_exit()
00109 {
00110 int i;
00111
00112 fprintf(stderr, "Usage: %s <options> filename\n\n"
00113 "Options:\n", exec_name);
00114 arg_show_usage(stderr, all_args);
00115 #if CONFIG_VP8_DECODER
00116 fprintf(stderr, "\nvp8 Postprocessing Options:\n");
00117 arg_show_usage(stderr, vp8_pp_args);
00118 #endif
00119 fprintf(stderr, "\nIncluded decoders:\n\n");
00120
00121 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
00122 fprintf(stderr, " %-6s - %s\n",
00123 ifaces[i].name,
00124 vpx_codec_iface_name(ifaces[i].iface));
00125
00126 exit(EXIT_FAILURE);
00127 }
00128
00129 void die(const char *fmt, ...)
00130 {
00131 va_list ap;
00132 va_start(ap, fmt);
00133 vfprintf(stderr, fmt, ap);
00134 fprintf(stderr, "\n");
00135 usage_exit();
00136 }
00137
00138 static unsigned int mem_get_le16(const void *vmem)
00139 {
00140 unsigned int val;
00141 const unsigned char *mem = (const unsigned char *)vmem;
00142
00143 val = mem[1] << 8;
00144 val |= mem[0];
00145 return val;
00146 }
00147
00148 static unsigned int mem_get_le32(const void *vmem)
00149 {
00150 unsigned int val;
00151 const unsigned char *mem = (const unsigned char *)vmem;
00152
00153 val = mem[3] << 24;
00154 val |= mem[2] << 16;
00155 val |= mem[1] << 8;
00156 val |= mem[0];
00157 return val;
00158 }
00159
00160 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t))
00161 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t))
00162 static int read_frame(FILE *infile,
00163 uint8_t **buf,
00164 uint32_t *buf_sz,
00165 uint32_t *buf_alloc_sz,
00166 int is_ivf)
00167 {
00168 char raw_hdr[IVF_FRAME_HDR_SZ];
00169 uint32_t new_buf_sz;
00170
00171
00172
00173
00174
00175 if (fread(raw_hdr, is_ivf ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1,
00176 infile) != 1)
00177 {
00178 if (!feof(infile))
00179 fprintf(stderr, "Failed to read frame size\n");
00180
00181 new_buf_sz = 0;
00182 }
00183 else
00184 {
00185 new_buf_sz = mem_get_le32(raw_hdr);
00186
00187 if (new_buf_sz > 256 * 1024 * 1024)
00188 {
00189 fprintf(stderr, "Error: Read invalid frame size (%u)\n",
00190 new_buf_sz);
00191 new_buf_sz = 0;
00192 }
00193
00194 if (!is_ivf && new_buf_sz > 256 * 1024)
00195 fprintf(stderr, "Warning: Read invalid frame size (%u)"
00196 " - not a raw file?\n", new_buf_sz);
00197
00198 if (new_buf_sz > *buf_alloc_sz)
00199 {
00200 uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz);
00201
00202 if (new_buf)
00203 {
00204 *buf = new_buf;
00205 *buf_alloc_sz = 2 * new_buf_sz;
00206 }
00207 else
00208 {
00209 fprintf(stderr, "Failed to allocate compressed data buffer\n");
00210 new_buf_sz = 0;
00211 }
00212 }
00213 }
00214
00215 *buf_sz = new_buf_sz;
00216
00217 if (*buf_sz)
00218 {
00219 if (fread(*buf, 1, *buf_sz, infile) != *buf_sz)
00220 {
00221 fprintf(stderr, "Failed to read full frame\n");
00222 return 1;
00223 }
00224
00225 return 0;
00226 }
00227
00228 return 1;
00229 }
00230
00231 void *out_open(const char *out_fn, int do_md5)
00232 {
00233 void *out = NULL;
00234
00235 if (do_md5)
00236 {
00237 #if CONFIG_MD5
00238 MD5Context *md5_ctx = out = malloc(sizeof(MD5Context));
00239 (void)out_fn;
00240 MD5Init(md5_ctx);
00241 #endif
00242 }
00243 else
00244 {
00245 FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") : stdout;
00246
00247 if (!outfile)
00248 {
00249 fprintf(stderr, "Failed to output file");
00250 exit(EXIT_FAILURE);
00251 }
00252 }
00253
00254 return out;
00255 }
00256
00257 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5)
00258 {
00259 if (do_md5)
00260 {
00261 #if CONFIG_MD5
00262 MD5Update(out, buf, len);
00263 #endif
00264 }
00265 else
00266 {
00267 fwrite(buf, 1, len, out);
00268 }
00269 }
00270
00271 void out_close(void *out, const char *out_fn, int do_md5)
00272 {
00273 if (do_md5)
00274 {
00275 #if CONFIG_MD5
00276 uint8_t md5[16];
00277 int i;
00278
00279 MD5Final(md5, out);
00280 free(out);
00281
00282 for (i = 0; i < 16; i++)
00283 printf("%02x", md5[i]);
00284
00285 printf(" %s\n", out_fn);
00286 #endif
00287 }
00288 else
00289 {
00290 fclose(out);
00291 }
00292 }
00293
00294 unsigned int file_is_ivf(FILE *infile,
00295 unsigned int *fourcc,
00296 unsigned int *width,
00297 unsigned int *height,
00298 unsigned int *timebase_num,
00299 unsigned int *timebase_den)
00300 {
00301 char raw_hdr[32];
00302 int is_ivf = 0;
00303
00304 if (fread(raw_hdr, 1, 32, infile) == 32)
00305 {
00306 if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K'
00307 && raw_hdr[2] == 'I' && raw_hdr[3] == 'F')
00308 {
00309 is_ivf = 1;
00310
00311 if (mem_get_le16(raw_hdr + 4) != 0)
00312 fprintf(stderr, "Error: Unrecognized IVF version! This file may not"
00313 " decode properly.");
00314
00315 *fourcc = mem_get_le32(raw_hdr + 8);
00316 *width = mem_get_le16(raw_hdr + 12);
00317 *height = mem_get_le16(raw_hdr + 14);
00318 *timebase_den = mem_get_le32(raw_hdr + 16);
00319 *timebase_num = mem_get_le32(raw_hdr + 20);
00320 }
00321 }
00322
00323 if (!is_ivf)
00324 rewind(infile);
00325
00326 return is_ivf;
00327 }
00328
00329 int main(int argc, const char **argv_)
00330 {
00331 vpx_codec_ctx_t decoder;
00332 char *prefix = NULL, *fn = NULL;
00333 int i;
00334 uint8_t *buf = NULL;
00335 uint32_t buf_sz = 0, buf_alloc_sz = 0;
00336 FILE *infile;
00337 int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0;
00338 int stop_after = 0, postproc = 0, summary = 0, quiet = 0;
00339 vpx_codec_iface_t *iface = NULL;
00340 unsigned int is_ivf, fourcc;
00341 unsigned long dx_time = 0;
00342 struct arg arg;
00343 char **argv, **argi, **argj;
00344 const char *fn2 = 0;
00345 int use_y4m = 0;
00346 unsigned int width;
00347 unsigned int height;
00348 unsigned int timebase_num;
00349 unsigned int timebase_den;
00350 void *out = NULL;
00351 vpx_codec_dec_cfg_t cfg = {0};
00352 #if CONFIG_VP8_DECODER
00353 vp8_postproc_cfg_t vp8_pp_cfg = {0};
00354 #endif
00355
00356
00357 exec_name = argv_[0];
00358 argv = argv_dup(argc - 1, argv_ + 1);
00359
00360 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step)
00361 {
00362 memset(&arg, 0, sizeof(arg));
00363 arg.argv_step = 1;
00364
00365 if (arg_match(&arg, &codecarg, argi))
00366 {
00367 int j, k = -1;
00368
00369 for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++)
00370 if (!strcmp(ifaces[j].name, arg.val))
00371 k = j;
00372
00373 if (k >= 0)
00374 iface = ifaces[k].iface;
00375 else
00376 die("Error: Unrecognized argument (%s) to --codec\n",
00377 arg.val);
00378 }
00379 else if (arg_match(&arg, &outputfile, argi))
00380 fn2 = arg.val;
00381 else if (arg_match(&arg, &usey4marg, argi))
00382 use_y4m = 1;
00383 else if (arg_match(&arg, &prefixarg, argi))
00384 prefix = strdup(arg.val);
00385 else if (arg_match(&arg, &use_yv12, argi))
00386 flipuv = 1;
00387 else if (arg_match(&arg, &use_i420, argi))
00388 flipuv = 0;
00389 else if (arg_match(&arg, &flipuvarg, argi))
00390 flipuv = 1;
00391 else if (arg_match(&arg, &noblitarg, argi))
00392 noblit = 1;
00393 else if (arg_match(&arg, &progressarg, argi))
00394 progress = 1;
00395 else if (arg_match(&arg, &limitarg, argi))
00396 stop_after = arg_parse_uint(&arg);
00397 else if (arg_match(&arg, &postprocarg, argi))
00398 postproc = 1;
00399 else if (arg_match(&arg, &md5arg, argi))
00400 do_md5 = 1;
00401 else if (arg_match(&arg, &summaryarg, argi))
00402 summary = 1;
00403 else if (arg_match(&arg, &threadsarg, argi))
00404 cfg.threads = arg_parse_uint(&arg);
00405 else if (arg_match(&arg, &quietarg, argi))
00406 quiet = 1;
00407
00408 #if CONFIG_VP8_DECODER
00409 else if (arg_match(&arg, &addnoise_level, argi))
00410 {
00411 postproc = 1;
00412 vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE;
00413 vp8_pp_cfg.noise_level = arg_parse_uint(&arg);
00414 }
00415 else if (arg_match(&arg, &demacroblock_level, argi))
00416 {
00417 postproc = 1;
00418 vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK;
00419 vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg);
00420 }
00421 else if (arg_match(&arg, &deblock, argi))
00422 {
00423 postproc = 1;
00424 vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK;
00425 }
00426 else if (arg_match(&arg, &pp_debug_info, argi))
00427 {
00428 unsigned int level = arg_parse_uint(&arg);
00429
00430 postproc = 1;
00431 vp8_pp_cfg.post_proc_flag &= ~0x7;
00432
00433 if (level)
00434 vp8_pp_cfg.post_proc_flag |= 8 << (level - 1);
00435 }
00436
00437 #endif
00438 else
00439 argj++;
00440 }
00441
00442
00443 for (argi = argv; *argi; argi++)
00444 if (argi[0][0] == '-' && strlen(argi[0]) > 1)
00445 die("Error: Unrecognized option %s\n", *argi);
00446
00447
00448 fn = argv[0];
00449
00450 if (!fn)
00451 usage_exit();
00452
00453 if (!prefix)
00454 prefix = strdup("img");
00455
00456
00457 infile = strcmp(fn, "-") ? fopen(fn, "rb") : stdin;
00458
00459 if (!infile)
00460 {
00461 fprintf(stderr, "Failed to open file");
00462 return EXIT_FAILURE;
00463 }
00464
00465 if (fn2)
00466 out = out_open(fn2, do_md5);
00467
00468 is_ivf = file_is_ivf(infile, &fourcc, &width, &height,
00469 &timebase_num, &timebase_den);
00470
00471 if (is_ivf)
00472 {
00473 if (use_y4m)
00474 {
00475 char buffer[128];
00476 if (!fn2)
00477 {
00478 fprintf(stderr, "YUV4MPEG2 output only supported with -o.\n");
00479 return EXIT_FAILURE;
00480 }
00481
00482
00483 if(timebase_den&1)timebase_num<<=1;
00484 else timebase_den>>=1;
00485
00486
00487
00488 sprintf(buffer, "YUV4MPEG2 C%s W%u H%u F%u:%u I%c\n",
00489 "420jpeg", width, height, timebase_den, timebase_num, 'p');
00490 out_put(out, (unsigned char *)buffer, strlen(buffer), do_md5);
00491 }
00492
00493
00494 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
00495 if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc)
00496 {
00497 vpx_codec_iface_t *ivf_iface = ifaces[i].iface;
00498
00499 if (iface && iface != ivf_iface)
00500 fprintf(stderr, "Notice -- IVF header indicates codec: %s\n",
00501 ifaces[i].name);
00502 else
00503 iface = ivf_iface;
00504
00505 break;
00506 }
00507 }
00508 else if(use_y4m)
00509 {
00510 fprintf(stderr, "YUV4MPEG2 output only supported from IVF input.\n");
00511 return EXIT_FAILURE;
00512 }
00513
00514 if (vpx_codec_dec_init(&decoder, iface ? iface : ifaces[0].iface, &cfg,
00515 postproc ? VPX_CODEC_USE_POSTPROC : 0))
00516 {
00517 fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder));
00518 return EXIT_FAILURE;
00519 }
00520
00521 if (!quiet)
00522 fprintf(stderr, "%s\n", decoder.name);
00523
00524 #if CONFIG_VP8_DECODER
00525
00526 if (vp8_pp_cfg.post_proc_flag
00527 && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg))
00528 {
00529 fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder));
00530 return EXIT_FAILURE;
00531 }
00532
00533 #endif
00534
00535
00536 while (!read_frame(infile, &buf, &buf_sz, &buf_alloc_sz, is_ivf))
00537 {
00538 vpx_codec_iter_t iter = NULL;
00539 vpx_image_t *img;
00540 struct vpx_usec_timer timer;
00541
00542 vpx_usec_timer_start(&timer);
00543
00544 if (vpx_codec_decode(&decoder, buf, buf_sz, NULL, 0))
00545 {
00546 const char *detail = vpx_codec_error_detail(&decoder);
00547 fprintf(stderr, "Failed to decode frame: %s\n", vpx_codec_error(&decoder));
00548
00549 if (detail)
00550 fprintf(stderr, " Additional information: %s\n", detail);
00551
00552 goto fail;
00553 }
00554
00555 vpx_usec_timer_mark(&timer);
00556 dx_time += vpx_usec_timer_elapsed(&timer);
00557
00558 ++frame_in;
00559
00560 if (progress)
00561 fprintf(stderr, "decoded frame %d.\n", frame_in);
00562
00563 if ((img = vpx_codec_get_frame(&decoder, &iter)))
00564 ++frame_out;
00565
00566 if (!noblit)
00567 {
00568 if (img)
00569 {
00570 unsigned int y;
00571 char out_fn[128+24];
00572 uint8_t *buf;
00573 const char *sfx = flipuv ? "yv12" : "i420";
00574
00575 if (!fn2)
00576 {
00577 sprintf(out_fn, "%s-%dx%d-%04d.%s",
00578 prefix, img->d_w, img->d_h, frame_in, sfx);
00579 out = out_open(out_fn, do_md5);
00580 }
00581 else if(use_y4m)
00582 out_put(out, (unsigned char *)"FRAME\n", 6, do_md5);
00583
00584 buf = img->planes[VPX_PLANE_Y];
00585
00586 for (y = 0; y < img->d_h; y++)
00587 {
00588 out_put(out, buf, img->d_w, do_md5);
00589 buf += img->stride[VPX_PLANE_Y];
00590 }
00591
00592 buf = img->planes[flipuv?VPX_PLANE_V:VPX_PLANE_U];
00593
00594 for (y = 0; y < (1 + img->d_h) / 2; y++)
00595 {
00596 out_put(out, buf, (1 + img->d_w) / 2, do_md5);
00597 buf += img->stride[VPX_PLANE_U];
00598 }
00599
00600 buf = img->planes[flipuv?VPX_PLANE_U:VPX_PLANE_V];
00601
00602 for (y = 0; y < (1 + img->d_h) / 2; y++)
00603 {
00604 out_put(out, buf, (1 + img->d_w) / 2, do_md5);
00605 buf += img->stride[VPX_PLANE_V];
00606 }
00607
00608 if (!fn2)
00609 out_close(out, out_fn, do_md5);
00610 }
00611 }
00612
00613 if (stop_after && frame_in >= stop_after)
00614 break;
00615 }
00616
00617 if (summary)
00618 {
00619 fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\n",
00620 frame_in, frame_out, dx_time, (float)frame_out * 1000000.0 / (float)dx_time);
00621 }
00622
00623 fail:
00624
00625 if (vpx_codec_destroy(&decoder))
00626 {
00627 fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder));
00628 return EXIT_FAILURE;
00629 }
00630
00631 if (fn2)
00632 out_close(out, fn2, do_md5);
00633
00634 free(buf);
00635 fclose(infile);
00636 free(prefix);
00637 free(argv);
00638
00639 return EXIT_SUCCESS;
00640 }