aomdec.c 31.1 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
John Koleszar's avatar
John Koleszar committed
3
 *
4 5 6 7 8 9
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
John Koleszar's avatar
John Koleszar committed
10 11
 */

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

Adrian Grange's avatar
Adrian Grange committed
19
#include "./aom_config.h"
20 21

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

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

Adrian Grange's avatar
Adrian Grange committed
28
#include "aom/aom_decoder.h"
Yaowu Xu's avatar
Yaowu Xu committed
29
#include "aom_ports/mem_ops.h"
Adrian Grange's avatar
Adrian Grange committed
30
#include "aom_ports/aom_timer.h"
31

32
#if CONFIG_AV1_DECODER
Yaowu Xu's avatar
Yaowu Xu committed
33
#include "aom/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
struct VpxDecInputContext {
Adrian Grange's avatar
Adrian Grange committed
47
  struct VpxInputContext *aom_input_ctx;
48 49 50
  struct WebmInputContext *webm_ctx;
};

clang-format's avatar
clang-format committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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, "Codec to use");
static const arg_def_t use_yv12 =
    ARG_DEF(NULL, "yv12", 0, "Output raw YV12 frames");
static const arg_def_t use_i420 =
    ARG_DEF(NULL, "i420", 0, "Output raw I420 frames");
static const arg_def_t flipuvarg =
    ARG_DEF(NULL, "flipuv", 0, "Flip the chroma planes in the output");
static const arg_def_t rawvideo =
    ARG_DEF(NULL, "rawvideo", 0, "Output raw YUV frames");
static const arg_def_t noblitarg =
    ARG_DEF(NULL, "noblit", 0, "Don't process the decoded frames");
static const arg_def_t progressarg =
    ARG_DEF(NULL, "progress", 0, "Show progress after each frame decodes");
static const arg_def_t limitarg =
    ARG_DEF(NULL, "limit", 1, "Stop decoding after n frames");
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, "Postprocess decoded frames");
static const arg_def_t summaryarg =
    ARG_DEF(NULL, "summary", 0, "Show timing summary");
static const arg_def_t outputfile =
    ARG_DEF("o", "output", 1, "Output file name pattern (see below)");
static const arg_def_t threadsarg =
    ARG_DEF("t", "threads", 1, "Max threads to use");
static const arg_def_t frameparallelarg =
    ARG_DEF(NULL, "frame-parallel", 0, "Frame parallel decode");
static const arg_def_t verbosearg =
    ARG_DEF("v", "verbose", 0, "Show version string");
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 continuearg =
    ARG_DEF("k", "keep-going", 0, "(debug) Continue decoding after error");
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");
92
#if CONFIG_AOM_HIGHBITDEPTH
clang-format's avatar
clang-format committed
93 94
static const arg_def_t outbitdeptharg =
    ARG_DEF(NULL, "output-bit-depth", 1, "Output bit-depth for decoded frames");
95
#endif
Dmitry Kovalev's avatar
Dmitry Kovalev committed
96

clang-format's avatar
clang-format committed
97
/* clang-format off */
John Koleszar's avatar
John Koleszar committed
98
static const arg_def_t *all_args[] = {
99
  &codecarg, &use_yv12, &use_i420, &flipuvarg, &rawvideo, &noblitarg,
100
  &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
101
  &threadsarg, &frameparallelarg, &verbosearg, &scalearg, &fb_arg,
James Zern's avatar
James Zern committed
102
  &md5arg, &error_concealment, &continuearg,
103
#if CONFIG_AOM_HIGHBITDEPTH
104 105
  &outbitdeptharg,
#endif
John Koleszar's avatar
John Koleszar committed
106
  NULL
John Koleszar's avatar
John Koleszar committed
107
};
clang-format's avatar
clang-format committed
108
/* clang-format on */
John Koleszar's avatar
John Koleszar committed
109

110
#if CONFIG_LIBYUV
Adrian Grange's avatar
Adrian Grange committed
111
static INLINE int libyuv_scale(aom_image_t *src, aom_image_t *dst,
clang-format's avatar
clang-format committed
112
                               FilterModeEnum mode) {
113
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
114 115
  if (src->fmt == AOM_IMG_FMT_I42016) {
    assert(dst->fmt == AOM_IMG_FMT_I42016);
clang-format's avatar
clang-format committed
116
    return I420Scale_16(
Adrian Grange's avatar
Adrian Grange committed
117 118 119 120 121 122 123
        (uint16_t *)src->planes[AOM_PLANE_Y], src->stride[AOM_PLANE_Y] / 2,
        (uint16_t *)src->planes[AOM_PLANE_U], src->stride[AOM_PLANE_U] / 2,
        (uint16_t *)src->planes[AOM_PLANE_V], src->stride[AOM_PLANE_V] / 2,
        src->d_w, src->d_h, (uint16_t *)dst->planes[AOM_PLANE_Y],
        dst->stride[AOM_PLANE_Y] / 2, (uint16_t *)dst->planes[AOM_PLANE_U],
        dst->stride[AOM_PLANE_U] / 2, (uint16_t *)dst->planes[AOM_PLANE_V],
        dst->stride[AOM_PLANE_V] / 2, dst->d_w, dst->d_h, mode);
124 125
  }
#endif
Adrian Grange's avatar
Adrian Grange committed
126 127 128 129 130 131 132 133
  assert(src->fmt == AOM_IMG_FMT_I420);
  assert(dst->fmt == AOM_IMG_FMT_I420);
  return I420Scale(src->planes[AOM_PLANE_Y], src->stride[AOM_PLANE_Y],
                   src->planes[AOM_PLANE_U], src->stride[AOM_PLANE_U],
                   src->planes[AOM_PLANE_V], src->stride[AOM_PLANE_V], src->d_w,
                   src->d_h, dst->planes[AOM_PLANE_Y], dst->stride[AOM_PLANE_Y],
                   dst->planes[AOM_PLANE_U], dst->stride[AOM_PLANE_U],
                   dst->planes[AOM_PLANE_V], dst->stride[AOM_PLANE_V], dst->d_w,
clang-format's avatar
clang-format committed
134
                   dst->d_h, mode);
135
}
136
#endif
137

