vpxdec.c 41.5 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1
/*
2
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
John Koleszar's avatar
John Koleszar committed
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5 6
 *  that can be found in the LICENSE file in the root of the source
 *  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.
John Koleszar's avatar
John Koleszar committed
9 10
 */

11
#include <assert.h>
John Koleszar's avatar
John Koleszar committed
12 13 14 15
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
16
#include <limits.h>
17

18 19 20
#include "./vpx_config.h"

#if CONFIG_LIBYUV
21
#include "third_party/libyuv/include/libyuv/scale.h"
22
#endif
23 24

#include "./args.h"
25 26
#include "./ivfdec.h"

John Koleszar's avatar
John Koleszar committed
27
#define VPX_CODEC_DISABLE_COMPAT 1
28
#include "vpx/vpx_decoder.h"
29
#include "vpx_ports/mem_ops.h"
John Koleszar's avatar
John Koleszar committed
30
#include "vpx_ports/vpx_timer.h"
31

32
#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
33
#include "vpx/vp8dx.h"
John Koleszar's avatar
John Koleszar committed
34
#endif
35

36
#include "./md5_utils.h"
37 38

#include "./tools_common.h"
39
#if CONFIG_WEBM_IO
40
#include "./webmdec.h"
41
#endif
42
#include "./y4menc.h"
John Koleszar's avatar
John Koleszar committed
43 44 45

static const char *exec_name;

46 47 48 49 50
struct VpxDecInputContext {
  struct VpxInputContext *vpx_input_ctx;
  struct WebmInputContext *webm_ctx;
};

51 52
static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1,
                                          "Number of times to decode the file");
John Koleszar's avatar
John Koleszar committed
53
static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
John Koleszar's avatar
John Koleszar committed
54
                                          "Codec to use");
John Koleszar's avatar
John Koleszar committed
55
static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
John Koleszar's avatar
John Koleszar committed
56
                                          "Output raw YV12 frames");
John Koleszar's avatar
John Koleszar committed
57
static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
John Koleszar's avatar
John Koleszar committed
58
                                          "Output raw I420 frames");
John Koleszar's avatar
John Koleszar committed
59
static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
John Koleszar's avatar
John Koleszar committed
60
                                           "Flip the chroma planes in the output");
61 62
static const arg_def_t rawvideo = ARG_DEF(NULL, "rawvideo", 0,
                                          "Output raw YUV frames");
John Koleszar's avatar
John Koleszar committed
63
static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
John Koleszar's avatar
John Koleszar committed
64
                                           "Don't process the decoded frames");
John Koleszar's avatar
John Koleszar committed
65
static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
John Koleszar's avatar
John Koleszar committed
66
                                             "Show progress after each frame decodes");
John Koleszar's avatar
John Koleszar committed
67
static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
John Koleszar's avatar
John Koleszar committed
68
                                          "Stop decoding after n frames");
69 70
static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1,
                                         "Skip the first n input frames");
John Koleszar's avatar
John Koleszar committed
71
static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
John Koleszar's avatar
John Koleszar committed
72
                                             "Postprocess decoded frames");
John Koleszar's avatar
John Koleszar committed
73
static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
John Koleszar's avatar
John Koleszar committed
74
                                            "Show timing summary");
75
static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
John Koleszar's avatar
John Koleszar committed
76
                                            "Output file name pattern (see below)");
John Koleszar's avatar
John Koleszar committed
77
static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
John Koleszar's avatar
John Koleszar committed
78
                                            "Max threads to use");
79
static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
John Koleszar's avatar
John Koleszar committed
80
                                            "Show version string");
81 82
static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0,
                                                   "Enable decoder error-concealment");
83 84
static const arg_def_t scalearg = ARG_DEF("S", "scale", 0,
                                            "Scale output frames uniformly");
85 86
static const arg_def_t continuearg =
    ARG_DEF("k", "keep-going", 0, "(debug) Continue decoding after error");
87

88 89
static const arg_def_t fb_arg =
    ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use");
John Koleszar's avatar
John Koleszar committed
90 91 92

static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
                                        "Compute the MD5 sum of the decoded frame");
93 94 95 96 97
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
static const arg_def_t outbitdeptharg = ARG_DEF(
    NULL, "output-bit-depth", 1,
    "Output bit-depth for decoded frames");
#endif
Dmitry Kovalev's avatar
Dmitry Kovalev committed
98

John Koleszar's avatar
John Koleszar committed
99
static const arg_def_t *all_args[] = {
100
  &codecarg, &use_yv12, &use_i420, &flipuvarg, &rawvideo, &noblitarg,
101
  &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
102
  &threadsarg, &verbosearg, &scalearg, &fb_arg,
103
  &md5arg, &error_concealment, &continuearg,
104 105 106
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  &outbitdeptharg,
#endif
John Koleszar's avatar
John Koleszar committed
107
  NULL
John Koleszar's avatar
John Koleszar committed
108 109
};

