Newer
Older
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "third_party/libyuv/include/libyuv/scale.h"
#include "./args.h"
#include "vpx/vpx_decoder.h"
#include "vpx_ports/mem_ops.h"
#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
#include "vpx/vp8dx.h"
#include "./tools_common.h"
#include "./y4menc.h"
struct VpxDecInputContext {
struct VpxInputContext *vpx_input_ctx;
struct WebmInputContext *webm_ctx;
};
static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1,
"Number of times to decode the file");
static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1,
"Skip the first n input frames");
static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0,
"Enable decoder error-concealment");
static const arg_def_t scalearg = ARG_DEF("S", "scale", 0,
"Scale output frames uniformly");
static const arg_def_t fb_arg =
ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use");
static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
"Compute the MD5 sum of the decoded frame");
static const arg_def_t *all_args[] = {
&codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg,
&progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
&threadsarg, &verbosearg, &scalearg, &fb_arg,
static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1,
static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1,
static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1,
static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1,
static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0,
"Enable multiframe quality enhancement");
static const arg_def_t *vp8_pp_args[] = {
&addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
&pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe,
static int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst,
assert(src->fmt == VPX_IMG_FMT_I420);
assert(dst->fmt == VPX_IMG_FMT_I420);
return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y],
src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U],
src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V],
src->d_w, src->d_h,
dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y],
dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U],
dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V],
dst->d_w, dst->d_h,
mode);
}
fprintf(stderr, "Usage: %s <options> filename\n\n"
"Options:\n", exec_name);
arg_show_usage(stderr, all_args);
fprintf(stderr, "\nVP8 Postprocessing Options:\n");
arg_show_usage(stderr, vp8_pp_args);
fprintf(stderr,
"\nOutput File Patterns:\n\n"
" The -o argument specifies the name of the file(s) to "
"write to. If the\n argument does not include any escape "
"characters, the output will be\n written to a single file. "
"Otherwise, the filename will be calculated by\n expanding "
"the following escape characters:\n");
fprintf(stderr,
"\n\t%%w - Frame width"
"\n\t%%h - Frame height"
"\n\t%%<n> - Frame number, zero padded to <n> places (1..9)"
"\n\n Pattern arguments are only supported in conjunction "
"with the --yv12 and\n --i420 options. If the -o option is "
"not specified, the output will be\n directed to stdout.\n"
);
fprintf(stderr, "\nIncluded decoders:\n\n");
for (i = 0; i < get_vpx_decoder_count(); ++i) {
const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
decoder->name, vpx_codec_iface_name(decoder->interface()));
}
static int raw_read_frame(FILE *infile, uint8_t **buffer,
size_t *bytes_read, size_t *buffer_size) {
char raw_hdr[RAW_FRAME_HDR_SZ];
if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) {
if (!feof(infile))
warn("Failed to read RAW frame size\n");
} else {
const size_t kCorruptFrameThreshold = 256 * 1024 * 1024;
const size_t kFrameTooSmallThreshold = 256 * 1024;
frame_size = mem_get_le32(raw_hdr);
if (frame_size > kCorruptFrameThreshold) {
warn("Read invalid frame size (%u)\n", (unsigned int)frame_size);
frame_size = 0;
}
if (frame_size < kFrameTooSmallThreshold) {
warn("Warning: Read invalid frame size (%u) - not a raw file?\n",
(unsigned int)frame_size);
if (frame_size > *buffer_size) {
uint8_t *new_buf = realloc(*buffer, 2 * frame_size);
if (new_buf) {
*buffer = new_buf;
*buffer_size = 2 * frame_size;
} else {
warn("Failed to allocate compressed data buffer\n");
frame_size = 0;
if (!feof(infile)) {
if (fread(*buffer, 1, frame_size, infile) != frame_size) {
warn("Failed to read full frame\n");
return 1;
}
*bytes_read = frame_size;
return 0;
}
static int read_frame(struct VpxDecInputContext *input, uint8_t **buf,
size_t *bytes_in_buffer, size_t *buffer_size) {
switch (input->vpx_input_ctx->file_type) {
#if CONFIG_WEBM_IO
case FILE_TYPE_WEBM:
return webm_read_frame(input->webm_ctx,
buf, bytes_in_buffer, buffer_size);
#endif
return raw_read_frame(input->vpx_input_ctx->file,
buf, bytes_in_buffer, buffer_size);
case FILE_TYPE_IVF:
return ivf_read_frame(input->vpx_input_ctx->file,
buf, bytes_in_buffer, buffer_size);
default:
return 1;
}
static void update_image_md5(const vpx_image_t *img, const int planes[3],
MD5Context *md5) {
int i, y;
for (i = 0; i < 3; ++i) {
const int plane = planes[i];
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
const int w = vpx_img_plane_width(img, plane);
const int h = vpx_img_plane_height(img, plane);
for (y = 0; y < h; ++y) {
MD5Update(md5, buf, w);
buf += stride;
}
}
}
static void write_image_file(const vpx_image_t *img, const int planes[3],
FILE *file) {
int i, y;
for (i = 0; i < 3; ++i) {
const int plane = planes[i];
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
const int w = vpx_img_plane_width(img, plane);
const int h = vpx_img_plane_height(img, plane);
for (y = 0; y < h; ++y) {
fwrite(buf, 1, w, file);
buf += stride;
}
int file_is_raw(struct VpxInputContext *input) {
uint8_t buf[32];
if (fread(buf, 1, 32, input->file) == 32) {
if (mem_get_le32(buf) < 256 * 1024 * 1024) {
for (i = 0; i < get_vpx_decoder_count(); ++i) {
const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
if (!vpx_codec_peek_stream_info(decoder->interface(),
input->fourcc = decoder->fourcc;
input->width = si.w;
input->height = si.h;
input->framerate.numerator = 30;
input->framerate.denominator = 1;
void show_progress(int frame_in, int frame_out, uint64_t dx_time) {
fprintf(stderr,
"%d decoded frames/%d showed frames in %"PRId64" us (%.2f fps)\r",
(double)frame_out * 1000000.0 / (double)dx_time);
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
struct ExternalFrameBuffer {
uint8_t* data;
size_t size;
int in_use;
};
struct ExternalFrameBufferList {
int num_external_frame_buffers;
struct ExternalFrameBuffer *ext_fb;
};
// Callback used by libvpx to request an external frame buffer. |cb_priv|
// Application private data passed into the set function. |min_size| is the
// minimum size in bytes needed to decode the next frame. |fb| pointer to the
// frame buffer.
int get_vp9_frame_buffer(void *cb_priv, size_t min_size,
vpx_codec_frame_buffer_t *fb) {
int i;
struct ExternalFrameBufferList *const ext_fb_list =
(struct ExternalFrameBufferList *)cb_priv;
if (ext_fb_list == NULL)
return -1;
// Find a free frame buffer.
for (i = 0; i < ext_fb_list->num_external_frame_buffers; ++i) {
if (!ext_fb_list->ext_fb[i].in_use)
break;
}
if (i == ext_fb_list->num_external_frame_buffers)
return -1;
if (ext_fb_list->ext_fb[i].size < min_size) {
free(ext_fb_list->ext_fb[i].data);
ext_fb_list->ext_fb[i].data = (uint8_t *)malloc(min_size);
if (!ext_fb_list->ext_fb[i].data)
return -1;
ext_fb_list->ext_fb[i].size = min_size;
}
fb->data = ext_fb_list->ext_fb[i].data;
fb->size = ext_fb_list->ext_fb[i].size;
ext_fb_list->ext_fb[i].in_use = 1;
// Set the frame buffer's private data to point at the external frame buffer.
fb->priv = &ext_fb_list->ext_fb[i];
return 0;
}
// Callback used by libvpx when there are no references to the frame buffer.
// |cb_priv| user private data passed into the set function. |fb| pointer
// to the frame buffer.
int release_vp9_frame_buffer(void *cb_priv,
vpx_codec_frame_buffer_t *fb) {
struct ExternalFrameBuffer *const ext_fb =
(struct ExternalFrameBuffer *)fb->priv;
(void)cb_priv;
ext_fb->in_use = 0;
return 0;
}
void generate_filename(const char *pattern, char *out, size_t q_len,
unsigned int d_w, unsigned int d_h,
unsigned int frame_in) {
const char *p = pattern;
char *q = out;
do {
char *next_pat = strchr(p, '%');
if (p == next_pat) {
size_t pat_len;
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
q[q_len - 1] = '\0';
switch (p[1]) {
case 'w':
snprintf(q, q_len - 1, "%d", d_w);
break;
case 'h':
snprintf(q, q_len - 1, "%d", d_h);
break;
case '1':
snprintf(q, q_len - 1, "%d", frame_in);
break;
case '2':
snprintf(q, q_len - 1, "%02d", frame_in);
break;
case '3':
snprintf(q, q_len - 1, "%03d", frame_in);
break;
case '4':
snprintf(q, q_len - 1, "%04d", frame_in);
break;
case '5':
snprintf(q, q_len - 1, "%05d", frame_in);
break;
case '6':
snprintf(q, q_len - 1, "%06d", frame_in);
break;
case '7':
snprintf(q, q_len - 1, "%07d", frame_in);
break;
case '8':
snprintf(q, q_len - 1, "%08d", frame_in);
break;
case '9':
snprintf(q, q_len - 1, "%09d", frame_in);
break;
default:
die("Unrecognized pattern %%%c\n", p[1]);
}
pat_len = strlen(q);
if (pat_len >= q_len - 1)
die("Output filename too long.\n");
q += pat_len;
p += 2;
q_len -= pat_len;
} else {
size_t copy_len;
if (!next_pat)
copy_len = strlen(p);
else
copy_len = next_pat - p;
if (copy_len >= q_len - 1)
die("Output filename too long.\n");
memcpy(q, p, copy_len);
q[copy_len] = '\0';
q += copy_len;
p += copy_len;
q_len -= copy_len;
}
} while (*p);
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
static int is_single_file(const char *outfile_pattern) {
const char *p = outfile_pattern;
do {
p = strchr(p, '%');
if (p && p[1] >= '1' && p[1] <= '9')
return 0; // pattern contains sequence number, so it's not unique
if (p)
p++;
} while (p);
return 1;
}
static void print_md5(unsigned char digest[16], const char *filename) {
int i;
for (i = 0; i < 16; ++i)
printf("%02x", digest[i]);
printf(" %s\n", filename);
}
static FILE *open_outfile(const char *name) {
if (strcmp("-", name) == 0) {
set_binary_mode(stdout);
return stdout;
} else {
FILE *file = fopen(name, "wb");
if (!file)
fatal("Failed to output file %s", name);
return file;
}
}
int main_loop(int argc, const char **argv_) {
size_t bytes_in_buffer = 0, buffer_size = 0;
int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0;
int do_md5 = 0, progress = 0;
const VpxInterface *interface = NULL;
const VpxInterface *fourcc_interface = NULL;
int single_file;
int use_y4m = 1;
vpx_codec_dec_cfg_t cfg = {0};
vp8_postproc_cfg_t vp8_pp_cfg = {0};
int vp8_dbg_color_ref_frame = 0;
int vp8_dbg_color_mb_modes = 0;
int vp8_dbg_color_b_modes = 0;
int vp8_dbg_display_mv = 0;
int num_external_frame_buffers = 0;
struct ExternalFrameBufferList ext_fb_list = {0};
const char *outfile_pattern = NULL;
char outfile_name[PATH_MAX] = {0};
FILE *outfile = NULL;
MD5Context md5_ctx;
unsigned char md5_digest[16];
struct VpxDecInputContext input = {0};
struct VpxInputContext vpx_input_ctx = {0};
struct WebmInputContext webm_ctx = {0};
input.webm_ctx = &webm_ctx;
#endif
input.vpx_input_ctx = &vpx_input_ctx;
/* Parse command line */
exec_name = argv_[0];
argv = argv_dup(argc - 1, argv_ + 1);
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
memset(&arg, 0, sizeof(arg));
arg.argv_step = 1;
if (arg_match(&arg, &codecarg, argi)) {
interface = get_vpx_decoder_by_name(arg.val);
if (!interface)
die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
} else if (arg_match(&arg, &looparg, argi)) {
// no-op
} else if (arg_match(&arg, &outputfile, argi))
outfile_pattern = arg.val;
else if (arg_match(&arg, &use_yv12, argi)) {
use_y4m = 0;
flipuv = 1;
} else if (arg_match(&arg, &use_i420, argi)) {
use_y4m = 0;
flipuv = 0;
} else if (arg_match(&arg, &flipuvarg, argi))
flipuv = 1;
else if (arg_match(&arg, &noblitarg, argi))
noblit = 1;
else if (arg_match(&arg, &progressarg, argi))
progress = 1;
else if (arg_match(&arg, &limitarg, argi))
stop_after = arg_parse_uint(&arg);
else if (arg_match(&arg, &skiparg, argi))
arg_skip = arg_parse_uint(&arg);
else if (arg_match(&arg, &postprocarg, argi))
postproc = 1;
else if (arg_match(&arg, &md5arg, argi))
do_md5 = 1;
else if (arg_match(&arg, &summaryarg, argi))
summary = 1;
else if (arg_match(&arg, &threadsarg, argi))
cfg.threads = arg_parse_uint(&arg);
else if (arg_match(&arg, &verbosearg, argi))
quiet = 0;
else if (arg_match(&arg, &scalearg, argi))
do_scale = 1;
else if (arg_match(&arg, &fb_arg, argi))
num_external_frame_buffers = arg_parse_uint(&arg);
else if (arg_match(&arg, &addnoise_level, argi)) {
postproc = 1;
vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE;
vp8_pp_cfg.noise_level = arg_parse_uint(&arg);
} else if (arg_match(&arg, &demacroblock_level, argi)) {
postproc = 1;
vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK;
vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg);
} else if (arg_match(&arg, &deblock, argi)) {
postproc = 1;
vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK;
} else if (arg_match(&arg, &mfqe, argi)) {
postproc = 1;
vp8_pp_cfg.post_proc_flag |= VP8_MFQE;
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
} else if (arg_match(&arg, &pp_debug_info, argi)) {
unsigned int level = arg_parse_uint(&arg);
postproc = 1;
vp8_pp_cfg.post_proc_flag &= ~0x7;
if (level)
vp8_pp_cfg.post_proc_flag |= level;
} else if (arg_match(&arg, &pp_disp_ref_frame, argi)) {
unsigned int flags = arg_parse_int(&arg);
if (flags) {
postproc = 1;
vp8_dbg_color_ref_frame = flags;
}
} else if (arg_match(&arg, &pp_disp_mb_modes, argi)) {
unsigned int flags = arg_parse_int(&arg);
if (flags) {
postproc = 1;
vp8_dbg_color_mb_modes = flags;
}
} else if (arg_match(&arg, &pp_disp_b_modes, argi)) {
unsigned int flags = arg_parse_int(&arg);
if (flags) {
postproc = 1;
vp8_dbg_color_b_modes = flags;
}
} else if (arg_match(&arg, &pp_disp_mvs, argi)) {
unsigned int flags = arg_parse_int(&arg);
if (flags) {
postproc = 1;
vp8_dbg_display_mv = flags;
}
} else if (arg_match(&arg, &error_concealment, argi)) {
ec_enabled = 1;
/* Check for unrecognized options */
for (argi = argv; *argi; argi++)
if (argi[0][0] == '-' && strlen(argi[0]) > 1)
die("Error: Unrecognized option %s\n", *argi);
/* Open file */
infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin);
fprintf(stderr, "Failed to open file '%s'", strcmp(fn, "-") ? fn : "stdin");
/* Make sure we don't dump to the terminal, unless forced to with -o - */
if (!outfile_pattern && isatty(fileno(stdout)) && !do_md5 && !noblit) {
fprintf(stderr,
"Not dumping raw video to your terminal. Use '-o -' to "
"override.\n");
return EXIT_FAILURE;
}
input.vpx_input_ctx->file = infile;
if (file_is_ivf(input.vpx_input_ctx))
input.vpx_input_ctx->file_type = FILE_TYPE_IVF;
#if CONFIG_WEBM_IO
else if (file_is_webm(input.webm_ctx, input.vpx_input_ctx))
input.vpx_input_ctx->file_type = FILE_TYPE_WEBM;
#endif
else if (file_is_raw(input.vpx_input_ctx))
input.vpx_input_ctx->file_type = FILE_TYPE_RAW;
else {
fprintf(stderr, "Unrecognized input file type.\n");
#if !CONFIG_WEBM_IO
fprintf(stderr, "vpxdec was built without WebM container support.\n");
#endif
return EXIT_FAILURE;
}
outfile_pattern = outfile_pattern ? outfile_pattern : "-";
single_file = is_single_file(outfile_pattern);
if (!noblit && single_file) {
generate_filename(outfile_pattern, outfile_name, PATH_MAX,
vpx_input_ctx.width, vpx_input_ctx.height, 0);
if (do_md5)
MD5Init(&md5_ctx);
else
outfile = open_outfile(outfile_name);
}
if (use_y4m && !noblit) {
if (!single_file) {
fprintf(stderr, "YUV4MPEG2 not supported with output patterns,"
" try --i420 or --yv12.\n");
return EXIT_FAILURE;
#if CONFIG_WEBM_IO
if (vpx_input_ctx.file_type == FILE_TYPE_WEBM) {
if (webm_guess_framerate(input.webm_ctx, input.vpx_input_ctx)) {
fprintf(stderr, "Failed to guess framerate -- error parsing "
"webm file?\n");
return EXIT_FAILURE;
}
#endif
fourcc_interface = get_vpx_decoder_by_fourcc(vpx_input_ctx.fourcc);
if (interface && fourcc_interface && interface != fourcc_interface)
warn("Header indicates codec: %s\n", fourcc_interface->name);
else
interface = fourcc_interface;
if (!interface)
interface = get_vpx_decoder_by_index(0);
dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) |
(ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0);
if (vpx_codec_dec_init(&decoder, interface->interface(), &cfg, dec_flags)) {
fprintf(stderr, "Failed to initialize decoder: %s\n",
vpx_codec_error(&decoder));
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
if (vp8_pp_cfg.post_proc_flag
&& vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) {
fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
if (vp8_dbg_color_ref_frame
&& vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_REF_FRAME, vp8_dbg_color_ref_frame)) {
fprintf(stderr, "Failed to configure reference block visualizer: %s\n", vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
if (vp8_dbg_color_mb_modes
&& vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_MB_MODES, vp8_dbg_color_mb_modes)) {
fprintf(stderr, "Failed to configure macro block visualizer: %s\n", vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
if (vp8_dbg_color_b_modes
&& vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_B_MODES, vp8_dbg_color_b_modes)) {
fprintf(stderr, "Failed to configure block visualizer: %s\n", vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
if (vp8_dbg_display_mv
&& vpx_codec_control(&decoder, VP8_SET_DBG_DISPLAY_MV, vp8_dbg_display_mv)) {
fprintf(stderr, "Failed to configure motion vector visualizer: %s\n", vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
#endif
fprintf(stderr, "Skipping first %d frames.\n", arg_skip);
if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size))
break;
arg_skip--;
}
if (num_external_frame_buffers > 0) {
ext_fb_list.num_external_frame_buffers = num_external_frame_buffers;
ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc(
num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb));
if (vpx_codec_set_frame_buffer_functions(
&decoder, get_vp9_frame_buffer, release_vp9_frame_buffer,
&ext_fb_list)) {
fprintf(stderr, "Failed to configure external frame buffers: %s\n",
vpx_codec_error(&decoder));
return EXIT_FAILURE;
}
}
vpx_codec_iter_t iter = NULL;
vpx_image_t *img;
struct vpx_usec_timer timer;
int corrupted;
frame_avail = 0;
if (!stop_after || frame_in < stop_after) {
if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) {
if (vpx_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer,
NULL, 0)) {
const char *detail = vpx_codec_error_detail(&decoder);
warn("Failed to decode frame %d: %s",
frame_in, vpx_codec_error(&decoder));
warn("Additional information: %s", detail);
goto fail;
}
vpx_usec_timer_mark(&timer);
dx_time += vpx_usec_timer_elapsed(&timer);
}
}
vpx_usec_timer_start(&timer);
got_data = 0;
if ((img = vpx_codec_get_frame(&decoder, &iter))) {
++frame_out;
got_data = 1;
dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer);
if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) {
warn("Failed VP8_GET_FRAME_CORRUPTED: %s", vpx_codec_error(&decoder));
if (progress)
show_progress(frame_in, frame_out, dx_time);
if (!noblit && img) {
const int PLANES_YUV[] = {VPX_PLANE_Y, VPX_PLANE_U, VPX_PLANE_V};
const int PLANES_YVU[] = {VPX_PLANE_Y, VPX_PLANE_V, VPX_PLANE_U};
const int *planes = flipuv ? PLANES_YVU : PLANES_YUV;
if (do_scale) {
if (frame_out == 1) {
// If the output frames are to be scaled to a fixed display size then
// use the width and height specified in the container. If either of
// these is set to 0, use the display size set in the first frame
// header. If that is unavailable, use the raw decoded size of the
// first decoded frame.
int display_width = vpx_input_ctx.width;
int display_height = vpx_input_ctx.height;
if (!display_width || !display_height) {
int display_size[2];
if (vpx_codec_control(&decoder, VP9D_GET_DISPLAY_SIZE,
display_size)) {
// As last resort use size of first frame as display size.
display_width = img->d_w;
display_height = img->d_h;
} else {
display_width = display_size[0];
display_height = display_size[1];
}
scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, display_width,
display_height, 16);
if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) {
vpx_image_scale(img, scaled_img, kFilterBox);
#else
fprintf(stderr, "Failed to scale output frame: %s.\n"
"Scaling is disabled in this configuration. "
"To enable scaling, configure with --enable-libyuv\n",
vpx_codec_error(&decoder));
return EXIT_FAILURE;
#endif
if (single_file) {
if (use_y4m) {
char buf[Y4M_BUFFER_SIZE] = {0};
size_t len = 0;
if (frame_out == 1) {
// Y4M file header
len = y4m_write_file_header(buf, sizeof(buf),
vpx_input_ctx.width,
vpx_input_ctx.height,
&vpx_input_ctx.framerate, img->fmt);
if (do_md5) {
MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
} else {
fputs(buf, outfile);
}
}
// Y4M frame header
len = y4m_write_frame_header(buf, sizeof(buf));
if (do_md5) {
MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
} else {
fputs(buf, outfile);
}
}
if (do_md5) {
update_image_md5(img, planes, &md5_ctx);
write_image_file(img, planes, outfile);
}
} else {
generate_filename(outfile_pattern, outfile_name, PATH_MAX,
img->d_w, img->d_h, frame_in);
if (do_md5) {
MD5Init(&md5_ctx);
update_image_md5(img, planes, &md5_ctx);
MD5Final(md5_digest, &md5_ctx);
print_md5(md5_digest, outfile_name);
} else {
outfile = open_outfile(outfile_name);
write_image_file(img, planes, outfile);
fclose(outfile);
if (summary || progress) {
show_progress(frame_in, frame_out, dx_time);
fprintf(stderr, "\n");
}
if (frames_corrupted)
fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted);
fprintf(stderr, "Failed to destroy decoder: %s\n",
vpx_codec_error(&decoder));
if (!noblit && single_file) {
if (do_md5) {
MD5Final(md5_digest, &md5_ctx);
print_md5(md5_digest, outfile_name);
} else {
fclose(outfile);
}
}
#if CONFIG_WEBM_IO
if (input.vpx_input_ctx->file_type == FILE_TYPE_WEBM)
webm_free(input.webm_ctx);
#endif
if (input.vpx_input_ctx->file_type != FILE_TYPE_WEBM)
if (scaled_img) vpx_img_free(scaled_img);
for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) {
free(ext_fb_list.ext_fb[i].data);
}
free(ext_fb_list.ext_fb);
int main(int argc, const char **argv_) {
unsigned int loops = 1, i;
char **argv, **argi, **argj;
struct arg arg;
int error = 0;
argv = argv_dup(argc - 1, argv_ + 1);
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
memset(&arg, 0, sizeof(arg));
arg.argv_step = 1;
if (arg_match(&arg, &looparg, argi)) {
loops = arg_parse_uint(&arg);
break;
}