138
void usage_exit(void) {
John Koleszar's avatar
John Koleszar committed
139
  int i;
John Koleszar's avatar
John Koleszar committed
140

clang-format's avatar
clang-format committed
141 142 143 144
  fprintf(stderr,
          "Usage: %s <options> filename\n\n"
          "Options:\n",
          exec_name);
John Koleszar's avatar
John Koleszar committed
145 146 147 148 149 150 151
  arg_show_usage(stderr, all_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 "
John Koleszar's avatar
John Koleszar committed
152 153
          "the following escape characters:\n");
  fprintf(stderr,
John Koleszar's avatar
John Koleszar committed
154 155 156 157 158
          "\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 "
clang-format's avatar
clang-format committed
159
          "not specified, the output will be\n  directed to stdout.\n");
John Koleszar's avatar
John Koleszar committed
160 161
  fprintf(stderr, "\nIncluded decoders:\n\n");

Adrian Grange's avatar
Adrian Grange committed
162 163
  for (i = 0; i < get_aom_decoder_count(); ++i) {
    const VpxInterface *const decoder = get_aom_decoder_by_index(i);
clang-format's avatar
clang-format committed
164
    fprintf(stderr, "    %-6s - %s\n", decoder->name,
Adrian Grange's avatar
Adrian Grange committed
165
            aom_codec_iface_name(decoder->codec_interface()));
166
  }
John Koleszar's avatar
John Koleszar committed
167 168

  exit(EXIT_FAILURE);
John Koleszar's avatar
John Koleszar committed
169 170
}

clang-format's avatar
clang-format committed
171 172
static int raw_read_frame(FILE *infile, uint8_t **buffer, size_t *bytes_read,
                          size_t *buffer_size) {
173
  char raw_hdr[RAW_FRAME_HDR_SZ];
174
  size_t frame_size = 0;
John Koleszar's avatar
John Koleszar committed
175

176
  if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) {
clang-format's avatar
clang-format committed
177
    if (!feof(infile)) warn("Failed to read RAW frame size\n");
178
  } else {
179 180
    const size_t kCorruptFrameThreshold = 256 * 1024 * 1024;
    const size_t kFrameTooSmallThreshold = 256 * 1024;
181
    frame_size = mem_get_le32(raw_hdr);
John Koleszar's avatar
John Koleszar committed
182

183 184 185 186
    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
187

188 189 190
    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
191
    }
John Koleszar's avatar
John Koleszar committed
192

193 194 195 196 197 198 199 200
    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;
201
      }
John Koleszar's avatar
John Koleszar committed
202
    }
203
  }
John Koleszar's avatar
John Koleszar committed
204

205 206 207 208 209 210
  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
211 212
  }

213 214 215 216 217
  return 0;
}

static int read_frame(struct VpxDecInputContext *input, uint8_t **buf,
                      size_t *bytes_in_buffer, size_t *buffer_size) {
Adrian Grange's avatar
Adrian Grange committed
218
  switch (input->aom_input_ctx->file_type) {
219
#if CONFIG_WEBM_IO
220
    case FILE_TYPE_WEBM:
clang-format's avatar
clang-format committed
221 222
      return webm_read_frame(input->webm_ctx, buf, bytes_in_buffer,
                             buffer_size);
223
#endif
224
    case FILE_TYPE_RAW:
Adrian Grange's avatar
Adrian Grange committed
225
      return raw_read_frame(input->aom_input_ctx->file, buf, bytes_in_buffer,
clang-format's avatar
clang-format committed
226
                            buffer_size);
227
    case FILE_TYPE_IVF:
Adrian Grange's avatar
Adrian Grange committed
228
      return ivf_read_frame(input->aom_input_ctx->file, buf, bytes_in_buffer,
clang-format's avatar
clang-format committed
229 230
                            buffer_size);
    default: return 1;
231
  }
John Koleszar's avatar
John Koleszar committed
232 233
}

Adrian Grange's avatar
Adrian Grange committed
234
static void update_image_md5(const aom_image_t *img, const int planes[3],
235 236 237 238 239 240 241
                             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];
Adrian Grange's avatar
Adrian Grange committed
242
    const int w = aom_img_plane_width(img, plane) *
Adrian Grange's avatar
Adrian Grange committed
243
                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
Adrian Grange's avatar
Adrian Grange committed
244
    const int h = aom_img_plane_height(img, plane);
245 246 247 248 249 250 251 252

    for (y = 0; y < h; ++y) {
      MD5Update(md5, buf, w);
      buf += stride;
    }
  }
}