110
#if CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
111
static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
John Koleszar's avatar
John Koleszar committed
112
                                                "Enable VP8 postproc add noise");
John Koleszar's avatar
John Koleszar committed
113
static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
John Koleszar's avatar
John Koleszar committed
114
                                         "Enable VP8 deblocking");
John Koleszar's avatar
John Koleszar committed
115
static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
John Koleszar's avatar
John Koleszar committed
116
                                                    "Enable VP8 demacroblocking, w/ level");
John Koleszar's avatar
John Koleszar committed
117
static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
John Koleszar's avatar
John Koleszar committed
118
                                               "Enable VP8 visible debug info");
119
static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1,
John Koleszar's avatar
John Koleszar committed
120
                                                   "Display only selected reference frame per macro block");
121
static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1,
John Koleszar's avatar
John Koleszar committed
122
                                                  "Display only selected macro block modes");
123
static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1,
John Koleszar's avatar
John Koleszar committed
124
                                                 "Display only selected block modes");
125
static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1,
John Koleszar's avatar
John Koleszar committed
126
                                             "Draw only selected motion vectors");
127 128
static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0,
                                      "Enable multiframe quality enhancement");
John Koleszar's avatar
John Koleszar committed
129

John Koleszar's avatar
John Koleszar committed
130 131
static const arg_def_t *vp8_pp_args[] = {
  &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
132
  &pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe,
John Koleszar's avatar
John Koleszar committed
133
  NULL
John Koleszar's avatar
John Koleszar committed
134 135 136
};
#endif

137
#if CONFIG_LIBYUV
138 139
static INLINE int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst,
                                  FilterModeEnum mode) {
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  if (src->fmt == VPX_IMG_FMT_I42016) {
    assert(dst->fmt == VPX_IMG_FMT_I42016);
    return I420Scale_16((uint16_t*)src->planes[VPX_PLANE_Y],
                        src->stride[VPX_PLANE_Y]/2,
                        (uint16_t*)src->planes[VPX_PLANE_U],
                        src->stride[VPX_PLANE_U]/2,
                        (uint16_t*)src->planes[VPX_PLANE_V],
                        src->stride[VPX_PLANE_V]/2,
                        src->d_w, src->d_h,
                        (uint16_t*)dst->planes[VPX_PLANE_Y],
                        dst->stride[VPX_PLANE_Y]/2,
                        (uint16_t*)dst->planes[VPX_PLANE_U],
                        dst->stride[VPX_PLANE_U]/2,
                        (uint16_t*)dst->planes[VPX_PLANE_V],
                        dst->stride[VPX_PLANE_V]/2,
                        dst->d_w, dst->d_h,
                        mode);
  }
#endif
160 161 162 163 164 165 166 167 168 169 170 171
  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);
}
172
#endif
173

174
void usage_exit() {
John Koleszar's avatar
John Koleszar committed
175
  int i;
John Koleszar's avatar
John Koleszar committed
176

John Koleszar's avatar
John Koleszar committed
177 178 179
  fprintf(stderr, "Usage: %s <options> filename\n\n"
          "Options:\n", exec_name);
  arg_show_usage(stderr, all_args);
180
#if CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
181 182
  fprintf(stderr, "\nVP8 Postprocessing Options:\n");
  arg_show_usage(stderr, vp8_pp_args);
John Koleszar's avatar
John Koleszar committed
183
#endif
John Koleszar's avatar
John Koleszar committed
184 185 186 187 188 189
  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 "
190 191
          "the following escape characters:\n");
  fprintf(stderr,
John Koleszar's avatar
John Koleszar committed
192 193 194 195 196 197 198 199 200
          "\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");

201 202
  for (i = 0; i < get_vpx_decoder_count(); ++i) {
    const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
John Koleszar's avatar
John Koleszar committed
203
    fprintf(stderr, "    %-6s - %s\n",
204
            decoder->name, vpx_codec_iface_name(decoder->codec_interface()));
205
  }
John Koleszar's avatar
John Koleszar committed
206 207

  exit(EXIT_FAILURE);
John Koleszar's avatar
John Koleszar committed
208 209
}

