WebM VP8 Codec SDK
|
00001 /* 00002 * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 00003 * 00004 * Use of this source code is governed by a BSD-style license 00005 * that can be found in the LICENSE file in the root of the source 00006 * tree. An additional intellectual property rights grant can be found 00007 * in the file PATENTS. All contributing project authors may 00008 * be found in the AUTHORS file in the root of the source tree. 00009 */ 00010 00011 00012 /* This is a simple program that reads ivf files and decodes them 00013 * using the new interface. Decoded frames are output as YV12 raw. 00014 */ 00015 #include <stdio.h> 00016 #include <stdlib.h> 00017 #include <stdarg.h> 00018 #include <string.h> 00019 #include <limits.h> 00020 00021 #define VPX_CODEC_DISABLE_COMPAT 1 00022 #include "vpx_config.h" 00023 #include "vpx/vpx_decoder.h" 00024 #include "vpx_ports/vpx_timer.h" 00025 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER 00026 #include "vpx/vp8dx.h" 00027 #endif 00028 #if CONFIG_MD5 00029 #include "md5_utils.h" 00030 #endif 00031 #include "tools_common.h" 00032 #include "nestegg/include/nestegg/nestegg.h" 00033 #include "third_party/libyuv/include/libyuv/scale.h" 00034 00035 #if CONFIG_OS_SUPPORT 00036 #if defined(_MSC_VER) 00037 #include <io.h> 00038 #define snprintf _snprintf 00039 #define isatty _isatty 00040 #define fileno _fileno 00041 #else 00042 #include <unistd.h> 00043 #endif 00044 #endif 00045 00046 #ifndef PATH_MAX 00047 #define PATH_MAX 256 00048 #endif 00049 00050 static const char *exec_name; 00051 00052 #define VP8_FOURCC (0x00385056) 00053 #define VP9_FOURCC (0x00395056) 00054 static const struct { 00055 char const *name; 00056 const vpx_codec_iface_t *(*iface)(void); 00057 unsigned int fourcc; 00058 unsigned int fourcc_mask; 00059 } ifaces[] = { 00060 #if CONFIG_VP8_DECODER 00061 {"vp8", vpx_codec_vp8_dx, VP8_FOURCC, 0x00FFFFFF}, 00062 #endif 00063 #if CONFIG_VP9_DECODER 00064 {"vp9", vpx_codec_vp9_dx, VP9_FOURCC, 0x00FFFFFF}, 00065 #endif 00066 }; 00067 00068 #include "args.h" 00069 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, 00070 "Codec to use"); 00071 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, 00072 "Output raw YV12 frames"); 00073 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0, 00074 "Output raw I420 frames"); 00075 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0, 00076 "Flip the chroma planes in the output"); 00077 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0, 00078 "Don't process the decoded frames"); 00079 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0, 00080 "Show progress after each frame decodes"); 00081 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1, 00082 "Stop decoding after n frames"); 00083 static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1, 00084 "Skip the first n input frames"); 00085 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0, 00086 "Postprocess decoded frames"); 00087 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0, 00088 "Show timing summary"); 00089 static const arg_def_t outputfile = ARG_DEF("o", "output", 1, 00090 "Output file name pattern (see below)"); 00091 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1, 00092 "Max threads to use"); 00093 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0, 00094 "Show version string"); 00095 static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0, 00096 "Enable decoder error-concealment"); 00097 static const arg_def_t scalearg = ARG_DEF("S", "scale", 0, 00098 "Scale output frames uniformly"); 00099 00100 00101 #if CONFIG_MD5 00102 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0, 00103 "Compute the MD5 sum of the decoded frame"); 00104 #endif 00105 static const arg_def_t *all_args[] = { 00106 &codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg, 00107 &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile, 00108 &threadsarg, &verbosearg, &scalearg, 00109 #if CONFIG_MD5 00110 &md5arg, 00111 #endif 00112 &error_concealment, 00113 NULL 00114 }; 00115 00116 #if CONFIG_VP8_DECODER 00117 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1, 00118 "Enable VP8 postproc add noise"); 00119 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0, 00120 "Enable VP8 deblocking"); 00121 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1, 00122 "Enable VP8 demacroblocking, w/ level"); 00123 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1, 00124 "Enable VP8 visible debug info"); 00125 static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1, 00126 "Display only selected reference frame per macro block"); 00127 static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1, 00128 "Display only selected macro block modes"); 00129 static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1, 00130 "Display only selected block modes"); 00131 static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1, 00132 "Draw only selected motion vectors"); 00133 static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0, 00134 "Enable multiframe quality enhancement"); 00135 00136 static const arg_def_t *vp8_pp_args[] = { 00137 &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info, 00138 &pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe, 00139 NULL 00140 }; 00141 #endif 00142 00143 static void usage_exit() { 00144 int i; 00145 00146 fprintf(stderr, "Usage: %s <options> filename\n\n" 00147 "Options:\n", exec_name); 00148 arg_show_usage(stderr, all_args); 00149 #if CONFIG_VP8_DECODER 00150 fprintf(stderr, "\nVP8 Postprocessing Options:\n"); 00151 arg_show_usage(stderr, vp8_pp_args); 00152 #endif 00153 fprintf(stderr, 00154 "\nOutput File Patterns:\n\n" 00155 " The -o argument specifies the name of the file(s) to " 00156 "write to. If the\n argument does not include any escape " 00157 "characters, the output will be\n written to a single file. " 00158 "Otherwise, the filename will be calculated by\n expanding " 00159 "the following escape characters:\n"); 00160 fprintf(stderr, 00161 "\n\t%%w - Frame width" 00162 "\n\t%%h - Frame height" 00163 "\n\t%%<n> - Frame number, zero padded to <n> places (1..9)" 00164 "\n\n Pattern arguments are only supported in conjunction " 00165 "with the --yv12 and\n --i420 options. If the -o option is " 00166 "not specified, the output will be\n directed to stdout.\n" 00167 ); 00168 fprintf(stderr, "\nIncluded decoders:\n\n"); 00169 00170 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 00171 fprintf(stderr, " %-6s - %s\n", 00172 ifaces[i].name, 00173 vpx_codec_iface_name(ifaces[i].iface())); 00174 00175 exit(EXIT_FAILURE); 00176 } 00177 00178 void die(const char *fmt, ...) { 00179 va_list ap; 00180 va_start(ap, fmt); 00181 vfprintf(stderr, fmt, ap); 00182 fprintf(stderr, "\n"); 00183 usage_exit(); 00184 } 00185 00186 static unsigned int mem_get_le16(const void *vmem) { 00187 unsigned int val; 00188 const unsigned char *mem = (const unsigned char *)vmem; 00189 00190 val = mem[1] << 8; 00191 val |= mem[0]; 00192 return val; 00193 } 00194 00195 static unsigned int mem_get_le32(const void *vmem) { 00196 unsigned int val; 00197 const unsigned char *mem = (const unsigned char *)vmem; 00198 00199 val = mem[3] << 24; 00200 val |= mem[2] << 16; 00201 val |= mem[1] << 8; 00202 val |= mem[0]; 00203 return val; 00204 } 00205 00206 enum file_kind { 00207 RAW_FILE, 00208 IVF_FILE, 00209 WEBM_FILE 00210 }; 00211 00212 struct input_ctx { 00213 enum file_kind kind; 00214 FILE *infile; 00215 nestegg *nestegg_ctx; 00216 nestegg_packet *pkt; 00217 unsigned int chunk; 00218 unsigned int chunks; 00219 unsigned int video_track; 00220 }; 00221 00222 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t)) 00223 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t)) 00224 static int read_frame(struct input_ctx *input, 00225 uint8_t **buf, 00226 size_t *buf_sz, 00227 size_t *buf_alloc_sz) { 00228 char raw_hdr[IVF_FRAME_HDR_SZ]; 00229 size_t new_buf_sz; 00230 FILE *infile = input->infile; 00231 enum file_kind kind = input->kind; 00232 if (kind == WEBM_FILE) { 00233 if (input->chunk >= input->chunks) { 00234 unsigned int track; 00235 00236 do { 00237 /* End of this packet, get another. */ 00238 if (input->pkt) 00239 nestegg_free_packet(input->pkt); 00240 00241 if (nestegg_read_packet(input->nestegg_ctx, &input->pkt) <= 0 00242 || nestegg_packet_track(input->pkt, &track)) 00243 return 1; 00244 00245 } while (track != input->video_track); 00246 00247 if (nestegg_packet_count(input->pkt, &input->chunks)) 00248 return 1; 00249 input->chunk = 0; 00250 } 00251 00252 if (nestegg_packet_data(input->pkt, input->chunk, buf, buf_sz)) 00253 return 1; 00254 input->chunk++; 00255 00256 return 0; 00257 } 00258 /* For both the raw and ivf formats, the frame size is the first 4 bytes 00259 * of the frame header. We just need to special case on the header 00260 * size. 00261 */ 00262 else if (fread(raw_hdr, kind == IVF_FILE 00263 ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, infile) != 1) { 00264 if (!feof(infile)) 00265 fprintf(stderr, "Failed to read frame size\n"); 00266 00267 new_buf_sz = 0; 00268 } else { 00269 new_buf_sz = mem_get_le32(raw_hdr); 00270 00271 if (new_buf_sz > 256 * 1024 * 1024) { 00272 fprintf(stderr, "Error: Read invalid frame size (%u)\n", 00273 (unsigned int)new_buf_sz); 00274 new_buf_sz = 0; 00275 } 00276 00277 if (kind == RAW_FILE && new_buf_sz > 256 * 1024) 00278 fprintf(stderr, "Warning: Read invalid frame size (%u)" 00279 " - not a raw file?\n", (unsigned int)new_buf_sz); 00280 00281 if (new_buf_sz > *buf_alloc_sz) { 00282 uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz); 00283 00284 if (new_buf) { 00285 *buf = new_buf; 00286 *buf_alloc_sz = 2 * new_buf_sz; 00287 } else { 00288 fprintf(stderr, "Failed to allocate compressed data buffer\n"); 00289 new_buf_sz = 0; 00290 } 00291 } 00292 } 00293 00294 *buf_sz = new_buf_sz; 00295 00296 if (!feof(infile)) { 00297 if (fread(*buf, 1, *buf_sz, infile) != *buf_sz) { 00298 fprintf(stderr, "Failed to read full frame\n"); 00299 return 1; 00300 } 00301 00302 return 0; 00303 } 00304 00305 return 1; 00306 } 00307 00308 void *out_open(const char *out_fn, int do_md5) { 00309 void *out = NULL; 00310 00311 if (do_md5) { 00312 #if CONFIG_MD5 00313 MD5Context *md5_ctx = out = malloc(sizeof(MD5Context)); 00314 (void)out_fn; 00315 MD5Init(md5_ctx); 00316 #endif 00317 } else { 00318 FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") 00319 : set_binary_mode(stdout); 00320 00321 if (!outfile) { 00322 fprintf(stderr, "Failed to output file"); 00323 exit(EXIT_FAILURE); 00324 } 00325 } 00326 00327 return out; 00328 } 00329 00330 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5) { 00331 if (do_md5) { 00332 #if CONFIG_MD5 00333 MD5Update(out, buf, len); 00334 #endif 00335 } else { 00336 (void) fwrite(buf, 1, len, out); 00337 } 00338 } 00339 00340 void out_close(void *out, const char *out_fn, int do_md5) { 00341 if (do_md5) { 00342 #if CONFIG_MD5 00343 uint8_t md5[16]; 00344 int i; 00345 00346 MD5Final(md5, out); 00347 free(out); 00348 00349 for (i = 0; i < 16; i++) 00350 printf("%02x", md5[i]); 00351 00352 printf(" %s\n", out_fn); 00353 #endif 00354 } else { 00355 fclose(out); 00356 } 00357 } 00358 00359 unsigned int file_is_ivf(FILE *infile, 00360 unsigned int *fourcc, 00361 unsigned int *width, 00362 unsigned int *height, 00363 unsigned int *fps_den, 00364 unsigned int *fps_num) { 00365 char raw_hdr[32]; 00366 int is_ivf = 0; 00367 00368 if (fread(raw_hdr, 1, 32, infile) == 32) { 00369 if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K' 00370 && raw_hdr[2] == 'I' && raw_hdr[3] == 'F') { 00371 is_ivf = 1; 00372 00373 if (mem_get_le16(raw_hdr + 4) != 0) 00374 fprintf(stderr, "Error: Unrecognized IVF version! This file may not" 00375 " decode properly."); 00376 00377 *fourcc = mem_get_le32(raw_hdr + 8); 00378 *width = mem_get_le16(raw_hdr + 12); 00379 *height = mem_get_le16(raw_hdr + 14); 00380 *fps_num = mem_get_le32(raw_hdr + 16); 00381 *fps_den = mem_get_le32(raw_hdr + 20); 00382 00383 /* Some versions of vpxenc used 1/(2*fps) for the timebase, so 00384 * we can guess the framerate using only the timebase in this 00385 * case. Other files would require reading ahead to guess the 00386 * timebase, like we do for webm. 00387 */ 00388 if (*fps_num < 1000) { 00389 /* Correct for the factor of 2 applied to the timebase in the 00390 * encoder. 00391 */ 00392 if (*fps_num & 1)*fps_den <<= 1; 00393 else *fps_num >>= 1; 00394 } else { 00395 /* Don't know FPS for sure, and don't have readahead code 00396 * (yet?), so just default to 30fps. 00397 */ 00398 *fps_num = 30; 00399 *fps_den = 1; 00400 } 00401 } 00402 } 00403 00404 if (!is_ivf) 00405 rewind(infile); 00406 00407 return is_ivf; 00408 } 00409 00410 00411 unsigned int file_is_raw(FILE *infile, 00412 unsigned int *fourcc, 00413 unsigned int *width, 00414 unsigned int *height, 00415 unsigned int *fps_den, 00416 unsigned int *fps_num) { 00417 unsigned char buf[32]; 00418 int is_raw = 0; 00419 vpx_codec_stream_info_t si; 00420 00421 si.sz = sizeof(si); 00422 00423 if (fread(buf, 1, 32, infile) == 32) { 00424 int i; 00425 00426 if (mem_get_le32(buf) < 256 * 1024 * 1024) 00427 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 00428 if (!vpx_codec_peek_stream_info(ifaces[i].iface(), 00429 buf + 4, 32 - 4, &si)) { 00430 is_raw = 1; 00431 *fourcc = ifaces[i].fourcc; 00432 *width = si.w; 00433 *height = si.h; 00434 *fps_num = 30; 00435 *fps_den = 1; 00436 break; 00437 } 00438 } 00439 00440 rewind(infile); 00441 return is_raw; 00442 } 00443 00444 00445 static int 00446 nestegg_read_cb(void *buffer, size_t length, void *userdata) { 00447 FILE *f = userdata; 00448 00449 if (fread(buffer, 1, length, f) < length) { 00450 if (ferror(f)) 00451 return -1; 00452 if (feof(f)) 00453 return 0; 00454 } 00455 return 1; 00456 } 00457 00458 00459 static int 00460 nestegg_seek_cb(int64_t offset, int whence, void *userdata) { 00461 switch (whence) { 00462 case NESTEGG_SEEK_SET: 00463 whence = SEEK_SET; 00464 break; 00465 case NESTEGG_SEEK_CUR: 00466 whence = SEEK_CUR; 00467 break; 00468 case NESTEGG_SEEK_END: 00469 whence = SEEK_END; 00470 break; 00471 }; 00472 return fseek(userdata, (long)offset, whence) ? -1 : 0; 00473 } 00474 00475 00476 static int64_t 00477 nestegg_tell_cb(void *userdata) { 00478 return ftell(userdata); 00479 } 00480 00481 00482 static void 00483 nestegg_log_cb(nestegg *context, unsigned int severity, char const *format, 00484 ...) { 00485 va_list ap; 00486 00487 va_start(ap, format); 00488 vfprintf(stderr, format, ap); 00489 fprintf(stderr, "\n"); 00490 va_end(ap); 00491 } 00492 00493 00494 static int 00495 webm_guess_framerate(struct input_ctx *input, 00496 unsigned int *fps_den, 00497 unsigned int *fps_num) { 00498 unsigned int i; 00499 uint64_t tstamp = 0; 00500 00501 /* Guess the framerate. Read up to 1 second, or 50 video packets, 00502 * whichever comes first. 00503 */ 00504 for (i = 0; tstamp < 1000000000 && i < 50;) { 00505 nestegg_packet *pkt; 00506 unsigned int track; 00507 00508 if (nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0) 00509 break; 00510 00511 nestegg_packet_track(pkt, &track); 00512 if (track == input->video_track) { 00513 nestegg_packet_tstamp(pkt, &tstamp); 00514 i++; 00515 } 00516 00517 nestegg_free_packet(pkt); 00518 } 00519 00520 if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) 00521 goto fail; 00522 00523 *fps_num = (i - 1) * 1000000; 00524 *fps_den = (unsigned int)(tstamp / 1000); 00525 return 0; 00526 fail: 00527 nestegg_destroy(input->nestegg_ctx); 00528 input->nestegg_ctx = NULL; 00529 rewind(input->infile); 00530 return 1; 00531 } 00532 00533 00534 static int 00535 file_is_webm(struct input_ctx *input, 00536 unsigned int *fourcc, 00537 unsigned int *width, 00538 unsigned int *height, 00539 unsigned int *fps_den, 00540 unsigned int *fps_num) { 00541 unsigned int i, n; 00542 int track_type = -1; 00543 int codec_id; 00544 00545 nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; 00546 nestegg_video_params params; 00547 00548 io.userdata = input->infile; 00549 if (nestegg_init(&input->nestegg_ctx, io, NULL)) 00550 goto fail; 00551 00552 if (nestegg_track_count(input->nestegg_ctx, &n)) 00553 goto fail; 00554 00555 for (i = 0; i < n; i++) { 00556 track_type = nestegg_track_type(input->nestegg_ctx, i); 00557 00558 if (track_type == NESTEGG_TRACK_VIDEO) 00559 break; 00560 else if (track_type < 0) 00561 goto fail; 00562 } 00563 00564 codec_id = nestegg_track_codec_id(input->nestegg_ctx, i); 00565 if (codec_id == NESTEGG_CODEC_VP8) { 00566 *fourcc = VP8_FOURCC; 00567 } else if (codec_id == NESTEGG_CODEC_VP9) { 00568 *fourcc = VP9_FOURCC; 00569 } else { 00570 fprintf(stderr, "Not VPx video, quitting.\n"); 00571 exit(1); 00572 } 00573 00574 input->video_track = i; 00575 00576 if (nestegg_track_video_params(input->nestegg_ctx, i, ¶ms)) 00577 goto fail; 00578 00579 *fps_den = 0; 00580 *fps_num = 0; 00581 *width = params.width; 00582 *height = params.height; 00583 return 1; 00584 fail: 00585 input->nestegg_ctx = NULL; 00586 rewind(input->infile); 00587 return 0; 00588 } 00589 00590 00591 void show_progress(int frame_in, int frame_out, unsigned long dx_time) { 00592 fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\r", 00593 frame_in, frame_out, dx_time, 00594 (float)frame_out * 1000000.0 / (float)dx_time); 00595 } 00596 00597 00598 void generate_filename(const char *pattern, char *out, size_t q_len, 00599 unsigned int d_w, unsigned int d_h, 00600 unsigned int frame_in) { 00601 const char *p = pattern; 00602 char *q = out; 00603 00604 do { 00605 char *next_pat = strchr(p, '%'); 00606 00607 if (p == next_pat) { 00608 size_t pat_len; 00609 00610 /* parse the pattern */ 00611 q[q_len - 1] = '\0'; 00612 switch (p[1]) { 00613 case 'w': 00614 snprintf(q, q_len - 1, "%d", d_w); 00615 break; 00616 case 'h': 00617 snprintf(q, q_len - 1, "%d", d_h); 00618 break; 00619 case '1': 00620 snprintf(q, q_len - 1, "%d", frame_in); 00621 break; 00622 case '2': 00623 snprintf(q, q_len - 1, "%02d", frame_in); 00624 break; 00625 case '3': 00626 snprintf(q, q_len - 1, "%03d", frame_in); 00627 break; 00628 case '4': 00629 snprintf(q, q_len - 1, "%04d", frame_in); 00630 break; 00631 case '5': 00632 snprintf(q, q_len - 1, "%05d", frame_in); 00633 break; 00634 case '6': 00635 snprintf(q, q_len - 1, "%06d", frame_in); 00636 break; 00637 case '7': 00638 snprintf(q, q_len - 1, "%07d", frame_in); 00639 break; 00640 case '8': 00641 snprintf(q, q_len - 1, "%08d", frame_in); 00642 break; 00643 case '9': 00644 snprintf(q, q_len - 1, "%09d", frame_in); 00645 break; 00646 default: 00647 die("Unrecognized pattern %%%c\n", p[1]); 00648 } 00649 00650 pat_len = strlen(q); 00651 if (pat_len >= q_len - 1) 00652 die("Output filename too long.\n"); 00653 q += pat_len; 00654 p += 2; 00655 q_len -= pat_len; 00656 } else { 00657 size_t copy_len; 00658 00659 /* copy the next segment */ 00660 if (!next_pat) 00661 copy_len = strlen(p); 00662 else 00663 copy_len = next_pat - p; 00664 00665 if (copy_len >= q_len - 1) 00666 die("Output filename too long.\n"); 00667 00668 memcpy(q, p, copy_len); 00669 q[copy_len] = '\0'; 00670 q += copy_len; 00671 p += copy_len; 00672 q_len -= copy_len; 00673 } 00674 } while (*p); 00675 } 00676 00677 00678 int main(int argc, const char **argv_) { 00679 vpx_codec_ctx_t decoder; 00680 char *fn = NULL; 00681 int i; 00682 uint8_t *buf = NULL; 00683 size_t buf_sz = 0, buf_alloc_sz = 0; 00684 FILE *infile; 00685 int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0; 00686 int stop_after = 0, postproc = 0, summary = 0, quiet = 1; 00687 int arg_skip = 0; 00688 int ec_enabled = 0; 00689 vpx_codec_iface_t *iface = NULL; 00690 unsigned int fourcc; 00691 unsigned long dx_time = 0; 00692 struct arg arg; 00693 char **argv, **argi, **argj; 00694 const char *outfile_pattern = 0; 00695 char outfile[PATH_MAX]; 00696 int single_file; 00697 int use_y4m = 1; 00698 unsigned int width; 00699 unsigned int height; 00700 unsigned int fps_den; 00701 unsigned int fps_num; 00702 void *out = NULL; 00703 vpx_codec_dec_cfg_t cfg = {0}; 00704 #if CONFIG_VP8_DECODER 00705 vp8_postproc_cfg_t vp8_pp_cfg = {0}; 00706 int vp8_dbg_color_ref_frame = 0; 00707 int vp8_dbg_color_mb_modes = 0; 00708 int vp8_dbg_color_b_modes = 0; 00709 int vp8_dbg_display_mv = 0; 00710 #endif 00711 struct input_ctx input = {0}; 00712 int frames_corrupted = 0; 00713 int dec_flags = 0; 00714 int do_scale = 0; 00715 int stream_w = 0, stream_h = 0; 00716 vpx_image_t *scaled_img = NULL; 00717 00718 /* Parse command line */ 00719 exec_name = argv_[0]; 00720 argv = argv_dup(argc - 1, argv_ + 1); 00721 00722 for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { 00723 memset(&arg, 0, sizeof(arg)); 00724 arg.argv_step = 1; 00725 00726 if (arg_match(&arg, &codecarg, argi)) { 00727 int j, k = -1; 00728 00729 for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++) 00730 if (!strcmp(ifaces[j].name, arg.val)) 00731 k = j; 00732 00733 if (k >= 0) 00734 iface = ifaces[k].iface(); 00735 else 00736 die("Error: Unrecognized argument (%s) to --codec\n", 00737 arg.val); 00738 } else if (arg_match(&arg, &outputfile, argi)) 00739 outfile_pattern = arg.val; 00740 else if (arg_match(&arg, &use_yv12, argi)) { 00741 use_y4m = 0; 00742 flipuv = 1; 00743 } else if (arg_match(&arg, &use_i420, argi)) { 00744 use_y4m = 0; 00745 flipuv = 0; 00746 } else if (arg_match(&arg, &flipuvarg, argi)) 00747 flipuv = 1; 00748 else if (arg_match(&arg, &noblitarg, argi)) 00749 noblit = 1; 00750 else if (arg_match(&arg, &progressarg, argi)) 00751 progress = 1; 00752 else if (arg_match(&arg, &limitarg, argi)) 00753 stop_after = arg_parse_uint(&arg); 00754 else if (arg_match(&arg, &skiparg, argi)) 00755 arg_skip = arg_parse_uint(&arg); 00756 else if (arg_match(&arg, &postprocarg, argi)) 00757 postproc = 1; 00758 else if (arg_match(&arg, &md5arg, argi)) 00759 do_md5 = 1; 00760 else if (arg_match(&arg, &summaryarg, argi)) 00761 summary = 1; 00762 else if (arg_match(&arg, &threadsarg, argi)) 00763 cfg.threads = arg_parse_uint(&arg); 00764 else if (arg_match(&arg, &verbosearg, argi)) 00765 quiet = 0; 00766 else if (arg_match(&arg, &scalearg, argi)) 00767 do_scale = 1; 00768 00769 #if CONFIG_VP8_DECODER 00770 else if (arg_match(&arg, &addnoise_level, argi)) { 00771 postproc = 1; 00772 vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE; 00773 vp8_pp_cfg.noise_level = arg_parse_uint(&arg); 00774 } else if (arg_match(&arg, &demacroblock_level, argi)) { 00775 postproc = 1; 00776 vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK; 00777 vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg); 00778 } else if (arg_match(&arg, &deblock, argi)) { 00779 postproc = 1; 00780 vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK; 00781 } else if (arg_match(&arg, &mfqe, argi)) { 00782 postproc = 1; 00783 vp8_pp_cfg.post_proc_flag |= VP8_MFQE; 00784 } else if (arg_match(&arg, &pp_debug_info, argi)) { 00785 unsigned int level = arg_parse_uint(&arg); 00786 00787 postproc = 1; 00788 vp8_pp_cfg.post_proc_flag &= ~0x7; 00789 00790 if (level) 00791 vp8_pp_cfg.post_proc_flag |= level; 00792 } else if (arg_match(&arg, &pp_disp_ref_frame, argi)) { 00793 unsigned int flags = arg_parse_int(&arg); 00794 if (flags) { 00795 postproc = 1; 00796 vp8_dbg_color_ref_frame = flags; 00797 } 00798 } else if (arg_match(&arg, &pp_disp_mb_modes, argi)) { 00799 unsigned int flags = arg_parse_int(&arg); 00800 if (flags) { 00801 postproc = 1; 00802 vp8_dbg_color_mb_modes = flags; 00803 } 00804 } else if (arg_match(&arg, &pp_disp_b_modes, argi)) { 00805 unsigned int flags = arg_parse_int(&arg); 00806 if (flags) { 00807 postproc = 1; 00808 vp8_dbg_color_b_modes = flags; 00809 } 00810 } else if (arg_match(&arg, &pp_disp_mvs, argi)) { 00811 unsigned int flags = arg_parse_int(&arg); 00812 if (flags) { 00813 postproc = 1; 00814 vp8_dbg_display_mv = flags; 00815 } 00816 } else if (arg_match(&arg, &error_concealment, argi)) { 00817 ec_enabled = 1; 00818 } 00819 00820 #endif 00821 else 00822 argj++; 00823 } 00824 00825 /* Check for unrecognized options */ 00826 for (argi = argv; *argi; argi++) 00827 if (argi[0][0] == '-' && strlen(argi[0]) > 1) 00828 die("Error: Unrecognized option %s\n", *argi); 00829 00830 /* Handle non-option arguments */ 00831 fn = argv[0]; 00832 00833 if (!fn) 00834 usage_exit(); 00835 00836 /* Open file */ 00837 infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin); 00838 00839 if (!infile) { 00840 fprintf(stderr, "Failed to open file '%s'", 00841 strcmp(fn, "-") ? fn : "stdin"); 00842 return EXIT_FAILURE; 00843 } 00844 #if CONFIG_OS_SUPPORT 00845 /* Make sure we don't dump to the terminal, unless forced to with -o - */ 00846 if (!outfile_pattern && isatty(fileno(stdout)) && !do_md5 && !noblit) { 00847 fprintf(stderr, 00848 "Not dumping raw video to your terminal. Use '-o -' to " 00849 "override.\n"); 00850 return EXIT_FAILURE; 00851 } 00852 #endif 00853 input.infile = infile; 00854 if (file_is_ivf(infile, &fourcc, &width, &height, &fps_den, 00855 &fps_num)) 00856 input.kind = IVF_FILE; 00857 else if (file_is_webm(&input, &fourcc, &width, &height, &fps_den, &fps_num)) 00858 input.kind = WEBM_FILE; 00859 else if (file_is_raw(infile, &fourcc, &width, &height, &fps_den, &fps_num)) 00860 input.kind = RAW_FILE; 00861 else { 00862 fprintf(stderr, "Unrecognized input file type.\n"); 00863 return EXIT_FAILURE; 00864 } 00865 00866 /* If the output file is not set or doesn't have a sequence number in 00867 * it, then we only open it once. 00868 */ 00869 outfile_pattern = outfile_pattern ? outfile_pattern : "-"; 00870 single_file = 1; 00871 { 00872 const char *p = outfile_pattern; 00873 do { 00874 p = strchr(p, '%'); 00875 if (p && p[1] >= '1' && p[1] <= '9') { 00876 /* pattern contains sequence number, so it's not unique. */ 00877 single_file = 0; 00878 break; 00879 } 00880 if (p) 00881 p++; 00882 } while (p); 00883 } 00884 00885 if (single_file && !noblit) { 00886 generate_filename(outfile_pattern, outfile, sizeof(outfile) - 1, 00887 width, height, 0); 00888 out = out_open(outfile, do_md5); 00889 } 00890 00891 if (use_y4m && !noblit) { 00892 char buffer[128]; 00893 if (!single_file) { 00894 fprintf(stderr, "YUV4MPEG2 not supported with output patterns," 00895 " try --i420 or --yv12.\n"); 00896 return EXIT_FAILURE; 00897 } 00898 00899 if (input.kind == WEBM_FILE) 00900 if (webm_guess_framerate(&input, &fps_den, &fps_num)) { 00901 fprintf(stderr, "Failed to guess framerate -- error parsing " 00902 "webm file?\n"); 00903 return EXIT_FAILURE; 00904 } 00905 00906 00907 /*Note: We can't output an aspect ratio here because IVF doesn't 00908 store one, and neither does VP8. 00909 That will have to wait until these tools support WebM natively.*/ 00910 sprintf(buffer, "YUV4MPEG2 C%s W%u H%u F%u:%u I%c\n", 00911 "420jpeg", width, height, fps_num, fps_den, 'p'); 00912 out_put(out, (unsigned char *)buffer, 00913 (unsigned int)strlen(buffer), do_md5); 00914 } 00915 00916 /* Try to determine the codec from the fourcc. */ 00917 for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++) 00918 if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc) { 00919 vpx_codec_iface_t *ivf_iface = ifaces[i].iface(); 00920 00921 if (iface && iface != ivf_iface) 00922 fprintf(stderr, "Notice -- IVF header indicates codec: %s\n", 00923 ifaces[i].name); 00924 else 00925 iface = ivf_iface; 00926 00927 break; 00928 } 00929 00930 dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) | 00931 (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0); 00932 if (vpx_codec_dec_init(&decoder, iface ? iface : ifaces[0].iface(), &cfg, 00933 dec_flags)) { 00934 fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder)); 00935 return EXIT_FAILURE; 00936 } 00937 00938 if (!quiet) 00939 fprintf(stderr, "%s\n", decoder.name); 00940 00941 #if CONFIG_VP8_DECODER 00942 00943 if (vp8_pp_cfg.post_proc_flag 00944 && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) { 00945 fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder)); 00946 return EXIT_FAILURE; 00947 } 00948 00949 if (vp8_dbg_color_ref_frame 00950 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_REF_FRAME, vp8_dbg_color_ref_frame)) { 00951 fprintf(stderr, "Failed to configure reference block visualizer: %s\n", vpx_codec_error(&decoder)); 00952 return EXIT_FAILURE; 00953 } 00954 00955 if (vp8_dbg_color_mb_modes 00956 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_MB_MODES, vp8_dbg_color_mb_modes)) { 00957 fprintf(stderr, "Failed to configure macro block visualizer: %s\n", vpx_codec_error(&decoder)); 00958 return EXIT_FAILURE; 00959 } 00960 00961 if (vp8_dbg_color_b_modes 00962 && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_B_MODES, vp8_dbg_color_b_modes)) { 00963 fprintf(stderr, "Failed to configure block visualizer: %s\n", vpx_codec_error(&decoder)); 00964 return EXIT_FAILURE; 00965 } 00966 00967 if (vp8_dbg_display_mv 00968 && vpx_codec_control(&decoder, VP8_SET_DBG_DISPLAY_MV, vp8_dbg_display_mv)) { 00969 fprintf(stderr, "Failed to configure motion vector visualizer: %s\n", vpx_codec_error(&decoder)); 00970 return EXIT_FAILURE; 00971 } 00972 #endif 00973 00974 00975 if(arg_skip) 00976 fprintf(stderr, "Skiping first %d frames.\n", arg_skip); 00977 while (arg_skip) { 00978 if (read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) 00979 break; 00980 arg_skip--; 00981 } 00982 00983 /* Decode file */ 00984 while (!read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) { 00985 vpx_codec_iter_t iter = NULL; 00986 vpx_image_t *img; 00987 struct vpx_usec_timer timer; 00988 int corrupted; 00989 00990 vpx_usec_timer_start(&timer); 00991 00992 if (vpx_codec_decode(&decoder, buf, (unsigned int)buf_sz, NULL, 0)) { 00993 const char *detail = vpx_codec_error_detail(&decoder); 00994 fprintf(stderr, "Failed to decode frame: %s\n", vpx_codec_error(&decoder)); 00995 00996 if (detail) 00997 fprintf(stderr, " Additional information: %s\n", detail); 00998 00999 goto fail; 01000 } 01001 01002 vpx_usec_timer_mark(&timer); 01003 dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); 01004 01005 ++frame_in; 01006 01007 if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) { 01008 fprintf(stderr, "Failed VP8_GET_FRAME_CORRUPTED: %s\n", 01009 vpx_codec_error(&decoder)); 01010 goto fail; 01011 } 01012 frames_corrupted += corrupted; 01013 01014 vpx_usec_timer_start(&timer); 01015 01016 if ((img = vpx_codec_get_frame(&decoder, &iter))) 01017 ++frame_out; 01018 01019 vpx_usec_timer_mark(&timer); 01020 dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer); 01021 01022 if (progress) 01023 show_progress(frame_in, frame_out, dx_time); 01024 01025 if (!noblit) { 01026 if (do_scale) { 01027 if (img && frame_out == 1) { 01028 stream_w = img->d_w; 01029 stream_h = img->d_h; 01030 scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 01031 stream_w, stream_h, 16); 01032 } 01033 if (img && (img->d_w != stream_w || img->d_h != stream_h)) { 01034 I420Scale(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], 01035 img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U], 01036 img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V], 01037 img->d_w, img->d_h, 01038 scaled_img->planes[VPX_PLANE_Y], 01039 scaled_img->stride[VPX_PLANE_Y], 01040 scaled_img->planes[VPX_PLANE_U], 01041 scaled_img->stride[VPX_PLANE_U], 01042 scaled_img->planes[VPX_PLANE_V], 01043 scaled_img->stride[VPX_PLANE_V], 01044 stream_w, stream_h, 01045 kFilterBox); 01046 img = scaled_img; 01047 } 01048 } 01049 01050 if (img) { 01051 unsigned int y; 01052 char out_fn[PATH_MAX]; 01053 uint8_t *buf; 01054 01055 if (!single_file) { 01056 size_t len = sizeof(out_fn) - 1; 01057 01058 out_fn[len] = '\0'; 01059 generate_filename(outfile_pattern, out_fn, len - 1, 01060 img->d_w, img->d_h, frame_in); 01061 out = out_open(out_fn, do_md5); 01062 } else if (use_y4m) 01063 out_put(out, (unsigned char *)"FRAME\n", 6, do_md5); 01064 01065 buf = img->planes[VPX_PLANE_Y]; 01066 01067 for (y = 0; y < img->d_h; y++) { 01068 out_put(out, buf, img->d_w, do_md5); 01069 buf += img->stride[VPX_PLANE_Y]; 01070 } 01071 01072 buf = img->planes[flipuv ? VPX_PLANE_V : VPX_PLANE_U]; 01073 01074 for (y = 0; y < (1 + img->d_h) / 2; y++) { 01075 out_put(out, buf, (1 + img->d_w) / 2, do_md5); 01076 buf += img->stride[VPX_PLANE_U]; 01077 } 01078 01079 buf = img->planes[flipuv ? VPX_PLANE_U : VPX_PLANE_V]; 01080 01081 for (y = 0; y < (1 + img->d_h) / 2; y++) { 01082 out_put(out, buf, (1 + img->d_w) / 2, do_md5); 01083 buf += img->stride[VPX_PLANE_V]; 01084 } 01085 01086 if (!single_file) 01087 out_close(out, out_fn, do_md5); 01088 } 01089 } 01090 01091 if (stop_after && frame_in >= stop_after) 01092 break; 01093 } 01094 01095 if (summary || progress) { 01096 show_progress(frame_in, frame_out, dx_time); 01097 fprintf(stderr, "\n"); 01098 } 01099 01100 if (frames_corrupted) 01101 fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted); 01102 01103 fail: 01104 01105 if (vpx_codec_destroy(&decoder)) { 01106 fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder)); 01107 return EXIT_FAILURE; 01108 } 01109 01110 if (single_file && !noblit) 01111 out_close(out, outfile, do_md5); 01112 01113 if (input.nestegg_ctx) 01114 nestegg_destroy(input.nestegg_ctx); 01115 if (input.kind != WEBM_FILE) 01116 free(buf); 01117 fclose(infile); 01118 free(argv); 01119 01120 return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS; 01121 }