Adrian Grange's avatar
Adrian Grange committed
253
static void write_image_file(const aom_image_t *img, const int planes[3],
254 255
                             FILE *file) {
  int i, y;
256
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
257
  const int bytes_per_sample = ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
258 259 260
#else
  const int bytes_per_sample = 1;
#endif
261 262 263 264 265

  for (i = 0; i < 3; ++i) {
    const int plane = planes[i];
    const unsigned char *buf = img->planes[plane];
    const int stride = img->stride[plane];
Adrian Grange's avatar
Adrian Grange committed
266 267
    const int w = aom_img_plane_width(img, plane);
    const int h = aom_img_plane_height(img, plane);
268 269

    for (y = 0; y < h; ++y) {
270
      fwrite(buf, bytes_per_sample, w, file);
271 272
      buf += stride;
    }
John Koleszar's avatar
John Koleszar committed
273
  }
John Koleszar's avatar
John Koleszar committed
274 275
}

276
static int file_is_raw(struct VpxInputContext *input) {
277
  uint8_t buf[32];
John Koleszar's avatar
John Koleszar committed
278
  int is_raw = 0;
Adrian Grange's avatar
Adrian Grange committed
279
  aom_codec_stream_info_t si;
280

John Koleszar's avatar
John Koleszar committed
281 282
  si.sz = sizeof(si);

283
  if (fread(buf, 1, 32, input->file) == 32) {
John Koleszar's avatar
John Koleszar committed
284 285
    int i;

286
    if (mem_get_le32(buf) < 256 * 1024 * 1024) {
Adrian Grange's avatar
Adrian Grange committed
287 288 289
      for (i = 0; i < get_aom_decoder_count(); ++i) {
        const VpxInterface *const decoder = get_aom_decoder_by_index(i);
        if (!aom_codec_peek_stream_info(decoder->codec_interface(), buf + 4,
clang-format's avatar
clang-format committed
290
                                        32 - 4, &si)) {
John Koleszar's avatar
John Koleszar committed
291
          is_raw = 1;
292
          input->fourcc = decoder->fourcc;
293 294 295 296
          input->width = si.w;
          input->height = si.h;
          input->framerate.numerator = 30;
          input->framerate.denominator = 1;
John Koleszar's avatar
John Koleszar committed
297 298
          break;
        }
299 300
      }
    }
John Koleszar's avatar
John Koleszar committed
301 302
  }

303
  rewind(input->file);
John Koleszar's avatar
John Koleszar committed
304
  return is_raw;
305 306
}

307
static void show_progress(int frame_in, int frame_out, uint64_t dx_time) {
308
  fprintf(stderr,
clang-format's avatar
clang-format committed
309
          "%d decoded frames/%d showed frames in %" PRId64 " us (%.2f fps)\r",
John Koleszar's avatar
John Koleszar committed
310
          frame_in, frame_out, dx_time,
311
          (double)frame_out * 1000000.0 / (double)dx_time);
312 313
}

314
struct ExternalFrameBuffer {
clang-format's avatar
clang-format committed
315
  uint8_t *data;
316 317 318 319 320 321 322 323 324
  size_t size;
  int in_use;
};

struct ExternalFrameBufferList {
  int num_external_frame_buffers;
  struct ExternalFrameBuffer *ext_fb;
};

Yaowu Xu's avatar
Yaowu Xu committed
325
// Callback used by libaom to request an external frame buffer. |cb_priv|
326 327 328
// 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.
329
static int get_vp9_frame_buffer(void *cb_priv, size_t min_size,
Adrian Grange's avatar
Adrian Grange committed
330
                                aom_codec_frame_buffer_t *fb) {
331 332 333
  int i;
  struct ExternalFrameBufferList *const ext_fb_list =
      (struct ExternalFrameBufferList *)cb_priv;
clang-format's avatar
clang-format committed
334
  if (ext_fb_list == NULL) return -1;
335 336 337

  // Find a free frame buffer.
  for (i = 0; i < ext_fb_list->num_external_frame_buffers; ++i) {
clang-format's avatar
clang-format committed
338
    if (!ext_fb_list->ext_fb[i].in_use) break;
339 340
  }

clang-format's avatar
clang-format committed
341
  if (i == ext_fb_list->num_external_frame_buffers) return -1;
342 343 344

  if (ext_fb_list->ext_fb[i].size < min_size) {
    free(ext_fb_list->ext_fb[i].data);
345
    ext_fb_list->ext_fb[i].data = (uint8_t *)calloc(min_size, sizeof(uint8_t));
clang-format's avatar
clang-format committed
346
    if (!ext_fb_list->ext_fb[i].data) return -1;
347 348 349 350 351 352 353 354 355 356 357 358 359

    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;
}

Yaowu Xu's avatar
Yaowu Xu committed
360
// Callback used by libaom when there are no references to the frame buffer.
361 362
// |cb_priv| user private data passed into the set function. |fb| pointer
// to the frame buffer.
363
static int release_vp9_frame_buffer(void *cb_priv,
Adrian Grange's avatar
Adrian Grange committed
364
                                    aom_codec_frame_buffer_t *fb) {
365 366 367 368 369 370 371
  struct ExternalFrameBuffer *const ext_fb =
      (struct ExternalFrameBuffer *)fb->priv;
  (void)cb_priv;
  ext_fb->in_use = 0;
  return 0;
}

372 373 374
static void generate_filename(const char *pattern, char *out, size_t q_len,
                              unsigned int d_w, unsigned int d_h,
                              unsigned int frame_in) {
John Koleszar's avatar
John Koleszar committed
375 376 377 378 379 380 381 382 383
  const char *p = pattern;
  char *q = out;

  do {
    char *next_pat = strchr(p, '%');

    if (p == next_pat) {
      size_t pat_len;

John Koleszar's avatar
John Koleszar committed
384
      /* parse the pattern */
John Koleszar's avatar
John Koleszar committed
385 386
      q[q_len - 1] = '\0';
      switch (p[1]) {
clang-format's avatar
clang-format committed
387 388 389 390 391 392 393 394 395 396 397 398
        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]); break;
John Koleszar's avatar
John Koleszar committed
399 400 401
      }

      pat_len = strlen(q);
clang-format's avatar
clang-format committed
402
      if (pat_len >= q_len - 1) die("Output filename too long.\n");
John Koleszar's avatar
John Koleszar committed
403 404 405 406 407 408
      q += pat_len;
      p += 2;
      q_len -= pat_len;
    } else {
      size_t copy_len;

John Koleszar's avatar
John Koleszar committed
409
      /* copy the next segment */
John Koleszar's avatar
John Koleszar committed
410 411 412 413 414
      if (!next_pat)
        copy_len = strlen(p);
      else
        copy_len = next_pat - p;

clang-format's avatar
clang-format committed
415
      if (copy_len >= q_len - 1) die("Output filename too long.\n");
John Koleszar's avatar
John Koleszar committed
416 417 418 419 420 421 422 423

      memcpy(q, p, copy_len);
      q[copy_len] = '\0';
      q += copy_len;
      p += copy_len;
      q_len -= copy_len;
    }
  } while (*p);