210
static int raw_read_frame(FILE *infile, uint8_t **buffer,
211
                          size_t *bytes_read, size_t *buffer_size) {
212
  char raw_hdr[RAW_FRAME_HDR_SZ];
213
  size_t frame_size = 0;
John Koleszar's avatar
John Koleszar committed
214

215 216 217 218
  if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) {
    if (!feof(infile))
      warn("Failed to read RAW frame size\n");
  } else {
219 220
    const size_t kCorruptFrameThreshold = 256 * 1024 * 1024;
    const size_t kFrameTooSmallThreshold = 256 * 1024;
221
    frame_size = mem_get_le32(raw_hdr);
John Koleszar's avatar
John Koleszar committed
222

223 224 225 226
    if (frame_size > kCorruptFrameThreshold) {
      warn("Read invalid frame size (%u)\n", (unsigned int)frame_size);
      frame_size = 0;
    }
John Koleszar's avatar
John Koleszar committed
227

228 229 230
    if (frame_size < kFrameTooSmallThreshold) {
      warn("Warning: Read invalid frame size (%u) - not a raw file?\n",
           (unsigned int)frame_size);
John Koleszar's avatar
John Koleszar committed
231
    }
John Koleszar's avatar
John Koleszar committed
232

233 234 235 236 237 238 239 240
    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;
241
      }
John Koleszar's avatar
John Koleszar committed
242
    }
243
  }
John Koleszar's avatar
John Koleszar committed
244

245 246 247 248 249 250
  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;
John Koleszar's avatar
John Koleszar committed
251 252
  }

253 254 255 256 257 258
  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) {
259
#if CONFIG_WEBM_IO
260 261 262
    case FILE_TYPE_WEBM:
      return webm_read_frame(input->webm_ctx,
                             buf, bytes_in_buffer, buffer_size);
263
#endif
264
    case FILE_TYPE_RAW:
265
      return raw_read_frame(input->vpx_input_ctx->file,
266 267
                            buf, bytes_in_buffer, buffer_size);
    case FILE_TYPE_IVF:
268
      return ivf_read_frame(input->vpx_input_ctx->file,
269 270 271 272
                            buf, bytes_in_buffer, buffer_size);
    default:
      return 1;
  }
John Koleszar's avatar
John Koleszar committed
273 274
}

275 276 277 278 279 280 281 282
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];
283 284
    const int w = vpx_img_plane_width(img, plane);
    const int h = vpx_img_plane_height(img, plane);
285 286 287 288 289 290 291 292 293 294 295

    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;
296 297 298 299 300
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  const int bytes_per_sample = ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
#else
  const int bytes_per_sample = 1;
#endif
301 302 303 304 305

  for (i = 0; i < 3; ++i) {
    const int plane = planes[i];
    const unsigned char *buf = img->planes[plane];
    const int stride = img->stride[plane];
306 307
    const int w = vpx_img_plane_width(img, plane);
    const int h = vpx_img_plane_height(img, plane);
308 309

    for (y = 0; y < h; ++y) {
310
      fwrite(buf, bytes_per_sample, w, file);
311 312
      buf += stride;
    }
John Koleszar's avatar
John Koleszar committed
313
  }
John Koleszar's avatar
John Koleszar committed
314 315
}

316 317
int file_is_raw(struct VpxInputContext *input) {
  uint8_t buf[32];
John Koleszar's avatar
John Koleszar committed
318 319
  int is_raw = 0;
  vpx_codec_stream_info_t si;
320

John Koleszar's avatar
John Koleszar committed
321 322
  si.sz = sizeof(si);

323
  if (fread(buf, 1, 32, input->file) == 32) {
John Koleszar's avatar
John Koleszar committed
324 325
    int i;

326
    if (mem_get_le32(buf) < 256 * 1024 * 1024) {
327 328
      for (i = 0; i < get_vpx_decoder_count(); ++i) {
        const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
329
        if (!vpx_codec_peek_stream_info(decoder->codec_interface(),
John Koleszar's avatar
John Koleszar committed
330 331
                                        buf + 4, 32 - 4, &si)) {
          is_raw = 1;
332
          input->fourcc = decoder->fourcc;
333 334 335 336
          input->width = si.w;
          input->height = si.h;
          input->framerate.numerator = 30;
          input->framerate.denominator = 1;
John Koleszar's avatar
John Koleszar committed
337 338
          break;
        }
339 340
      }
    }
John Koleszar's avatar
John Koleszar committed
341 342
  }

343
  rewind(input->file);
John Koleszar's avatar
John Koleszar committed
344
  return is_raw;
345 346
}

347 348 349
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",
John Koleszar's avatar
John Koleszar committed
350
          frame_in, frame_out, dx_time,
351
          (double)frame_out * 1000000.0 / (double)dx_time);
352 353
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 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
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;
}

416 417
void generate_filename(const char *pattern, char *out, size_t q_len,
                       unsigned int d_w, unsigned int d_h,
John Koleszar's avatar
John Koleszar committed
418 419 420 421 422 423 424 425 426 427
                       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;

428
      /* parse the pattern */
John Koleszar's avatar
John Koleszar committed
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
      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]);
466
          break;
John Koleszar's avatar
John Koleszar committed
467 468 469 470 471 472 473 474 475 476 477
      }

      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;

