1/*
2 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  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.
9 */
10
11#include <assert.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stdarg.h>
15#include <string.h>
16#include <limits.h>
17
18#include "./vpx_config.h"
19
20#if CONFIG_LIBYUV
21#include "third_party/libyuv/include/libyuv/scale.h"
22#endif
23
24#include "./args.h"
25#include "./ivfdec.h"
26
27#include "vpx/vpx_decoder.h"
28#include "vpx_ports/mem_ops.h"
29#include "vpx_ports/vpx_timer.h"
30
31#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
32#include "vpx/vp8dx.h"
33#endif
34
35#include "./md5_utils.h"
36
37#include "./tools_common.h"
38#if CONFIG_WEBM_IO
39#include "./webmdec.h"
40#endif
41#include "./y4menc.h"
42
43static const char *exec_name;
44
45struct VpxDecInputContext {
46  struct VpxInputContext *vpx_input_ctx;
47  struct WebmInputContext *webm_ctx;
48};
49
50static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1,
51                                          "Number of times to decode the file");
52static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
53                                          "Codec to use");
54static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
55                                          "Output raw YV12 frames");
56static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
57                                          "Output raw I420 frames");
58static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
59                                           "Flip the chroma planes in the output");
60static const arg_def_t rawvideo = ARG_DEF(NULL, "rawvideo", 0,
61                                          "Output raw YUV frames");
62static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
63                                           "Don't process the decoded frames");
64static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
65                                             "Show progress after each frame decodes");
66static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
67                                          "Stop decoding after n frames");
68static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1,
69                                         "Skip the first n input frames");
70static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
71                                             "Postprocess decoded frames");
72static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
73                                            "Show timing summary");
74static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
75                                            "Output file name pattern (see below)");
76static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
77                                            "Max threads to use");
78static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
79                                            "Show version string");
80static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0,
81                                                   "Enable decoder error-concealment");
82static const arg_def_t scalearg = ARG_DEF("S", "scale", 0,
83                                            "Scale output frames uniformly");
84static const arg_def_t continuearg =
85    ARG_DEF("k", "keep-going", 0, "(debug) Continue decoding after error");
86
87static const arg_def_t fb_arg =
88    ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use");
89
90static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
91                                        "Compute the MD5 sum of the decoded frame");
92#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
93static const arg_def_t outbitdeptharg = ARG_DEF(
94    NULL, "output-bit-depth", 1,
95    "Output bit-depth for decoded frames");
96#endif
97
98static const arg_def_t *all_args[] = {
99  &codecarg, &use_yv12, &use_i420, &flipuvarg, &rawvideo, &noblitarg,
100  &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
101  &threadsarg, &verbosearg, &scalearg, &fb_arg,
102  &md5arg, &error_concealment, &continuearg,
103#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
104  &outbitdeptharg,
105#endif
106  NULL
107};
108
109#if CONFIG_VP8_DECODER
110static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
111                                                "Enable VP8 postproc add noise");
112static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
113                                         "Enable VP8 deblocking");
114static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
115                                                    "Enable VP8 demacroblocking, w/ level");
116static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
117                                               "Enable VP8 visible debug info");
118static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1,
119                                                   "Display only selected reference frame per macro block");
120static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1,
121                                                  "Display only selected macro block modes");
122static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1,
123                                                 "Display only selected block modes");
124static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1,
125                                             "Draw only selected motion vectors");
126static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0,
127                                      "Enable multiframe quality enhancement");
128
129static const arg_def_t *vp8_pp_args[] = {
130  &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
131  &pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe,
132  NULL
133};
134#endif
135
136#if CONFIG_LIBYUV
137static INLINE int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst,
138                                  FilterModeEnum mode) {
139#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
140  if (src->fmt == VPX_IMG_FMT_I42016) {
141    assert(dst->fmt == VPX_IMG_FMT_I42016);
142    return I420Scale_16((uint16_t*)src->planes[VPX_PLANE_Y],
143                        src->stride[VPX_PLANE_Y]/2,
144                        (uint16_t*)src->planes[VPX_PLANE_U],
145                        src->stride[VPX_PLANE_U]/2,
146                        (uint16_t*)src->planes[VPX_PLANE_V],
147                        src->stride[VPX_PLANE_V]/2,
148                        src->d_w, src->d_h,
149                        (uint16_t*)dst->planes[VPX_PLANE_Y],
150                        dst->stride[VPX_PLANE_Y]/2,
151                        (uint16_t*)dst->planes[VPX_PLANE_U],
152                        dst->stride[VPX_PLANE_U]/2,
153                        (uint16_t*)dst->planes[VPX_PLANE_V],
154                        dst->stride[VPX_PLANE_V]/2,
155                        dst->d_w, dst->d_h,
156                        mode);
157  }
158#endif
159  assert(src->fmt == VPX_IMG_FMT_I420);
160  assert(dst->fmt == VPX_IMG_FMT_I420);
161  return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y],
162                   src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U],
163                   src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V],
164                   src->d_w, src->d_h,
165                   dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y],
166                   dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U],
167                   dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V],
168                   dst->d_w, dst->d_h,
169                   mode);
170}
171#endif
172
173void usage_exit() {
174  int i;
175
176  fprintf(stderr, "Usage: %s <options> filename\n\n"
177          "Options:\n", exec_name);
178  arg_show_usage(stderr, all_args);
179#if CONFIG_VP8_DECODER
180  fprintf(stderr, "\nVP8 Postprocessing Options:\n");
181  arg_show_usage(stderr, vp8_pp_args);
182#endif
183  fprintf(stderr,
184          "\nOutput File Patterns:\n\n"
185          "  The -o argument specifies the name of the file(s) to "
186          "write to. If the\n  argument does not include any escape "
187          "characters, the output will be\n  written to a single file. "
188          "Otherwise, the filename will be calculated by\n  expanding "
189          "the following escape characters:\n");
190  fprintf(stderr,
191          "\n\t%%w   - Frame width"
192          "\n\t%%h   - Frame height"
193          "\n\t%%<n> - Frame number, zero padded to <n> places (1..9)"
194          "\n\n  Pattern arguments are only supported in conjunction "
195          "with the --yv12 and\n  --i420 options. If the -o option is "
196          "not specified, the output will be\n  directed to stdout.\n"
197         );
198  fprintf(stderr, "\nIncluded decoders:\n\n");
199
200  for (i = 0; i < get_vpx_decoder_count(); ++i) {
201    const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
202    fprintf(stderr, "    %-6s - %s\n",
203            decoder->name, vpx_codec_iface_name(decoder->codec_interface()));
204  }
205
206  exit(EXIT_FAILURE);
207}
208
209static int raw_read_frame(FILE *infile, uint8_t **buffer,
210                          size_t *bytes_read, size_t *buffer_size) {
211  char raw_hdr[RAW_FRAME_HDR_SZ];
212  size_t frame_size = 0;
213
214  if (fread(raw_hdr, RAW_FRAME_HDR_SZ, 1, infile) != 1) {
215    if (!feof(infile))
216      warn("Failed to read RAW frame size\n");
217  } else {
218    const size_t kCorruptFrameThreshold = 256 * 1024 * 1024;
219    const size_t kFrameTooSmallThreshold = 256 * 1024;
220    frame_size = mem_get_le32(raw_hdr);
221
222    if (frame_size > kCorruptFrameThreshold) {
223      warn("Read invalid frame size (%u)\n", (unsigned int)frame_size);
224      frame_size = 0;
225    }
226
227    if (frame_size < kFrameTooSmallThreshold) {
228      warn("Warning: Read invalid frame size (%u) - not a raw file?\n",
229           (unsigned int)frame_size);
230    }
231
232    if (frame_size > *buffer_size) {
233      uint8_t *new_buf = realloc(*buffer, 2 * frame_size);
234      if (new_buf) {
235        *buffer = new_buf;
236        *buffer_size = 2 * frame_size;
237      } else {
238        warn("Failed to allocate compressed data buffer\n");
239        frame_size = 0;
240      }
241    }
242  }
243
244  if (!feof(infile)) {
245    if (fread(*buffer, 1, frame_size, infile) != frame_size) {
246      warn("Failed to read full frame\n");
247      return 1;
248    }
249    *bytes_read = frame_size;
250  }
251
252  return 0;
253}
254
255static int read_frame(struct VpxDecInputContext *input, uint8_t **buf,
256                      size_t *bytes_in_buffer, size_t *buffer_size) {
257  switch (input->vpx_input_ctx->file_type) {
258#if CONFIG_WEBM_IO
259    case FILE_TYPE_WEBM:
260      return webm_read_frame(input->webm_ctx,
261                             buf, bytes_in_buffer, buffer_size);
262#endif
263    case FILE_TYPE_RAW:
264      return raw_read_frame(input->vpx_input_ctx->file,
265                            buf, bytes_in_buffer, buffer_size);
266    case FILE_TYPE_IVF:
267      return ivf_read_frame(input->vpx_input_ctx->file,
268                            buf, bytes_in_buffer, buffer_size);
269    default:
270      return 1;
271  }
272}
273
274static void update_image_md5(const vpx_image_t *img, const int planes[3],
275                             MD5Context *md5) {
276  int i, y;
277
278  for (i = 0; i < 3; ++i) {
279    const int plane = planes[i];
280    const unsigned char *buf = img->planes[plane];
281    const int stride = img->stride[plane];
282    const int w = vpx_img_plane_width(img, plane);
283    const int h = vpx_img_plane_height(img, plane);
284
285    for (y = 0; y < h; ++y) {
286      MD5Update(md5, buf, w);
287      buf += stride;
288    }
289  }
290}
291
292static void write_image_file(const vpx_image_t *img, const int planes[3],
293                             FILE *file) {
294  int i, y;
295#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
296  const int bytes_per_sample = ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
297#else
298  const int bytes_per_sample = 1;
299#endif
300
301  for (i = 0; i < 3; ++i) {
302    const int plane = planes[i];
303    const unsigned char *buf = img->planes[plane];
304    const int stride = img->stride[plane];
305    const int w = vpx_img_plane_width(img, plane);
306    const int h = vpx_img_plane_height(img, plane);
307
308    for (y = 0; y < h; ++y) {
309      fwrite(buf, bytes_per_sample, w, file);
310      buf += stride;
311    }
312  }
313}
314
315int file_is_raw(struct VpxInputContext *input) {
316  uint8_t buf[32];
317  int is_raw = 0;
318  vpx_codec_stream_info_t si;
319
320  si.sz = sizeof(si);
321
322  if (fread(buf, 1, 32, input->file) == 32) {
323    int i;
324
325    if (mem_get_le32(buf) < 256 * 1024 * 1024) {
326      for (i = 0; i < get_vpx_decoder_count(); ++i) {
327        const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
328        if (!vpx_codec_peek_stream_info(decoder->codec_interface(),
329                                        buf + 4, 32 - 4, &si)) {
330          is_raw = 1;
331          input->fourcc = decoder->fourcc;
332          input->width = si.w;
333          input->height = si.h;
334          input->framerate.numerator = 30;
335          input->framerate.denominator = 1;
336          break;
337        }
338      }
339    }
340  }
341
342  rewind(input->file);
343  return is_raw;
344}
345
346void show_progress(int frame_in, int frame_out, uint64_t dx_time) {
347  fprintf(stderr,
348          "%d decoded frames/%d showed frames in %"PRId64" us (%.2f fps)\r",
349          frame_in, frame_out, dx_time,
350          (double)frame_out * 1000000.0 / (double)dx_time);
351}
352
353struct ExternalFrameBuffer {
354  uint8_t* data;
355  size_t size;
356  int in_use;
357};
358
359struct ExternalFrameBufferList {
360  int num_external_frame_buffers;
361  struct ExternalFrameBuffer *ext_fb;
362};
363
364// Callback used by libvpx to request an external frame buffer. |cb_priv|
365// Application private data passed into the set function. |min_size| is the
366// minimum size in bytes needed to decode the next frame. |fb| pointer to the
367// frame buffer.
368int get_vp9_frame_buffer(void *cb_priv, size_t min_size,
369                         vpx_codec_frame_buffer_t *fb) {
370  int i;
371  struct ExternalFrameBufferList *const ext_fb_list =
372      (struct ExternalFrameBufferList *)cb_priv;
373  if (ext_fb_list == NULL)
374    return -1;
375
376  // Find a free frame buffer.
377  for (i = 0; i < ext_fb_list->num_external_frame_buffers; ++i) {
378    if (!ext_fb_list->ext_fb[i].in_use)
379      break;
380  }
381
382  if (i == ext_fb_list->num_external_frame_buffers)
383    return -1;
384
385  if (ext_fb_list->ext_fb[i].size < min_size) {
386    free(ext_fb_list->ext_fb[i].data);
387    ext_fb_list->ext_fb[i].data = (uint8_t *)calloc(min_size, sizeof(uint8_t));
388    if (!ext_fb_list->ext_fb[i].data)
389      return -1;
390
391    ext_fb_list->ext_fb[i].size = min_size;
392  }
393
394  fb->data = ext_fb_list->ext_fb[i].data;
395  fb->size = ext_fb_list->ext_fb[i].size;
396  ext_fb_list->ext_fb[i].in_use = 1;
397
398  // Set the frame buffer's private data to point at the external frame buffer.
399  fb->priv = &ext_fb_list->ext_fb[i];
400  return 0;
401}
402
403// Callback used by libvpx when there are no references to the frame buffer.
404// |cb_priv| user private data passed into the set function. |fb| pointer
405// to the frame buffer.
406int release_vp9_frame_buffer(void *cb_priv,
407                             vpx_codec_frame_buffer_t *fb) {
408  struct ExternalFrameBuffer *const ext_fb =
409      (struct ExternalFrameBuffer *)fb->priv;
410  (void)cb_priv;
411  ext_fb->in_use = 0;
412  return 0;
413}
414
415void generate_filename(const char *pattern, char *out, size_t q_len,
416                       unsigned int d_w, unsigned int d_h,
417                       unsigned int frame_in) {
418  const char *p = pattern;
419  char *q = out;
420
421  do {
422    char *next_pat = strchr(p, '%');
423
424    if (p == next_pat) {
425      size_t pat_len;
426
427      /* parse the pattern */
428      q[q_len - 1] = '\0';
429      switch (p[1]) {
430        case 'w':
431          snprintf(q, q_len - 1, "%d", d_w);
432          break;
433        case 'h':
434          snprintf(q, q_len - 1, "%d", d_h);
435          break;
436        case '1':
437          snprintf(q, q_len - 1, "%d", frame_in);
438          break;
439        case '2':
440          snprintf(q, q_len - 1, "%02d", frame_in);
441          break;
442        case '3':
443          snprintf(q, q_len - 1, "%03d", frame_in);
444          break;
445        case '4':
446          snprintf(q, q_len - 1, "%04d", frame_in);
447          break;
448        case '5':
449          snprintf(q, q_len - 1, "%05d", frame_in);
450          break;
451        case '6':
452          snprintf(q, q_len - 1, "%06d", frame_in);
453          break;
454        case '7':
455          snprintf(q, q_len - 1, "%07d", frame_in);
456          break;
457        case '8':
458          snprintf(q, q_len - 1, "%08d", frame_in);
459          break;
460        case '9':
461          snprintf(q, q_len - 1, "%09d", frame_in);
462          break;
463        default:
464          die("Unrecognized pattern %%%c\n", p[1]);
465          break;
466      }
467
468      pat_len = strlen(q);
469      if (pat_len >= q_len - 1)
470        die("Output filename too long.\n");
471      q += pat_len;
472      p += 2;
473      q_len -= pat_len;
474    } else {
475      size_t copy_len;
476
477      /* copy the next segment */
478      if (!next_pat)
479        copy_len = strlen(p);
480      else
481        copy_len = next_pat - p;
482
483      if (copy_len >= q_len - 1)
484        die("Output filename too long.\n");
485
486      memcpy(q, p, copy_len);
487      q[copy_len] = '\0';
488      q += copy_len;
489      p += copy_len;
490      q_len -= copy_len;
491    }
492  } while (*p);
493}
494
495static int is_single_file(const char *outfile_pattern) {
496  const char *p = outfile_pattern;
497
498  do {
499    p = strchr(p, '%');
500    if (p && p[1] >= '1' && p[1] <= '9')
501      return 0;  // pattern contains sequence number, so it's not unique
502    if (p)
503      p++;
504  } while (p);
505
506  return 1;
507}
508
509static void print_md5(unsigned char digest[16], const char *filename) {
510  int i;
511
512  for (i = 0; i < 16; ++i)
513    printf("%02x", digest[i]);
514  printf("  %s\n", filename);
515}
516
517static FILE *open_outfile(const char *name) {
518  if (strcmp("-", name) == 0) {
519    set_binary_mode(stdout);
520    return stdout;
521  } else {
522    FILE *file = fopen(name, "wb");
523    if (!file)
524      fatal("Failed to output file %s", name);
525    return file;
526  }
527}
528
529#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
530static void high_img_upshift(vpx_image_t *dst, vpx_image_t *src,
531                             int input_shift) {
532  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
533  int plane;
534  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
535      dst->x_chroma_shift != src->x_chroma_shift ||
536      dst->y_chroma_shift != src->y_chroma_shift ||
537      dst->fmt != src->fmt || input_shift < 0) {
538    fatal("Unsupported image conversion");
539  }
540  switch (src->fmt) {
541    case VPX_IMG_FMT_I42016:
542    case VPX_IMG_FMT_I42216:
543    case VPX_IMG_FMT_I44416:
544      break;
545    default:
546      fatal("Unsupported image conversion");
547      break;
548  }
549  for (plane = 0; plane < 3; plane++) {
550    int w = src->d_w;
551    int h = src->d_h;
552    int x, y;
553    if (plane) {
554      w >>= src->x_chroma_shift;
555      h >>= src->y_chroma_shift;
556    }
557    for (y = 0; y < h; y++) {
558      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
559                                     y * src->stride[plane]);
560      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
561                                     y * dst->stride[plane]);
562      for (x = 0; x < w; x++)
563        *p_dst++ = (*p_src++ << input_shift) + offset;
564    }
565  }
566}
567
568static void low_img_upshift(vpx_image_t *dst, vpx_image_t *src,
569                            int input_shift) {
570  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
571  int plane;
572  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
573      dst->x_chroma_shift != src->x_chroma_shift ||
574      dst->y_chroma_shift != src->y_chroma_shift ||
575      dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
576      input_shift < 0) {
577    fatal("Unsupported image conversion");
578  }
579  switch (src->fmt) {
580    case VPX_IMG_FMT_I420:
581    case VPX_IMG_FMT_I422:
582    case VPX_IMG_FMT_I444:
583      break;
584    default:
585      fatal("Unsupported image conversion");
586      break;
587  }
588  for (plane = 0; plane < 3; plane++) {
589    int w = src->d_w;
590    int h = src->d_h;
591    int x, y;
592    if (plane) {
593      w >>= src->x_chroma_shift;
594      h >>= src->y_chroma_shift;
595    }
596    for (y = 0; y < h; y++) {
597      uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
598      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
599                                     y * dst->stride[plane]);
600      for (x = 0; x < w; x++) {
601        *p_dst++ = (*p_src++ << input_shift) + offset;
602      }
603    }
604  }
605}
606
607static void img_upshift(vpx_image_t *dst, vpx_image_t *src,
608                        int input_shift) {
609  if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
610    high_img_upshift(dst, src, input_shift);
611  } else {
612    low_img_upshift(dst, src, input_shift);
613  }
614}
615
616static void high_img_downshift(vpx_image_t *dst, vpx_image_t *src,
617                               int down_shift) {
618  int plane;
619  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
620      dst->x_chroma_shift != src->x_chroma_shift ||
621      dst->y_chroma_shift != src->y_chroma_shift ||
622      dst->fmt != src->fmt || down_shift < 0) {
623    fatal("Unsupported image conversion");
624  }
625  switch (src->fmt) {
626    case VPX_IMG_FMT_I42016:
627    case VPX_IMG_FMT_I42216:
628    case VPX_IMG_FMT_I44416:
629      break;
630    default:
631      fatal("Unsupported image conversion");
632      break;
633  }
634  for (plane = 0; plane < 3; plane++) {
635    int w = src->d_w;
636    int h = src->d_h;
637    int x, y;
638    if (plane) {
639      w >>= src->x_chroma_shift;
640      h >>= src->y_chroma_shift;
641    }
642    for (y = 0; y < h; y++) {
643      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
644                                     y * src->stride[plane]);
645      uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
646                                     y * dst->stride[plane]);
647      for (x = 0; x < w; x++)
648        *p_dst++ = *p_src++ >> down_shift;
649    }
650  }
651}
652
653static void low_img_downshift(vpx_image_t *dst, vpx_image_t *src,
654                            int down_shift) {
655  int plane;
656  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
657      dst->x_chroma_shift != src->x_chroma_shift ||
658      dst->y_chroma_shift != src->y_chroma_shift ||
659      src->fmt != dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
660      down_shift < 0) {
661    fatal("Unsupported image conversion");
662  }
663  switch (dst->fmt) {
664    case VPX_IMG_FMT_I420:
665    case VPX_IMG_FMT_I422:
666    case VPX_IMG_FMT_I444:
667      break;
668    default:
669      fatal("Unsupported image conversion");
670      break;
671  }
672  for (plane = 0; plane < 3; plane++) {
673    int w = src->d_w;
674    int h = src->d_h;
675    int x, y;
676    if (plane) {
677      w >>= src->x_chroma_shift;
678      h >>= src->y_chroma_shift;
679    }
680    for (y = 0; y < h; y++) {
681      uint16_t *p_src = (uint16_t *)(src->planes[plane] +
682                                     y * src->stride[plane]);
683      uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
684      for (x = 0; x < w; x++) {
685        *p_dst++ = *p_src++ >> down_shift;
686      }
687    }
688  }
689}
690
691static void img_downshift(vpx_image_t *dst, vpx_image_t *src,
692                          int down_shift) {
693  if (dst->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
694    high_img_downshift(dst, src, down_shift);
695  } else {
696    low_img_downshift(dst, src, down_shift);
697  }
698}
699#endif
700
701int main_loop(int argc, const char **argv_) {
702  vpx_codec_ctx_t       decoder;
703  char                  *fn = NULL;
704  int                    i;
705  uint8_t               *buf = NULL;
706  size_t                 bytes_in_buffer = 0, buffer_size = 0;
707  FILE                  *infile;
708  int                    frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0;
709  int                    do_md5 = 0, progress = 0;
710  int                    stop_after = 0, postproc = 0, summary = 0, quiet = 1;
711  int                    arg_skip = 0;
712  int                    ec_enabled = 0;
713  int                    keep_going = 0;
714  const VpxInterface *interface = NULL;
715  const VpxInterface *fourcc_interface = NULL;
716  uint64_t dx_time = 0;
717  struct arg               arg;
718  char                   **argv, **argi, **argj;
719
720  int                     single_file;
721  int                     use_y4m = 1;
722  int                     opt_yv12 = 0;
723  int                     opt_i420 = 0;
724  vpx_codec_dec_cfg_t     cfg = {0, 0, 0};
725#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
726  int                     output_bit_depth = 0;
727#endif
728#if CONFIG_VP8_DECODER
729  vp8_postproc_cfg_t      vp8_pp_cfg = {0};
730  int                     vp8_dbg_color_ref_frame = 0;
731  int                     vp8_dbg_color_mb_modes = 0;
732  int                     vp8_dbg_color_b_modes = 0;
733  int                     vp8_dbg_display_mv = 0;
734#endif
735  int                     frames_corrupted = 0;
736  int                     dec_flags = 0;
737  int                     do_scale = 0;
738  vpx_image_t             *scaled_img = NULL;
739#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
740  vpx_image_t             *img_shifted = NULL;
741#endif
742  int                     frame_avail, got_data;
743  int                     num_external_frame_buffers = 0;
744  struct ExternalFrameBufferList ext_fb_list = {0, NULL};
745
746  const char *outfile_pattern = NULL;
747  char outfile_name[PATH_MAX] = {0};
748  FILE *outfile = NULL;
749
750  MD5Context md5_ctx;
751  unsigned char md5_digest[16];
752
753  struct VpxDecInputContext input = {NULL, NULL};
754  struct VpxInputContext vpx_input_ctx;
755#if CONFIG_WEBM_IO
756  struct WebmInputContext webm_ctx;
757  memset(&(webm_ctx), 0, sizeof(webm_ctx));
758  input.webm_ctx = &webm_ctx;
759#endif
760  input.vpx_input_ctx = &vpx_input_ctx;
761
762  /* Parse command line */
763  exec_name = argv_[0];
764  argv = argv_dup(argc - 1, argv_ + 1);
765
766  for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
767    memset(&arg, 0, sizeof(arg));
768    arg.argv_step = 1;
769
770    if (arg_match(&arg, &codecarg, argi)) {
771      interface = get_vpx_decoder_by_name(arg.val);
772      if (!interface)
773        die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
774    } else if (arg_match(&arg, &looparg, argi)) {
775      // no-op
776    } else if (arg_match(&arg, &outputfile, argi))
777      outfile_pattern = arg.val;
778    else if (arg_match(&arg, &use_yv12, argi)) {
779      use_y4m = 0;
780      flipuv = 1;
781      opt_yv12 = 1;
782#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
783      output_bit_depth = 8;  // For yv12 8-bit depth output is assumed
784#endif
785    } else if (arg_match(&arg, &use_i420, argi)) {
786      use_y4m = 0;
787      flipuv = 0;
788      opt_i420 = 1;
789    } else if (arg_match(&arg, &rawvideo, argi)) {
790      use_y4m = 0;
791    } else if (arg_match(&arg, &flipuvarg, argi))
792      flipuv = 1;
793    else if (arg_match(&arg, &noblitarg, argi))
794      noblit = 1;
795    else if (arg_match(&arg, &progressarg, argi))
796      progress = 1;
797    else if (arg_match(&arg, &limitarg, argi))
798      stop_after = arg_parse_uint(&arg);
799    else if (arg_match(&arg, &skiparg, argi))
800      arg_skip = arg_parse_uint(&arg);
801    else if (arg_match(&arg, &postprocarg, argi))
802      postproc = 1;
803    else if (arg_match(&arg, &md5arg, argi))
804      do_md5 = 1;
805    else if (arg_match(&arg, &summaryarg, argi))
806      summary = 1;
807    else if (arg_match(&arg, &threadsarg, argi))
808      cfg.threads = arg_parse_uint(&arg);
809    else if (arg_match(&arg, &verbosearg, argi))
810      quiet = 0;
811    else if (arg_match(&arg, &scalearg, argi))
812      do_scale = 1;
813    else if (arg_match(&arg, &fb_arg, argi))
814      num_external_frame_buffers = arg_parse_uint(&arg);
815    else if (arg_match(&arg, &continuearg, argi))
816      keep_going = 1;
817#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
818    else if (arg_match(&arg, &outbitdeptharg, argi)) {
819      output_bit_depth = arg_parse_uint(&arg);
820    }
821#endif
822#if CONFIG_VP8_DECODER
823    else if (arg_match(&arg, &addnoise_level, argi)) {
824      postproc = 1;
825      vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE;
826      vp8_pp_cfg.noise_level = arg_parse_uint(&arg);
827    } else if (arg_match(&arg, &demacroblock_level, argi)) {
828      postproc = 1;
829      vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK;
830      vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg);
831    } else if (arg_match(&arg, &deblock, argi)) {
832      postproc = 1;
833      vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK;
834    } else if (arg_match(&arg, &mfqe, argi)) {
835      postproc = 1;
836      vp8_pp_cfg.post_proc_flag |= VP8_MFQE;
837    } else if (arg_match(&arg, &pp_debug_info, argi)) {
838      unsigned int level = arg_parse_uint(&arg);
839
840      postproc = 1;
841      vp8_pp_cfg.post_proc_flag &= ~0x7;
842
843      if (level)
844        vp8_pp_cfg.post_proc_flag |= level;
845    } else if (arg_match(&arg, &pp_disp_ref_frame, argi)) {
846      unsigned int flags = arg_parse_int(&arg);
847      if (flags) {
848        postproc = 1;
849        vp8_dbg_color_ref_frame = flags;
850      }
851    } else if (arg_match(&arg, &pp_disp_mb_modes, argi)) {
852      unsigned int flags = arg_parse_int(&arg);
853      if (flags) {
854        postproc = 1;
855        vp8_dbg_color_mb_modes = flags;
856      }
857    } else if (arg_match(&arg, &pp_disp_b_modes, argi)) {
858      unsigned int flags = arg_parse_int(&arg);
859      if (flags) {
860        postproc = 1;
861        vp8_dbg_color_b_modes = flags;
862      }
863    } else if (arg_match(&arg, &pp_disp_mvs, argi)) {
864      unsigned int flags = arg_parse_int(&arg);
865      if (flags) {
866        postproc = 1;
867        vp8_dbg_display_mv = flags;
868      }
869    } else if (arg_match(&arg, &error_concealment, argi)) {
870      ec_enabled = 1;
871    }
872#endif  // CONFIG_VP8_DECODER
873    else
874      argj++;
875  }
876
877  /* Check for unrecognized options */
878  for (argi = argv; *argi; argi++)
879    if (argi[0][0] == '-' && strlen(argi[0]) > 1)
880      die("Error: Unrecognized option %s\n", *argi);
881
882  /* Handle non-option arguments */
883  fn = argv[0];
884
885  if (!fn)
886    usage_exit();
887
888  /* Open file */
889  infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin);
890
891  if (!infile) {
892    fprintf(stderr, "Failed to open file '%s'", strcmp(fn, "-") ? fn : "stdin");
893    return EXIT_FAILURE;
894  }
895#if CONFIG_OS_SUPPORT
896  /* Make sure we don't dump to the terminal, unless forced to with -o - */
897  if (!outfile_pattern && isatty(fileno(stdout)) && !do_md5 && !noblit) {
898    fprintf(stderr,
899            "Not dumping raw video to your terminal. Use '-o -' to "
900            "override.\n");
901    return EXIT_FAILURE;
902  }
903#endif
904  input.vpx_input_ctx->file = infile;
905  if (file_is_ivf(input.vpx_input_ctx))
906    input.vpx_input_ctx->file_type = FILE_TYPE_IVF;
907#if CONFIG_WEBM_IO
908  else if (file_is_webm(input.webm_ctx, input.vpx_input_ctx))
909    input.vpx_input_ctx->file_type = FILE_TYPE_WEBM;
910#endif
911  else if (file_is_raw(input.vpx_input_ctx))
912    input.vpx_input_ctx->file_type = FILE_TYPE_RAW;
913  else {
914    fprintf(stderr, "Unrecognized input file type.\n");
915#if !CONFIG_WEBM_IO
916    fprintf(stderr, "vpxdec was built without WebM container support.\n");
917#endif
918    return EXIT_FAILURE;
919  }
920
921  outfile_pattern = outfile_pattern ? outfile_pattern : "-";
922  single_file = is_single_file(outfile_pattern);
923
924  if (!noblit && single_file) {
925    generate_filename(outfile_pattern, outfile_name, PATH_MAX,
926                      vpx_input_ctx.width, vpx_input_ctx.height, 0);
927    if (do_md5)
928      MD5Init(&md5_ctx);
929    else
930      outfile = open_outfile(outfile_name);
931  }
932
933  if (use_y4m && !noblit) {
934    if (!single_file) {
935      fprintf(stderr, "YUV4MPEG2 not supported with output patterns,"
936              " try --i420 or --yv12.\n");
937      return EXIT_FAILURE;
938    }
939
940#if CONFIG_WEBM_IO
941    if (vpx_input_ctx.file_type == FILE_TYPE_WEBM) {
942      if (webm_guess_framerate(input.webm_ctx, input.vpx_input_ctx)) {
943        fprintf(stderr, "Failed to guess framerate -- error parsing "
944                "webm file?\n");
945        return EXIT_FAILURE;
946      }
947    }
948#endif
949  }
950
951  fourcc_interface = get_vpx_decoder_by_fourcc(vpx_input_ctx.fourcc);
952  if (interface && fourcc_interface && interface != fourcc_interface)
953    warn("Header indicates codec: %s\n", fourcc_interface->name);
954  else
955    interface = fourcc_interface;
956
957  if (!interface)
958    interface = get_vpx_decoder_by_index(0);
959
960  dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) |
961              (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0);
962  if (vpx_codec_dec_init(&decoder, interface->codec_interface(),
963                         &cfg, dec_flags)) {
964    fprintf(stderr, "Failed to initialize decoder: %s\n",
965            vpx_codec_error(&decoder));
966    return EXIT_FAILURE;
967  }
968
969  if (!quiet)
970    fprintf(stderr, "%s\n", decoder.name);
971
972#if CONFIG_VP8_DECODER
973
974  if (vp8_pp_cfg.post_proc_flag
975      && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) {
976    fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder));
977    return EXIT_FAILURE;
978  }
979
980  if (vp8_dbg_color_ref_frame
981      && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_REF_FRAME, vp8_dbg_color_ref_frame)) {
982    fprintf(stderr, "Failed to configure reference block visualizer: %s\n", vpx_codec_error(&decoder));
983    return EXIT_FAILURE;
984  }
985
986  if (vp8_dbg_color_mb_modes
987      && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_MB_MODES, vp8_dbg_color_mb_modes)) {
988    fprintf(stderr, "Failed to configure macro block visualizer: %s\n", vpx_codec_error(&decoder));
989    return EXIT_FAILURE;
990  }
991
992  if (vp8_dbg_color_b_modes
993      && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_B_MODES, vp8_dbg_color_b_modes)) {
994    fprintf(stderr, "Failed to configure block visualizer: %s\n", vpx_codec_error(&decoder));
995    return EXIT_FAILURE;
996  }
997
998  if (vp8_dbg_display_mv
999      && vpx_codec_control(&decoder, VP8_SET_DBG_DISPLAY_MV, vp8_dbg_display_mv)) {
1000    fprintf(stderr, "Failed to configure motion vector visualizer: %s\n", vpx_codec_error(&decoder));
1001    return EXIT_FAILURE;
1002  }
1003#endif
1004
1005
1006  if (arg_skip)
1007    fprintf(stderr, "Skipping first %d frames.\n", arg_skip);
1008  while (arg_skip) {
1009    if (read_frame(&input, &buf, &bytes_in_buffer, &buffer_size))
1010      break;
1011    arg_skip--;
1012  }
1013
1014  if (num_external_frame_buffers > 0) {
1015    ext_fb_list.num_external_frame_buffers = num_external_frame_buffers;
1016    ext_fb_list.ext_fb = (struct ExternalFrameBuffer *)calloc(
1017        num_external_frame_buffers, sizeof(*ext_fb_list.ext_fb));
1018    if (vpx_codec_set_frame_buffer_functions(
1019            &decoder, get_vp9_frame_buffer, release_vp9_frame_buffer,
1020            &ext_fb_list)) {
1021      fprintf(stderr, "Failed to configure external frame buffers: %s\n",
1022              vpx_codec_error(&decoder));
1023      return EXIT_FAILURE;
1024    }
1025  }
1026
1027  frame_avail = 1;
1028  got_data = 0;
1029
1030  /* Decode file */
1031  while (frame_avail || got_data) {
1032    vpx_codec_iter_t  iter = NULL;
1033    vpx_image_t    *img;
1034    struct vpx_usec_timer timer;
1035    int                   corrupted;
1036
1037    frame_avail = 0;
1038    if (!stop_after || frame_in < stop_after) {
1039      if (!read_frame(&input, &buf, &bytes_in_buffer, &buffer_size)) {
1040        frame_avail = 1;
1041        frame_in++;
1042
1043        vpx_usec_timer_start(&timer);
1044
1045        if (vpx_codec_decode(&decoder, buf, (unsigned int)bytes_in_buffer,
1046                             NULL, 0)) {
1047          const char *detail = vpx_codec_error_detail(&decoder);
1048          warn("Failed to decode frame %d: %s",
1049               frame_in, vpx_codec_error(&decoder));
1050
1051          if (detail)
1052            warn("Additional information: %s", detail);
1053          if (!keep_going)
1054            goto fail;
1055        }
1056
1057        vpx_usec_timer_mark(&timer);
1058        dx_time += vpx_usec_timer_elapsed(&timer);
1059      }
1060    }
1061
1062    vpx_usec_timer_start(&timer);
1063
1064    got_data = 0;
1065    if ((img = vpx_codec_get_frame(&decoder, &iter))) {
1066      ++frame_out;
1067      got_data = 1;
1068    }
1069
1070    vpx_usec_timer_mark(&timer);
1071    dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer);
1072
1073    if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) {
1074      warn("Failed VP8_GET_FRAME_CORRUPTED: %s", vpx_codec_error(&decoder));
1075      goto fail;
1076    }
1077    frames_corrupted += corrupted;
1078
1079    if (progress)
1080      show_progress(frame_in, frame_out, dx_time);
1081
1082    if (!noblit && img) {
1083      const int PLANES_YUV[] = {VPX_PLANE_Y, VPX_PLANE_U, VPX_PLANE_V};
1084      const int PLANES_YVU[] = {VPX_PLANE_Y, VPX_PLANE_V, VPX_PLANE_U};
1085      const int *planes = flipuv ? PLANES_YVU : PLANES_YUV;
1086
1087      if (do_scale) {
1088        if (frame_out == 1) {
1089          // If the output frames are to be scaled to a fixed display size then
1090          // use the width and height specified in the container. If either of
1091          // these is set to 0, use the display size set in the first frame
1092          // header. If that is unavailable, use the raw decoded size of the
1093          // first decoded frame.
1094          int display_width = vpx_input_ctx.width;
1095          int display_height = vpx_input_ctx.height;
1096          if (!display_width || !display_height) {
1097            int display_size[2];
1098            if (vpx_codec_control(&decoder, VP9D_GET_DISPLAY_SIZE,
1099                                  display_size)) {
1100              // As last resort use size of first frame as display size.
1101              display_width = img->d_w;
1102              display_height = img->d_h;
1103            } else {
1104              display_width = display_size[0];
1105              display_height = display_size[1];
1106            }
1107          }
1108          scaled_img = vpx_img_alloc(NULL, img->fmt, display_width,
1109                                     display_height, 16);
1110          scaled_img->bit_depth = img->bit_depth;
1111        }
1112
1113        if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) {
1114#if CONFIG_LIBYUV
1115          vpx_image_scale(img, scaled_img, kFilterBox);
1116          img = scaled_img;
1117#else
1118          fprintf(stderr, "Failed  to scale output frame: %s.\n"
1119                  "Scaling is disabled in this configuration. "
1120                  "To enable scaling, configure with --enable-libyuv\n",
1121                  vpx_codec_error(&decoder));
1122          return EXIT_FAILURE;
1123#endif
1124        }
1125      }
1126#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
1127      // Default to codec bit depth if output bit depth not set
1128      if (!output_bit_depth) {
1129        output_bit_depth = img->bit_depth;
1130      }
1131      // Shift up or down if necessary
1132      if (output_bit_depth != img->bit_depth) {
1133        if (!img_shifted) {
1134          if (output_bit_depth == 8) {
1135            img_shifted = vpx_img_alloc(
1136                NULL, img->fmt - VPX_IMG_FMT_HIGHBITDEPTH,
1137                img->d_w, img->d_h, 16);
1138          } else {
1139            img_shifted = vpx_img_alloc(
1140                NULL, img->fmt | VPX_IMG_FMT_HIGHBITDEPTH,
1141                img->d_w, img->d_h, 16);
1142          }
1143          img_shifted->bit_depth = output_bit_depth;
1144        }
1145        if (output_bit_depth > img->bit_depth) {
1146          img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
1147        } else {
1148          img_downshift(img_shifted, img, img->bit_depth - output_bit_depth);
1149        }
1150        img = img_shifted;
1151      }
1152#endif
1153
1154      if (single_file) {
1155        if (use_y4m) {
1156          char buf[Y4M_BUFFER_SIZE] = {0};
1157          size_t len = 0;
1158          if (frame_out == 1) {
1159            // Y4M file header
1160            len = y4m_write_file_header(buf, sizeof(buf),
1161                                        vpx_input_ctx.width,
1162                                        vpx_input_ctx.height,
1163                                        &vpx_input_ctx.framerate,
1164                                        img->fmt, img->bit_depth);
1165            if (do_md5) {
1166              MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
1167            } else {
1168              fputs(buf, outfile);
1169            }
1170          }
1171
1172          // Y4M frame header
1173          len = y4m_write_frame_header(buf, sizeof(buf));
1174          if (do_md5) {
1175            MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len);
1176          } else {
1177            fputs(buf, outfile);
1178          }
1179        } else {
1180          if (frame_out == 1) {
1181            // Check if --yv12 or --i420 options are consistent with the
1182            // bit-stream decoded
1183            if (opt_i420) {
1184              if (img->fmt != VPX_IMG_FMT_I420 &&
1185                  img->fmt != VPX_IMG_FMT_I42016) {
1186                fprintf(stderr, "Cannot produce i420 output for bit-stream.\n");
1187                goto fail;
1188              }
1189            }
1190            if (opt_yv12) {
1191              if ((img->fmt != VPX_IMG_FMT_I420 &&
1192                   img->fmt != VPX_IMG_FMT_YV12) || img->bit_depth != 8) {
1193                fprintf(stderr, "Cannot produce yv12 output for bit-stream.\n");
1194                goto fail;
1195              }
1196            }
1197          }
1198        }
1199
1200        if (do_md5) {
1201          update_image_md5(img, planes, &md5_ctx);
1202        } else {
1203          write_image_file(img, planes, outfile);
1204        }
1205      } else {
1206        generate_filename(outfile_pattern, outfile_name, PATH_MAX,
1207                          img->d_w, img->d_h, frame_in);
1208        if (do_md5) {
1209          MD5Init(&md5_ctx);
1210          update_image_md5(img, planes, &md5_ctx);
1211          MD5Final(md5_digest, &md5_ctx);
1212          print_md5(md5_digest, outfile_name);
1213        } else {
1214          outfile = open_outfile(outfile_name);
1215          write_image_file(img, planes, outfile);
1216          fclose(outfile);
1217        }
1218      }
1219    }
1220
1221    if (stop_after && frame_in >= stop_after)
1222      break;
1223  }
1224
1225  if (summary || progress) {
1226    show_progress(frame_in, frame_out, dx_time);
1227    fprintf(stderr, "\n");
1228  }
1229
1230  if (frames_corrupted)
1231    fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted);
1232
1233fail:
1234
1235  if (vpx_codec_destroy(&decoder)) {
1236    fprintf(stderr, "Failed to destroy decoder: %s\n",
1237            vpx_codec_error(&decoder));
1238    return EXIT_FAILURE;
1239  }
1240
1241  if (!noblit && single_file) {
1242    if (do_md5) {
1243      MD5Final(md5_digest, &md5_ctx);
1244      print_md5(md5_digest, outfile_name);
1245    } else {
1246      fclose(outfile);
1247    }
1248  }
1249
1250#if CONFIG_WEBM_IO
1251  if (input.vpx_input_ctx->file_type == FILE_TYPE_WEBM)
1252    webm_free(input.webm_ctx);
1253#endif
1254
1255  if (input.vpx_input_ctx->file_type != FILE_TYPE_WEBM)
1256    free(buf);
1257
1258  if (scaled_img) vpx_img_free(scaled_img);
1259#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
1260  if (img_shifted) vpx_img_free(img_shifted);
1261#endif
1262
1263  for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) {
1264    free(ext_fb_list.ext_fb[i].data);
1265  }
1266  free(ext_fb_list.ext_fb);
1267
1268  fclose(infile);
1269  free(argv);
1270
1271  return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS;
1272}
1273
1274int main(int argc, const char **argv_) {
1275  unsigned int loops = 1, i;
1276  char **argv, **argi, **argj;
1277  struct arg arg;
1278  int error = 0;
1279
1280  argv = argv_dup(argc - 1, argv_ + 1);
1281  for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
1282    memset(&arg, 0, sizeof(arg));
1283    arg.argv_step = 1;
1284
1285    if (arg_match(&arg, &looparg, argi)) {
1286      loops = arg_parse_uint(&arg);
1287      break;
1288    }
1289  }
1290  free(argv);
1291  for (i = 0; !error && i < loops; i++)
1292    error = main_loop(argc, argv_);
1293  return error;
1294}
1295