424 425
}

426 427 428 429 430 431 432
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
clang-format's avatar
clang-format committed
433
    if (p) p++;
434 435 436 437 438 439 440 441
  } while (p);

  return 1;
}

static void print_md5(unsigned char digest[16], const char *filename) {
  int i;

clang-format's avatar
clang-format committed
442
  for (i = 0; i < 16; ++i) printf("%02x", digest[i]);
443 444 445 446 447 448 449 450 451
  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");
clang-format's avatar
clang-format committed
452
    if (!file) fatal("Failed to open output file '%s'", name);
453 454 455 456
    return file;
  }
}

457
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
458 459 460
static int img_shifted_realloc_required(const aom_image_t *img,
                                        const aom_image_t *shifted,
                                        aom_img_fmt_t required_fmt) {
clang-format's avatar
clang-format committed
461
  return img->d_w != shifted->d_w || img->d_h != shifted->d_h ||
462 463
         required_fmt != shifted->fmt;
}
464 465
#endif

466
static int main_loop(int argc, const char **argv_) {
Adrian Grange's avatar
Adrian Grange committed
467
  aom_codec_ctx_t decoder;
clang-format's avatar
clang-format committed
468 469 470 471 472 473 474 475 476 477 478
  char *fn = NULL;
  int i;
  uint8_t *buf = NULL;
  size_t bytes_in_buffer = 0, buffer_size = 0;
  FILE *infile;
  int frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0;
  int do_md5 = 0, progress = 0, frame_parallel = 0;
  int stop_after = 0, postproc = 0, summary = 0, quiet = 1;
  int arg_skip = 0;
  int ec_enabled = 0;
  int keep_going = 0;
479 480
  const VpxInterface *interface = NULL;
  const VpxInterface *fourcc_interface = NULL;
481
  uint64_t dx_time = 0;
clang-format's avatar
clang-format committed
482 483 484 485 486 487 488
  struct arg arg;
  char **argv, **argi, **argj;

  int single_file;
  int use_y4m = 1;
  int opt_yv12 = 0;
  int opt_i420 = 0;
Adrian Grange's avatar
Adrian Grange committed
489
  aom_codec_dec_cfg_t cfg = { 0, 0, 0 };
490
#if CONFIG_AOM_HIGHBITDEPTH
clang-format's avatar
clang-format committed
491
  unsigned int output_bit_depth = 0;
John Koleszar's avatar
John Koleszar committed
492
#endif
clang-format's avatar
clang-format committed
493 494 495
  int frames_corrupted = 0;
  int dec_flags = 0;
  int do_scale = 0;
Adrian Grange's avatar
Adrian Grange committed
496
  aom_image_t *scaled_img = NULL;
497
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
498
  aom_image_t *img_shifted = NULL;
499
#endif
clang-format's avatar
clang-format committed
500 501 502
  int frame_avail, got_data, flush_decoder = 0;
  int num_external_frame_buffers = 0;
  struct ExternalFrameBufferList ext_fb_list = { 0, NULL };
John Koleszar's avatar
John Koleszar committed
503

504
  const char *outfile_pattern = NULL;
clang-format's avatar
clang-format committed
505
  char outfile_name[PATH_MAX] = { 0 };
506 507 508 509 510
  FILE *outfile = NULL;

  MD5Context md5_ctx;
  unsigned char md5_digest[16];

clang-format's avatar
clang-format committed
511
  struct VpxDecInputContext input = { NULL, NULL };
Adrian Grange's avatar
Adrian Grange committed
512
  struct VpxInputContext aom_input_ctx;
513
#if CONFIG_WEBM_IO
514 515
  struct WebmInputContext webm_ctx;
  memset(&(webm_ctx), 0, sizeof(webm_ctx));
516
  input.webm_ctx = &webm_ctx;
517
#endif
Adrian Grange's avatar
Adrian Grange committed
518
  input.aom_input_ctx = &aom_input_ctx;
519

John Koleszar's avatar
John Koleszar committed
520 521 522 523 524 525 526 527 528
  /* 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)) {
Adrian Grange's avatar
Adrian Grange committed
529
      interface = get_aom_decoder_by_name(arg.val);
530 531
      if (!interface)
        die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
John Koleszar's avatar
John Koleszar committed
532 533
    } else if (arg_match(&arg, &looparg, argi)) {
      // no-op
John Koleszar's avatar
John Koleszar committed
534 535 536 537 538
    } else if (arg_match(&arg, &outputfile, argi))
      outfile_pattern = arg.val;
    else if (arg_match(&arg, &use_yv12, argi)) {
      use_y4m = 0;
      flipuv = 1;
539
      opt_yv12 = 1;
John Koleszar's avatar
John Koleszar committed
540 541 542
    } else if (arg_match(&arg, &use_i420, argi)) {
      use_y4m = 0;
      flipuv = 0;
543 544 545
      opt_i420 = 1;
    } else if (arg_match(&arg, &rawvideo, argi)) {
      use_y4m = 0;
John Koleszar's avatar
John Koleszar committed
546 547 548 549 550 551 552 553
    } 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);
554 555
    else if (arg_match(&arg, &skiparg, argi))
      arg_skip = arg_parse_uint(&arg);
John Koleszar's avatar
John Koleszar committed
556 557 558 559 560 561 562 563
    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);
564
#if CONFIG_AV1_DECODER
565 566 567
    else if (arg_match(&arg, &frameparallelarg, argi))
      frame_parallel = 1;
#endif
John Koleszar's avatar
John Koleszar committed
568 569
    else if (arg_match(&arg, &verbosearg, argi))
      quiet = 0;
John Koleszar's avatar
John Koleszar committed
570 571
    else if (arg_match(&arg, &scalearg, argi))
      do_scale = 1;
572 573
    else if (arg_match(&arg, &fb_arg, argi))
      num_external_frame_buffers = arg_parse_uint(&arg);
574 575
    else if (arg_match(&arg, &continuearg, argi))
      keep_going = 1;
576
#if CONFIG_AOM_HIGHBITDEPTH
577 578 579 580
    else if (arg_match(&arg, &outbitdeptharg, argi)) {
      output_bit_depth = arg_parse_uint(&arg);
    }
#endif
John Koleszar's avatar
John Koleszar committed
581 582 583
    else
      argj++;
  }
John Koleszar's avatar
John Koleszar committed
584

John Koleszar's avatar
John Koleszar committed
585 586 587 588
  /* 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
589

John Koleszar's avatar
John Koleszar committed
590 591
  /* Handle non-option arguments */
  fn = argv[0];
John Koleszar's avatar
John Koleszar committed
592

hanno's avatar
hanno committed
593 594
  if (!fn) {
    free(argv);
John Koleszar's avatar
John Koleszar committed
595
    usage_exit();
hanno's avatar
hanno committed
596
  }
John Koleszar's avatar
John Koleszar committed
597 598
  /* Open file */
  infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin);