478
      /* copy the next segment */
John Koleszar's avatar
John Koleszar committed
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
      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);
494 495
}

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
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;
  }
}

530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 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 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
static void high_img_upshift(vpx_image_t *dst, vpx_image_t *src,
                             int input_shift) {
  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
  int plane;
  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
      dst->x_chroma_shift != src->x_chroma_shift ||
      dst->y_chroma_shift != src->y_chroma_shift ||
      dst->fmt != src->fmt || input_shift < 0) {
    fatal("Unsupported image conversion");
  }
  switch (src->fmt) {
    case VPX_IMG_FMT_I42016:
    case VPX_IMG_FMT_I42216:
    case VPX_IMG_FMT_I44416:
      break;
    default:
      fatal("Unsupported image conversion");
      break;
  }
  for (plane = 0; plane < 3; plane++) {
    int w = src->d_w;
    int h = src->d_h;
    int x, y;
    if (plane) {
      w >>= src->x_chroma_shift;
      h >>= src->y_chroma_shift;
    }
    for (y = 0; y < h; y++) {
      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
                                     y * src->stride[plane]);
      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
                                     y * dst->stride[plane]);
      for (x = 0; x < w; x++)
        *p_dst++ = (*p_src++ << input_shift) + offset;
    }
  }
}

static void low_img_upshift(vpx_image_t *dst, vpx_image_t *src,
                            int input_shift) {
  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
  int plane;
  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
      dst->x_chroma_shift != src->x_chroma_shift ||
      dst->y_chroma_shift != src->y_chroma_shift ||
      dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
      input_shift < 0) {
    fatal("Unsupported image conversion");
  }
  switch (src->fmt) {
    case VPX_IMG_FMT_I420:
    case VPX_IMG_FMT_I422:
    case VPX_IMG_FMT_I444:
      break;
    default:
      fatal("Unsupported image conversion");
      break;
  }
  for (plane = 0; plane < 3; plane++) {
    int w = src->d_w;
    int h = src->d_h;
    int x, y;
    if (plane) {
      w >>= src->x_chroma_shift;
      h >>= src->y_chroma_shift;
    }
    for (y = 0; y < h; y++) {
      uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
                                     y * dst->stride[plane]);
      for (x = 0; x < w; x++) {
        *p_dst++ = (*p_src++ << input_shift) + offset;
      }
    }
  }
}

static void img_upshift(vpx_image_t *dst, vpx_image_t *src,
                        int input_shift) {
  if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
    high_img_upshift(dst, src, input_shift);
  } else {
    low_img_upshift(dst, src, input_shift);
  }
}

static void high_img_downshift(vpx_image_t *dst, vpx_image_t *src,
                               int down_shift) {
  int plane;
  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
      dst->x_chroma_shift != src->x_chroma_shift ||
      dst->y_chroma_shift != src->y_chroma_shift ||
      dst->fmt != src->fmt || down_shift < 0) {
    fatal("Unsupported image conversion");
  }
  switch (src->fmt) {
    case VPX_IMG_FMT_I42016:
    case VPX_IMG_FMT_I42216:
    case VPX_IMG_FMT_I44416:
      break;
    default:
      fatal("Unsupported image conversion");
      break;
  }
  for (plane = 0; plane < 3; plane++) {
    int w = src->d_w;
    int h = src->d_h;
    int x, y;
    if (plane) {
      w >>= src->x_chroma_shift;
      h >>= src->y_chroma_shift;
    }
    for (y = 0; y < h; y++) {
      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
                                     y * src->stride[plane]);
      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
                                     y * dst->stride[plane]);
      for (x = 0; x < w; x++)
        *p_dst++ = *p_src++ >> down_shift;
    }
  }
}

static void low_img_downshift(vpx_image_t *dst, vpx_image_t *src,
                            int down_shift) {
  int plane;
  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
      dst->x_chroma_shift != src->x_chroma_shift ||
      dst->y_chroma_shift != src->y_chroma_shift ||
      src->fmt != dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
      down_shift < 0) {
    fatal("Unsupported image conversion");
  }
  switch (dst->fmt) {
    case VPX_IMG_FMT_I420:
    case VPX_IMG_FMT_I422:
    case VPX_IMG_FMT_I444:
      break;
    default:
      fatal("Unsupported image conversion");
      break;
  }
  for (plane = 0; plane < 3; plane++) {
    int w = src->d_w;
    int h = src->d_h;
    int x, y;
    if (plane) {
      w >>= src->x_chroma_shift;
      h >>= src->y_chroma_shift;
    }
    for (y = 0; y < h; y++) {
      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
                                     y * src->stride[plane]);
      uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
      for (x = 0; x < w; x++) {
        *p_dst++ = *p_src++ >> down_shift;
      }
    }
  }
}

