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
Adrian Grange's avatar
Adrian Grange committed
33
#include "aom/aomdx.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;

Adrian Grange's avatar
Adrian Grange committed
46 47
struct AvxDecInputContext {
  struct AvxInputContext *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
  for (i = 0; i < get_aom_decoder_count(); ++i) {
Adrian Grange's avatar
Adrian Grange committed
163
    const AvxInterface *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
  return 0;
}

Adrian Grange's avatar
Adrian Grange committed
216
static int read_frame(struct AvxDecInputContext *input, uint8_t **buf,
217
                      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:
Yaowu Xu's avatar
Yaowu Xu committed
221
      return webm_read_frame(input->webm_ctx, buf, bytes_in_buffer);
222
#endif
223
    case FILE_TYPE_RAW:
Adrian Grange's avatar
Adrian Grange committed
224
      return raw_read_frame(input->aom_input_ctx->file, buf, bytes_in_buffer,
clang-format's avatar
clang-format committed
225
                            buffer_size);
226
    case FILE_TYPE_IVF:
Adrian Grange's avatar
Adrian Grange committed
227
      return ivf_read_frame(input->aom_input_ctx->file, buf, bytes_in_buffer,
clang-format's avatar
clang-format committed
228 229
                            buffer_size);
    default: return 1;
230
  }
John Koleszar's avatar
John Koleszar committed
231 232
}

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

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

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

  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
265 266
    const int w = aom_img_plane_width(img, plane);
    const int h = aom_img_plane_height(img, plane);
267 268

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

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

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

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

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

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

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

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

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

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

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

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

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

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

371 372 373
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
374 375 376 377 378 379 380 381 382
  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
383
      /* parse the pattern */
John Koleszar's avatar
John Koleszar committed
384 385
      q[q_len - 1] = '\0';
      switch (p[1]) {
clang-format's avatar
clang-format committed
386 387 388 389 390 391 392 393 394 395 396 397
        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
398 399 400
      }

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

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

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

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

425 426 427 428 429 430 431
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
432
    if (p) p++;
433 434 435 436 437 438 439 440
  } while (p);

  return 1;
}

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

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

456
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
457 458 459
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
460
  return img->d_w != shifted->d_w || img->d_h != shifted->d_h ||
461 462
         required_fmt != shifted->fmt;
}
463 464
#endif

465
static int main_loop(int argc, const char **argv_) {
Adrian Grange's avatar
Adrian Grange committed
466
  aom_codec_ctx_t decoder;
clang-format's avatar
clang-format committed
467 468 469 470 471 472 473 474 475 476 477
  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;
Adrian Grange's avatar
Adrian Grange committed
478 479
  const AvxInterface *interface = NULL;
  const AvxInterface *fourcc_interface = NULL;
480
  uint64_t dx_time = 0;
clang-format's avatar
clang-format committed
481 482 483 484 485 486 487
  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
488
  aom_codec_dec_cfg_t cfg = { 0, 0, 0 };
489
#if CONFIG_AOM_HIGHBITDEPTH
clang-format's avatar
clang-format committed
490
  unsigned int output_bit_depth = 0;
John Koleszar's avatar
John Koleszar committed
491
#endif
clang-format's avatar
clang-format committed
492 493 494
  int frames_corrupted = 0;
  int dec_flags = 0;
  int do_scale = 0;
Adrian Grange's avatar
Adrian Grange committed
495
  aom_image_t *scaled_img = NULL;
496
#if CONFIG_AOM_HIGHBITDEPTH
Adrian Grange's avatar
Adrian Grange committed
497
  aom_image_t *img_shifted = NULL;
498
#endif
clang-format's avatar
clang-format committed
499 500 501
  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
502

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

  MD5Context md5_ctx;
  unsigned char md5_digest[16];

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

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

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

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

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

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

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

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

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

648
#if CONFIG_WEBM_IO
Adrian Grange's avatar
Adrian Grange committed
649 650
    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
651 652
        fprintf(stderr,
                "Failed to guess framerate -- error parsing "
John Koleszar's avatar
John Koleszar committed
653 654 655
                "webm file?\n");
        return EXIT_FAILURE;
      }
656
    }
657
#endif
John Koleszar's avatar
John Koleszar committed
658 659
  }

Adrian Grange's avatar
Adrian Grange committed
660
  fourcc_interface = get_aom_decoder_by_fourcc(aom_input_ctx.fourcc);
661 662 663 664
  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
665

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

Adrian Grange's avatar
Adrian Grange committed
668 669 670
  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
671
  if (aom_codec_dec_init(&decoder, interface->codec_interface(), &cfg,
clang-format's avatar
clang-format committed
672
                         dec_flags)) {
673
    fprintf(stderr, "Failed to initialize decoder: %s\n",
Adrian Grange's avatar
Adrian Grange committed
674
            aom_codec_error(&decoder));
John Koleszar's avatar
John Koleszar committed
675 676
    return EXIT_FAILURE;
  }
John Koleszar's avatar
John Koleszar committed
677

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

763
    if (!noblit && img) {
Adrian Grange's avatar
Adrian Grange committed
764 765
      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 };
766
      const int *planes = flipuv ? PLANES_YVU : PLANES_YUV;
767

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

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

839
      if (single_file) {
840
        if (use_y4m) {
841
          char y4m_buf[Y4M_BUFFER_SIZE] = { 0 };
842
          size_t len = 0;
Adrian Grange's avatar
Adrian Grange committed
843
          if (img->fmt == AOM_IMG_FMT_I440 || img->fmt == AOM_IMG_FMT_I44016) {
Deb Mukherjee's avatar
Deb Mukherjee committed
844 845 846
            fprintf(stderr, "Cannot produce y4m output for 440 sampling.\n");
            goto fail;
          }
847 848
          if (frame_out == 1) {
            // Y4M file header
clang-format's avatar
clang-format committed
849
            len = y4m_write_file_header(
850 851 852
                y4m_buf, sizeof(y4m_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 *)y4m_buf, (unsigned int)len);
855
            } else {
856
              fputs(y4m_buf, outfile);
857 858 859 860
            }
          }

          // Y4M frame header
861
          len = y4m_write_frame_header(y4m_buf, sizeof(y4m_buf));
862
          if (do_md5) {
863
            MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len);