John Koleszar's avatar
John Koleszar committed
599

John Koleszar's avatar
John Koleszar committed
600
  if (!infile) {
601
    fatal("Failed to open input file '%s'", strcmp(fn, "-") ? fn : "stdin");
John Koleszar's avatar
John Koleszar committed
602
  }
603
#if CONFIG_OS_SUPPORT
John Koleszar's avatar
John Koleszar committed
604 605 606 607 608 609 610
  /* 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;
  }
611
#endif
Adrian Grange's avatar
Adrian Grange committed
612 613 614
  input.aom_input_ctx->file = infile;
  if (file_is_ivf(input.aom_input_ctx))
    input.aom_input_ctx->file_type = FILE_TYPE_IVF;
615
#if CONFIG_WEBM_IO
Adrian Grange's avatar
Adrian Grange committed
616 617
  else if (file_is_webm(input.webm_ctx, input.aom_input_ctx))
    input.aom_input_ctx->file_type = FILE_TYPE_WEBM;
618
#endif
Adrian Grange's avatar
Adrian Grange committed
619 620
  else if (file_is_raw(input.aom_input_ctx))
    input.aom_input_ctx->file_type = FILE_TYPE_RAW;
John Koleszar's avatar
John Koleszar committed
621 622
  else {
    fprintf(stderr, "Unrecognized input file type.\n");
623
#if !CONFIG_WEBM_IO
Adrian Grange's avatar
Adrian Grange committed
624
    fprintf(stderr, "aomdec was built without WebM container support.\n");
625
#endif
John Koleszar's avatar
John Koleszar committed
626 627 628 629
    return EXIT_FAILURE;
  }

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

632 633
  if (!noblit && single_file) {
    generate_filename(outfile_pattern, outfile_name, PATH_MAX,
Adrian Grange's avatar
Adrian Grange committed
634
                      aom_input_ctx.width, aom_input_ctx.height, 0);
635 636 637 638
    if (do_md5)
      MD5Init(&md5_ctx);
    else
      outfile = open_outfile(outfile_name);
John Koleszar's avatar
John Koleszar committed
639 640 641 642
  }

  if (use_y4m && !noblit) {
    if (!single_file) {
clang-format's avatar
clang-format committed
643 644
      fprintf(stderr,
              "YUV4MPEG2 not supported with output patterns,"
Deb Mukherjee's avatar
Deb Mukherjee committed
645
              " try --i420 or --yv12 or --rawvideo.\n");
John Koleszar's avatar
John Koleszar committed
646
      return EXIT_FAILURE;
647
    }
John Koleszar's avatar
John Koleszar committed
648

649
#if CONFIG_WEBM_IO
Adrian Grange's avatar
Adrian Grange committed
650 651
    if (aom_input_ctx.file_type == FILE_TYPE_WEBM) {
      if (webm_guess_framerate(input.webm_ctx, input.aom_input_ctx)) {
clang-format's avatar
clang-format committed
652 653
        fprintf(stderr,
                "Failed to guess framerate -- error parsing "
John Koleszar's avatar
John Koleszar committed
654 655 656
                "webm file?\n");
        return EXIT_FAILURE;
      }
657
    }
658
#endif
John Koleszar's avatar
John Koleszar committed
659 660
  }

Adrian Grange's avatar
Adrian Grange committed
661
  fourcc_interface = get_aom_decoder_by_fourcc(aom_input_ctx.fourcc);
662 663 664 665
  if (interface && fourcc_interface && interface != fourcc_interface)
    warn("Header indicates codec: %s\n", fourcc_interface->name);
  else
    interface = fourcc_interface;
John Koleszar's avatar
John Koleszar committed
666

Adrian Grange's avatar
Adrian Grange committed
667
  if (!interface) interface = get_aom_decoder_by_index(0);
668

Adrian Grange's avatar
Adrian Grange committed
669 670 671
  dec_flags = (postproc ? AOM_CODEC_USE_POSTPROC : 0) |
              (ec_enabled ? AOM_CODEC_USE_ERROR_CONCEALMENT : 0) |
              (frame_parallel ? AOM_CODEC_USE_FRAME_THREADING : 0);
Adrian Grange's avatar
Adrian Grange committed
672
  if (aom_codec_dec_init(&decoder, interface->codec_interface(), &cfg,
clang-format's avatar
clang-format committed
673
                         dec_flags)) {
674
    fprintf(stderr, "Failed to initialize decoder: %s\n",
Adrian Grange's avatar
Adrian Grange committed
675
            aom_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
676 677
    return EXIT_FAILURE;
  }
John Koleszar's avatar
John Koleszar committed
678

clang-format's avatar
clang-format committed
679
  if (!quiet) fprintf(stderr, "%s\n", decoder.name);
John Koleszar's avatar
John Koleszar committed
680

clang-format's avatar
clang-format committed
681
  if (arg_skip) fprintf(stderr, "Skipping first %d frames.\n", arg_skip);
682
  while (arg_skip) {
clang-format's avatar
clang-format committed
683
    if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) break;
684 685 686
    arg_skip--;
  }

687 688 689 690
  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));
Adrian Grange's avatar
Adrian Grange committed
691
    if (aom_codec_set_frame_buffer_functions(&decoder, get_vp9_frame_buffer,
clang-format's avatar
clang-format committed
692 693
                                             release_vp9_frame_buffer,
                                             &ext_fb_list)) {
694
      fprintf(stderr, "Failed to configure external frame buffers: %s\n",
Adrian Grange's avatar
Adrian Grange committed
695
              aom_codec_error(&decoder));
696 697 698 699
      return EXIT_FAILURE;
    }
  }

Scott LaVarnway's avatar
Scott LaVarnway committed
700 701 702
  frame_avail = 1;
  got_data = 0;

John Koleszar's avatar
John Koleszar committed
703
  /* Decode file */