static void img_downshift(vpx_image_t *dst, vpx_image_t *src,
                          int down_shift) {
  if (dst->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
    high_img_downshift(dst, src, down_shift);
  } else {
    low_img_downshift(dst, src, down_shift);
  }
}
#endif

702
int main_loop(int argc, const char **argv_) {
703
  vpx_codec_ctx_t       decoder;
John Koleszar's avatar
John Koleszar committed
704
  char                  *fn = NULL;
705
  int                    i;
John Koleszar's avatar
John Koleszar committed
706
  uint8_t               *buf = NULL;
707
  size_t                 bytes_in_buffer = 0, buffer_size = 0;
John Koleszar's avatar
John Koleszar committed
708
  FILE                  *infile;
709 710
  int                    frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0;
  int                    do_md5 = 0, progress = 0;
John Koleszar's avatar
John Koleszar committed
711
  int                    stop_after = 0, postproc = 0, summary = 0, quiet = 1;
712
  int                    arg_skip = 0;
713
  int                    ec_enabled = 0;
714
  int                    keep_going = 0;
715 716
  const VpxInterface *interface = NULL;
  const VpxInterface *fourcc_interface = NULL;
717
  uint64_t dx_time = 0;
John Koleszar's avatar
John Koleszar committed
718 719
  struct arg               arg;
  char                   **argv, **argi, **argj;
720

John Koleszar's avatar
John Koleszar committed
721 722
  int                     single_file;
  int                     use_y4m = 1;
723 724
  int                     opt_yv12 = 0;
  int                     opt_i420 = 0;
725
  vpx_codec_dec_cfg_t     cfg = {0, 0, 0};
726 727 728
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  int                     output_bit_depth = 0;
#endif
729
#if CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
730 731 732 733 734
  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;
John Koleszar's avatar
John Koleszar committed
735
#endif
John Koleszar's avatar
John Koleszar committed
736 737
  int                     frames_corrupted = 0;
  int                     dec_flags = 0;
738
  int                     do_scale = 0;
739
  vpx_image_t             *scaled_img = NULL;
740 741 742
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  vpx_image_t             *img_shifted = NULL;
#endif
Scott LaVarnway's avatar
Scott LaVarnway committed
743
  int                     frame_avail, got_data;
744
  int                     num_external_frame_buffers = 0;
745
  struct ExternalFrameBufferList ext_fb_list = {0, NULL};
John Koleszar's avatar
John Koleszar committed
746

747 748 749 750 751 752 753
  const char *outfile_pattern = NULL;
  char outfile_name[PATH_MAX] = {0};
  FILE *outfile = NULL;

  MD5Context md5_ctx;
  unsigned char md5_digest[16];

754 755
  struct VpxDecInputContext input = {NULL, NULL};
  struct VpxInputContext vpx_input_ctx;
756
#if CONFIG_WEBM_IO
757 758
  struct WebmInputContext webm_ctx;
  memset(&(webm_ctx), 0, sizeof(webm_ctx));
759
  input.webm_ctx = &webm_ctx;
760 761
#endif
  input.vpx_input_ctx = &vpx_input_ctx;
762

John Koleszar's avatar
John Koleszar committed
763 764 765 766 767 768 769 770 771
  /* 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)) {
772 773 774
      interface = get_vpx_decoder_by_name(arg.val);
      if (!interface)
        die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
775 776
    } else if (arg_match(&arg, &looparg, argi)) {
      // no-op
John Koleszar's avatar
John Koleszar committed
777 778 779 780 781
    } else if (arg_match(&arg, &outputfile, argi))
      outfile_pattern = arg.val;
    else if (arg_match(&arg, &use_yv12, argi)) {
      use_y4m = 0;
      flipuv = 1;
782
      opt_yv12 = 1;
783 784 785
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
      output_bit_depth = 8;  // For yv12 8-bit depth output is assumed
#endif
John Koleszar's avatar
John Koleszar committed
786 787 788
    } else if (arg_match(&arg, &use_i420, argi)) {
      use_y4m = 0;
      flipuv = 0;
789 790 791
      opt_i420 = 1;
    } else if (arg_match(&arg, &rawvideo, argi)) {
      use_y4m = 0;
John Koleszar's avatar
John Koleszar committed
792 793 794 795 796 797 798 799
    } 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);
800 801
    else if (arg_match(&arg, &skiparg, argi))
      arg_skip = arg_parse_uint(&arg);
John Koleszar's avatar
John Koleszar committed
802 803 804 805 806 807 808 809 810 811
    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;
812 813
    else if (arg_match(&arg, &scalearg, argi))
      do_scale = 1;
814 815
    else if (arg_match(&arg, &fb_arg, argi))
      num_external_frame_buffers = arg_parse_uint(&arg);
816 817
    else if (arg_match(&arg, &continuearg, argi))
      keep_going = 1;
818 819 820 821 822
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
    else if (arg_match(&arg, &outbitdeptharg, argi)) {
      output_bit_depth = arg_parse_uint(&arg);
    }
#endif
823
#if CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
824 825 826 827 828 829 830 831 832 833 834
    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;
835 836 837
    } else if (arg_match(&arg, &mfqe, argi)) {
      postproc = 1;
      vp8_pp_cfg.post_proc_flag |= VP8_MFQE;
John Koleszar's avatar
John Koleszar committed
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    } 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;
      }
870 871
    } else if (arg_match(&arg, &error_concealment, argi)) {
      ec_enabled = 1;
John Koleszar's avatar
John Koleszar committed
872
    }
873
#endif  // CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
874 875 876
    else
      argj++;
  }
John Koleszar's avatar
John Koleszar committed
877

John Koleszar's avatar
John Koleszar committed
878 879 880 881
  /* Check for unrecognized options */
  for (argi = argv; *argi; argi++)
    if (argi[0][0] == '-' && strlen(argi[0]) > 1)
      die("Error: Unrecognized option %s\n", *argi);
John Koleszar's avatar
John Koleszar committed
882

John Koleszar's avatar
John Koleszar committed
883 884
  /* Handle non-option arguments */
  fn = argv[0];
John Koleszar's avatar
John Koleszar committed
885

John Koleszar's avatar
John Koleszar committed
886 887
  if (!fn)
    usage_exit();
John Koleszar's avatar
John Koleszar committed
888

John Koleszar's avatar
John Koleszar committed
889 890
  /* Open file */
  infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin);
John Koleszar's avatar
John Koleszar committed
891

John Koleszar's avatar
John Koleszar committed
892
  if (!infile) {
893
    fprintf(stderr, "Failed to open file '%s'", strcmp(fn, "-") ? fn : "stdin");
John Koleszar's avatar
John Koleszar committed
894 895
    return EXIT_FAILURE;
  }
896
#if CONFIG_OS_SUPPORT
John Koleszar's avatar
John Koleszar committed
897 898 899 900 901 902 903
  /* 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;
  }
904
#endif
905 906 907
  input.vpx_input_ctx->file = infile;
  if (file_is_ivf(input.vpx_input_ctx))
    input.vpx_input_ctx->file_type = FILE_TYPE_IVF;
908
#if CONFIG_WEBM_IO
909
  else if (file_is_webm(input.webm_ctx, input.vpx_input_ctx))
910
    input.vpx_input_ctx->file_type = FILE_TYPE_WEBM;
911
#endif
912 913
  else if (file_is_raw(input.vpx_input_ctx))
    input.vpx_input_ctx->file_type = FILE_TYPE_RAW;
John Koleszar's avatar
John Koleszar committed
914 915
  else {
    fprintf(stderr, "Unrecognized input file type.\n");
916 917 918
#if !CONFIG_WEBM_IO
    fprintf(stderr, "vpxdec was built without WebM container support.\n");
#endif
John Koleszar's avatar
John Koleszar committed
919 920 921 922
    return EXIT_FAILURE;
  }

  outfile_pattern = outfile_pattern ? outfile_pattern : "-";
923
  single_file = is_single_file(outfile_pattern);
John Koleszar's avatar
John Koleszar committed
924

925 926
  if (!noblit && single_file) {
    generate_filename(outfile_pattern, outfile_name, PATH_MAX,
927
                      vpx_input_ctx.width, vpx_input_ctx.height, 0);
928 929 930 931
    if (do_md5)
      MD5Init(&md5_ctx);
    else
      outfile = open_outfile(outfile_name);
John Koleszar's avatar
John Koleszar committed
932 933 934 935 936 937 938
  }

  if (use_y4m && !noblit) {
    if (!single_file) {
      fprintf(stderr, "YUV4MPEG2 not supported with output patterns,"
              " try --i420 or --yv12.\n");
      return EXIT_FAILURE;
939
    }
John Koleszar's avatar
John Koleszar committed
940

941
#if CONFIG_WEBM_IO
942
    if (vpx_input_ctx.file_type == FILE_TYPE_WEBM) {
943
      if (webm_guess_framerate(input.webm_ctx, input.vpx_input_ctx)) {
John Koleszar's avatar
John Koleszar committed
944 945 946 947
        fprintf(stderr, "Failed to guess framerate -- error parsing "
                "webm file?\n");
        return EXIT_FAILURE;
      }
948
    }
949
#endif
John Koleszar's avatar
John Koleszar committed
950 951
  }

952 953 954 955 956
  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;
957

958 959
  if (!interface)
    interface = get_vpx_decoder_by_index(0);
960

961 962
  dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) |
              (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0);
963 964
  if (vpx_codec_dec_init(&decoder, interface->codec_interface(),
                         &cfg, dec_flags)) {
965 966
    fprintf(stderr, "Failed to initialize decoder: %s\n",
            vpx_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
967 968
    return EXIT_FAILURE;
  }
John Koleszar's avatar
John Koleszar committed
969

John Koleszar's avatar
John Koleszar committed
970 971
  if (!quiet)
    fprintf(stderr, "%s\n", decoder.name);
John Koleszar's avatar
John Koleszar committed
972

973
#if CONFIG_VP8_DECODER
John Koleszar's avatar
John Koleszar committed
974

John Koleszar's avatar
John Koleszar committed
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
  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
John Koleszar's avatar
John Koleszar committed
1005

1006

1007
  if (arg_skip)
1008
    fprintf(stderr, "Skipping first %d frames.\n", arg_skip);
1009
  while (arg_skip) {
1010
    if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size))
1011 1012 1013 1014
      break;
    arg_skip--;
  }

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
  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;
    }
  }

Scott LaVarnway's avatar
Scott LaVarnway committed
1028 1029 1030
  frame_avail = 1;
  got_data = 0;

John Koleszar's avatar
John Koleszar committed
1031
  /* Decode file */