Scott LaVarnway's avatar
Scott LaVarnway committed
704
  while (frame_avail || got_data) {
Adrian Grange's avatar
Adrian Grange committed
705 706 707
    aom_codec_iter_t iter = NULL;
    aom_image_t *img;
    struct aom_usec_timer timer;
clang-format's avatar
clang-format committed
708
    int corrupted = 0;
John Koleszar's avatar
John Koleszar committed
709

Scott LaVarnway's avatar
Scott LaVarnway committed
710 711
    frame_avail = 0;
    if (!stop_after || frame_in < stop_after) {
712
      if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) {
Scott LaVarnway's avatar
Scott LaVarnway committed
713 714
        frame_avail = 1;
        frame_in++;
John Koleszar's avatar
John Koleszar committed
715

Adrian Grange's avatar
Adrian Grange committed
716
        aom_usec_timer_start(&timer);
John Koleszar's avatar
John Koleszar committed
717

Adrian Grange's avatar
Adrian Grange committed
718
        if (aom_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer, NULL,
clang-format's avatar
clang-format committed
719
                             0)) {
Adrian Grange's avatar
Adrian Grange committed
720
          const char *detail = aom_codec_error_detail(&decoder);
clang-format's avatar
clang-format committed
721
          warn("Failed to decode frame %d: %s", frame_in,
Adrian Grange's avatar
Adrian Grange committed
722
               aom_codec_error(&decoder));
723

clang-format's avatar
clang-format committed
724 725
          if (detail) warn("Additional information: %s", detail);
          if (!keep_going) goto fail;
Scott LaVarnway's avatar
Scott LaVarnway committed
726 727
        }

Adrian Grange's avatar
Adrian Grange committed
728 729
        aom_usec_timer_mark(&timer);
        dx_time += aom_usec_timer_elapsed(&timer);
730 731
      } else {
        flush_decoder = 1;
Scott LaVarnway's avatar
Scott LaVarnway committed
732
      }