Scott LaVarnway's avatar
Scott LaVarnway committed
1032
  while (frame_avail || got_data) {
John Koleszar's avatar
John Koleszar committed
1033 1034 1035 1036
    vpx_codec_iter_t  iter = NULL;
    vpx_image_t    *img;
    struct vpx_usec_timer timer;
    int                   corrupted;
John Koleszar's avatar
John Koleszar committed
1037

Scott LaVarnway's avatar
Scott LaVarnway committed
1038 1039
    frame_avail = 0;
    if (!stop_after || frame_in < stop_after) {
1040
      if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) {
Scott LaVarnway's avatar
Scott LaVarnway committed
1041 1042
        frame_avail = 1;
        frame_in++;
John Koleszar's avatar
John Koleszar committed
1043

Scott LaVarnway's avatar
Scott LaVarnway committed
1044
        vpx_usec_timer_start(&timer);
John Koleszar's avatar
John Koleszar committed
1045

1046 1047
        if (vpx_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer,
                             NULL, 0)) {
Scott LaVarnway's avatar
Scott LaVarnway committed
1048
          const char *detail = vpx_codec_error_detail(&decoder);
1049 1050
          warn("Failed to decode frame %d: %s",
               frame_in, vpx_codec_error(&decoder));
1051

Scott LaVarnway's avatar
Scott LaVarnway committed
1052
          if (detail)
1053
            warn("Additional information: %s", detail);
1054 1055
          if (!keep_going)
            goto fail;
Scott LaVarnway's avatar
Scott LaVarnway committed
1056 1057 1058
        }

        vpx_usec_timer_mark(&timer);
1059
        dx_time += vpx_usec_timer_elapsed(&timer);
Scott LaVarnway's avatar
Scott LaVarnway committed
1060 1061 1062 1063 1064 1065 1066 1067 1068
      }
    }

    vpx_usec_timer_start(&timer);

    got_data = 0;
    if ((img = vpx_codec_get_frame(&decoder, &iter))) {
      ++frame_out;
      got_data = 1;
1069 1070
    }

John Koleszar's avatar
John Koleszar committed
1071
    vpx_usec_timer_mark(&timer);
1072
    dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer);
1073

John Koleszar's avatar
John Koleszar committed
1074
    if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) {
1075
      warn("Failed VP8_GET_FRAME_CORRUPTED: %s", vpx_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
1076
      goto fail;
1077
    }
John Koleszar's avatar
John Koleszar committed
1078
    frames_corrupted += corrupted;
John Koleszar's avatar
John Koleszar committed
1079

John Koleszar's avatar
John Koleszar committed
1080 1081
    if (progress)
      show_progress(frame_in, frame_out, dx_time);
John Koleszar's avatar
John Koleszar committed
1082

1083 1084 1085 1086
    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;
1087

1088
      if (do_scale) {
1089 1090 1091 1092
        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
1093 1094
          // header. If that is unavailable, use the raw decoded size of the
          // first decoded frame.
1095 1096
          int display_width = vpx_input_ctx.width;
          int display_height = vpx_input_ctx.height;
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
          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];
            }
1108
          }
1109
          scaled_img = vpx_img_alloc(NULL, img->fmt, display_width,
1110
                                     display_height, 16);
1111
          scaled_img->bit_depth = img->bit_depth;
1112
        }
1113

1114
        if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) {
1115
#if CONFIG_LIBYUV
1116
          vpx_image_scale(img, scaled_img, kFilterBox);
1117
          img = scaled_img;
1118 1119 1120 1121 1122 1123 1124
#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
1125 1126
        }
      }
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
      // Default to codec bit depth if output bit depth not set
      if (!output_bit_depth) {
        output_bit_depth = img->bit_depth;
      }
      // Shift up or down if necessary
      if (output_bit_depth != img->bit_depth) {
        if (!img_shifted) {
          if (output_bit_depth == 8) {
            img_shifted = vpx_img_alloc(
                NULL, img->fmt - VPX_IMG_FMT_HIGHBITDEPTH,
                img->d_w, img->d_h, 16);
          } else {
            img_shifted = vpx_img_alloc(
                NULL, img->fmt | VPX_IMG_FMT_HIGHBITDEPTH,
                img->d_w, img->d_h, 16);
          }
          img_shifted->bit_depth = output_bit_depth;
        }
        if (output_bit_depth > img->bit_depth) {
          img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
        } else {
          img_downshift(img_shifted, img, img->bit_depth - output_bit_depth);
        }
        img = img_shifted;
      }
#endif
1154

1155
      if (single_file) {
1156 1157 1158 1159 1160 1161 1162 1163
        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,
1164
                                        &vpx_input_ctx.framerate,
1165
                                        img->fmt, img->bit_depth);
1166
            if (do_md5) {
1167
              MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
1168 1169 1170 1171 1172 1173 1174 1175
            } else {
              fputs(buf, outfile);
            }
          }

          // Y4M frame header
          len = y4m_write_frame_header(buf, sizeof(buf));
          if (do_md5) {
1176
            MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
1177 1178 1179
          } else {
            fputs(buf, outfile);
          }
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
        } else {
          if (frame_out == 1) {
            // Check if --yv12 or --i420 options are consistent with the
            // bit-stream decoded
            if (opt_i420) {
              if (img->fmt != VPX_IMG_FMT_I420 &&
                  img->fmt != VPX_IMG_FMT_I42016) {
                fprintf(stderr, "Cannot produce i420 output for bit-stream.\n");
                goto fail;
              }
            }
            if (opt_yv12) {
              if ((img->fmt != VPX_IMG_FMT_I420 &&
                   img->fmt != VPX_IMG_FMT_YV12) || img->bit_depth != 8) {
                fprintf(stderr, "Cannot produce yv12 output for bit-stream.\n");
                goto fail;
              }
            }
          }
1199 1200
        }

1201 1202
        if (do_md5) {
          update_image_md5(img, planes, &md5_ctx);
1203
        } else {
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
          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);
1218
        }
John Koleszar's avatar
John Koleszar committed
1219
      }
John Koleszar's avatar
John Koleszar committed
1220 1221
    }

John Koleszar's avatar
John Koleszar committed
1222 1223 1224
    if (stop_after && frame_in >= stop_after)
      break;
  }
John Koleszar's avatar
John Koleszar committed
1225

John Koleszar's avatar
John Koleszar committed
1226 1227 1228 1229 1230 1231 1232
  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);
1233

John Koleszar's avatar
John Koleszar committed
1234 1235
fail:

John Koleszar's avatar
John Koleszar committed
1236
  if (vpx_codec_destroy(&decoder)) {
1237 1238
    fprintf(stderr, "Failed to destroy decoder: %s\n",
            vpx_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
1239 1240
    return EXIT_FAILURE;
  }
John Koleszar's avatar
John Koleszar committed
1241

1242 1243 1244 1245 1246 1247 1248 1249
  if (!noblit && single_file) {
    if (do_md5) {
      MD5Final(md5_digest, &md5_ctx);
      print_md5(md5_digest, outfile_name);
    } else {
      fclose(outfile);
    }
  }
John Koleszar's avatar
John Koleszar committed
1250

1251
#if CONFIG_WEBM_IO
1252 1253
  if (input.vpx_input_ctx->file_type == FILE_TYPE_WEBM)
    webm_free(input.webm_ctx);
1254 1255 1256
#endif

  if (input.vpx_input_ctx->file_type != FILE_TYPE_WEBM)
John Koleszar's avatar
John Koleszar committed
1257
    free(buf);
1258

1259
  if (scaled_img) vpx_img_free(scaled_img);
1260 1261 1262
#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
  if (img_shifted) vpx_img_free(img_shifted);
#endif
1263

1264 1265 1266 1267 1268
  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);

John Koleszar's avatar
John Koleszar committed
1269 1270
  fclose(infile);
  free(argv);
John Koleszar's avatar
John Koleszar committed
1271

John Koleszar's avatar
John Koleszar committed
1272
  return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS;
John Koleszar's avatar
John Koleszar committed
1273
}
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295

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;
    }
  }
  free(argv);
  for (i = 0; !error && i < loops; i++)
    error = main_loop(argc, argv_);
  return error;
}