733 734
    } else {
      flush_decoder = 1;
Scott LaVarnway's avatar
Scott LaVarnway committed
735 736
    }

Adrian Grange's avatar
Adrian Grange committed
737
    aom_usec_timer_start(&timer);
Scott LaVarnway's avatar
Scott LaVarnway committed
738

739 740
    if (flush_decoder) {
      // Flush the decoder in frame parallel decode.
Adrian Grange's avatar
Adrian Grange committed
741 742
      if (aom_codec_decode(&decoder, NULL, 0, NULL, 0)) {
        warn("Failed to flush decoder: %s", aom_codec_error(&decoder));
743 744 745
      }
    }

Scott LaVarnway's avatar
Scott LaVarnway committed
746
    got_data = 0;
Adrian Grange's avatar
Adrian Grange committed
747
    if ((img = aom_codec_get_frame(&decoder, &iter))) {
Scott LaVarnway's avatar
Scott LaVarnway committed
748 749
      ++frame_out;
      got_data = 1;
750 751
    }

Adrian Grange's avatar
Adrian Grange committed
752 753
    aom_usec_timer_mark(&timer);
    dx_time += (unsigned int)aom_usec_timer_elapsed(&timer);
754

755
    if (!frame_parallel &&
Adrian Grange's avatar
Adrian Grange committed
756 757
        aom_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) {
      warn("Failed VP8_GET_FRAME_CORRUPTED: %s", aom_codec_error(&decoder));
clang-format's avatar
clang-format committed
758
      if (!keep_going) goto fail;
759
    }
John Koleszar's avatar
John Koleszar committed
760
    frames_corrupted += corrupted;
John Koleszar's avatar
John Koleszar committed
761

clang-format's avatar
clang-format committed
762
    if (progress) show_progress(frame_in, frame_out, dx_time);
John Koleszar's avatar
John Koleszar committed
763

764
    if (!noblit && img) {
Adrian Grange's avatar
Adrian Grange committed
765 766
      const int PLANES_YUV[] = { AOM_PLANE_Y, AOM_PLANE_U, AOM_PLANE_V };
      const int PLANES_YVU[] = { AOM_PLANE_Y, AOM_PLANE_V, AOM_PLANE_U };
767
      const int *planes = flipuv ? PLANES_YVU : PLANES_YUV;
768

769
      if (do_scale) {
770 771 772 773
        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
774 775
          // header. If that is unavailable, use the raw decoded size of the
          // first decoded frame.
Adrian Grange's avatar
Adrian Grange committed
776 777
          int render_width = aom_input_ctx.width;
          int render_height = aom_input_ctx.height;
778 779
          if (!render_width || !render_height) {
            int render_size[2];
Adrian Grange's avatar
Adrian Grange committed
780
            if (aom_codec_control(&decoder, VP9D_GET_DISPLAY_SIZE,
781
                                  render_size)) {
782
              // As last resort use size of first frame as display size.
783 784
              render_width = img->d_w;
              render_height = img->d_h;
785
            } else {
786 787
              render_width = render_size[0];
              render_height = render_size[1];
788
            }
789
          }
clang-format's avatar
clang-format committed
790
          scaled_img =
Adrian Grange's avatar
Adrian Grange committed
791
              aom_img_alloc(NULL, img->fmt, render_width, render_height, 16);
792
          scaled_img->bit_depth = img->bit_depth;
John Koleszar's avatar
John Koleszar committed
793
        }
794

795
        if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) {
Deb Mukherjee's avatar
Deb Mukherjee committed
796
#if CONFIG_LIBYUV
797
          libyuv_scale(img, scaled_img, kFilterBox);
John Koleszar's avatar
John Koleszar committed
798
          img = scaled_img;
Deb Mukherjee's avatar
Deb Mukherjee committed
799
#else
clang-format's avatar
clang-format committed
800 801
          fprintf(stderr,
                  "Failed  to scale output frame: %s.\n"
Deb Mukherjee's avatar
Deb Mukherjee committed
802 803
                  "Scaling is disabled in this configuration. "
                  "To enable scaling, configure with --enable-libyuv\n",
Adrian Grange's avatar
Adrian Grange committed
804
                  aom_codec_error(&decoder));
Deb Mukherjee's avatar
Deb Mukherjee committed
805 806
          return EXIT_FAILURE;
#endif
John Koleszar's avatar
John Koleszar committed
807 808
        }
      }
809
#if CONFIG_AOM_HIGHBITDEPTH
810
      // Default to codec bit depth if output bit depth not set
811
      if (!output_bit_depth && single_file && !do_md5) {
812 813 814
        output_bit_depth = img->bit_depth;
      }
      // Shift up or down if necessary
815
      if (output_bit_depth != 0 && output_bit_depth != img->bit_depth) {
Adrian Grange's avatar
Adrian Grange committed
816
        const aom_img_fmt_t shifted_fmt =
clang-format's avatar
clang-format committed
817
            output_bit_depth == 8
Adrian Grange's avatar
Adrian Grange committed
818 819
                ? img->fmt ^ (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH)
                : img->fmt | AOM_IMG_FMT_HIGHBITDEPTH;
820 821
        if (img_shifted &&
            img_shifted_realloc_required(img, img_shifted, shifted_fmt)) {
Adrian Grange's avatar
Adrian Grange committed
822
          aom_img_free(img_shifted);
823 824
          img_shifted = NULL;
        }
825
        if (!img_shifted) {
clang-format's avatar
clang-format committed
826
          img_shifted =
Adrian Grange's avatar
Adrian Grange committed
827
              aom_img_alloc(NULL, shifted_fmt, img->d_w, img->d_h, 16);
828 829 830
          img_shifted->bit_depth = output_bit_depth;
        }
        if (output_bit_depth > img->bit_depth) {
Adrian Grange's avatar
Adrian Grange committed
831
          aom_img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
832
        } else {
Adrian Grange's avatar
Adrian Grange committed
833
          aom_img_downshift(img_shifted, img,
834
                            img->bit_depth - output_bit_depth);
835 836 837 838
        }
        img = img_shifted;
      }
#endif
839

840
      if (single_file) {
841
        if (use_y4m) {
clang-format's avatar
clang-format committed
842
          char buf[Y4M_BUFFER_SIZE] = { 0 };
843
          size_t len = 0;
Adrian Grange's avatar
Adrian Grange committed
844
          if (img->fmt == AOM_IMG_FMT_I440 || img->fmt == AOM_IMG_FMT_I44016) {
Deb Mukherjee's avatar
Deb Mukherjee committed
845 846 847
            fprintf(stderr, "Cannot produce y4m output for 440 sampling.\n");
            goto fail;
          }
848 849
          if (frame_out == 1) {
            // Y4M file header
clang-format's avatar
clang-format committed
850
            len = y4m_write_file_header(
Adrian Grange's avatar
Adrian Grange committed
851 852
                buf, sizeof(buf), aom_input_ctx.width, aom_input_ctx.height,
                &aom_input_ctx.framerate, img->fmt, img->bit_depth);
853
            if (do_md5) {
854
              MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
855 856 857 858 859 860 861 862
            } else {
              fputs(buf, outfile);
            }
          }

          // Y4M frame header
          len = y4m_write_frame_header(buf, sizeof(buf));
          if (do_md5) {
863
            MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
864 865 866
          } else {
            fputs(buf, outfile);
          }
867 868 869 870 871
        } else {
          if (frame_out == 1) {
            // Check if --yv12 or --i420 options are consistent with the
            // bit-stream decoded
            if (opt_i420) {
Adrian Grange's avatar
Adrian Grange committed
872 873
              if (img->fmt != AOM_IMG_FMT_I420 &&
                  img->fmt != AOM_IMG_FMT_I42016) {
874 875 876 877 878
                fprintf(stderr, "Cannot produce i420 output for bit-stream.\n");
                goto fail;
              }
            }
            if (opt_yv12) {
Adrian Grange's avatar
Adrian Grange committed
879 880
              if ((img->fmt != AOM_IMG_FMT_I420 &&
                   img->fmt != AOM_IMG_FMT_YV12) ||
clang-format's avatar
clang-format committed
881
                  img->bit_depth != 8) {
882 883 884 885 886
                fprintf(stderr, "Cannot produce yv12 output for bit-stream.\n");
                goto fail;
              }
            }
          }
887 888
        }

889 890
        if (do_md5) {
          update_image_md5(img, planes, &md5_ctx);
891
        } else {
892 893 894
          write_image_file(img, planes, outfile);
        }
      } else {
clang-format's avatar
clang-format committed
895 896
        generate_filename(outfile_pattern, outfile_name, PATH_MAX, img->d_w,
                          img->d_h, frame_in);
897 898 899 900 901 902 903 904 905
        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);
906
        }
John Koleszar's avatar
John Koleszar committed
907
      }
John Koleszar's avatar
John Koleszar committed
908
    }
John Koleszar's avatar
John Koleszar committed
909
  }
John Koleszar's avatar
John Koleszar committed
910

John Koleszar's avatar
John Koleszar committed
911 912 913 914 915 916 917
  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);
918

John Koleszar's avatar
John Koleszar committed
919 920
fail:

Adrian Grange's avatar
Adrian Grange committed
921
  if (aom_codec_destroy(&decoder)) {
922
    fprintf(stderr, "Failed to destroy decoder: %s\n",
Adrian Grange's avatar
Adrian Grange committed
923
            aom_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
924 925
    return EXIT_FAILURE;
  }
John Koleszar's avatar
John Koleszar committed
926

927 928 929 930 931 932 933 934
  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
935

936
#if CONFIG_WEBM_IO
Adrian Grange's avatar
Adrian Grange committed
937
  if (input.aom_input_ctx->file_type == FILE_TYPE_WEBM)
938
    webm_free(input.webm_ctx);
939 940
#endif

Adrian Grange's avatar
Adrian Grange committed
941
  if (input.aom_input_ctx->file_type != FILE_TYPE_WEBM) free(buf);
942

Adrian Grange's avatar
Adrian Grange committed
943
  if (scaled_img) aom_img_free(scaled_img);
944
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
945
  if (img_shifted) aom_img_free(img_shifted);
946
#endif
947

948 949 950 951 952
  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
953 954
  fclose(infile);
  free(argv);
John Koleszar's avatar
John Koleszar committed
955

John Koleszar's avatar
John Koleszar committed
956
  return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS;
John Koleszar's avatar
John Koleszar committed
957
}
John Koleszar's avatar
John Koleszar committed
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975

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);
clang-format's avatar
clang-format committed
976
  for (i = 0; !error && i < loops; i++) error = main_loop(argc, argv_);
John Koleszar's avatar
John Koleszar committed
977 978
  return error;
}