]> granicus.if.org Git - libvpx/blob - vpxdec.c
Two optimizations:
[libvpx] / vpxdec.c
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
12 /* This is a simple program that reads ivf files and decodes them
13  * using the new interface. Decoded frames are output as YV12 raw.
14  */
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <limits.h>
21
22 #define VPX_CODEC_DISABLE_COMPAT 1
23 #include "vpx_config.h"
24 #include "vpx/vpx_decoder.h"
25 #include "vpx_ports/vpx_timer.h"
26 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
27 #include "vpx/vp8dx.h"
28 #endif
29 #if CONFIG_MD5
30 #include "md5_utils.h"
31 #endif
32 #include "tools_common.h"
33 #include "nestegg/include/nestegg/nestegg.h"
34 #include "third_party/libyuv/include/libyuv/scale.h"
35
36 #if CONFIG_OS_SUPPORT
37 #if defined(_MSC_VER)
38 #include <io.h>
39 #define snprintf _snprintf
40 #define isatty   _isatty
41 #define fileno   _fileno
42 #else
43 #include <unistd.h>
44 #endif
45 #endif
46
47 #ifndef PATH_MAX
48 #define PATH_MAX 256
49 #endif
50
51 static const char *exec_name;
52
53 #define VP8_FOURCC (0x00385056)
54 #define VP9_FOURCC (0x00395056)
55 static const struct {
56   char const *name;
57   const vpx_codec_iface_t *(*iface)(void);
58   unsigned int             fourcc;
59   unsigned int             fourcc_mask;
60 } ifaces[] = {
61 #if CONFIG_VP8_DECODER
62   {"vp8",  vpx_codec_vp8_dx,   VP8_FOURCC, 0x00FFFFFF},
63 #endif
64 #if CONFIG_VP9_DECODER
65   {"vp9",  vpx_codec_vp9_dx,   VP9_FOURCC, 0x00FFFFFF},
66 #endif
67 };
68
69 #include "args.h"
70 static const arg_def_t looparg = ARG_DEF(NULL, "loops", 1,
71                                           "Number of times to decode the file");
72 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
73                                           "Codec to use");
74 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
75                                           "Output raw YV12 frames");
76 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
77                                           "Output raw I420 frames");
78 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
79                                            "Flip the chroma planes in the output");
80 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
81                                            "Don't process the decoded frames");
82 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
83                                              "Show progress after each frame decodes");
84 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
85                                           "Stop decoding after n frames");
86 static const arg_def_t skiparg = ARG_DEF(NULL, "skip", 1,
87                                          "Skip the first n input frames");
88 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
89                                              "Postprocess decoded frames");
90 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
91                                             "Show timing summary");
92 static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
93                                             "Output file name pattern (see below)");
94 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
95                                             "Max threads to use");
96 static const arg_def_t verbosearg = ARG_DEF("v", "verbose", 0,
97                                             "Show version string");
98 static const arg_def_t error_concealment = ARG_DEF(NULL, "error-concealment", 0,
99                                                    "Enable decoder error-concealment");
100 static const arg_def_t scalearg = ARG_DEF("S", "scale", 0,
101                                             "Scale output frames uniformly");
102
103
104 #if CONFIG_MD5
105 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
106                                         "Compute the MD5 sum of the decoded frame");
107 #endif
108 static const arg_def_t *all_args[] = {
109   &codecarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg,
110   &progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
111   &threadsarg, &verbosearg, &scalearg,
112 #if CONFIG_MD5
113   &md5arg,
114 #endif
115   &error_concealment,
116   NULL
117 };
118
119 #if CONFIG_VP8_DECODER
120 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
121                                                 "Enable VP8 postproc add noise");
122 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
123                                          "Enable VP8 deblocking");
124 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
125                                                     "Enable VP8 demacroblocking, w/ level");
126 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
127                                                "Enable VP8 visible debug info");
128 static const arg_def_t pp_disp_ref_frame = ARG_DEF(NULL, "pp-dbg-ref-frame", 1,
129                                                    "Display only selected reference frame per macro block");
130 static const arg_def_t pp_disp_mb_modes = ARG_DEF(NULL, "pp-dbg-mb-modes", 1,
131                                                   "Display only selected macro block modes");
132 static const arg_def_t pp_disp_b_modes = ARG_DEF(NULL, "pp-dbg-b-modes", 1,
133                                                  "Display only selected block modes");
134 static const arg_def_t pp_disp_mvs = ARG_DEF(NULL, "pp-dbg-mvs", 1,
135                                              "Draw only selected motion vectors");
136 static const arg_def_t mfqe = ARG_DEF(NULL, "mfqe", 0,
137                                       "Enable multiframe quality enhancement");
138
139 static const arg_def_t *vp8_pp_args[] = {
140   &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
141   &pp_disp_ref_frame, &pp_disp_mb_modes, &pp_disp_b_modes, &pp_disp_mvs, &mfqe,
142   NULL
143 };
144 #endif
145
146 static void usage_exit() {
147   int i;
148
149   fprintf(stderr, "Usage: %s <options> filename\n\n"
150           "Options:\n", exec_name);
151   arg_show_usage(stderr, all_args);
152 #if CONFIG_VP8_DECODER
153   fprintf(stderr, "\nVP8 Postprocessing Options:\n");
154   arg_show_usage(stderr, vp8_pp_args);
155 #endif
156   fprintf(stderr,
157           "\nOutput File Patterns:\n\n"
158           "  The -o argument specifies the name of the file(s) to "
159           "write to. If the\n  argument does not include any escape "
160           "characters, the output will be\n  written to a single file. "
161           "Otherwise, the filename will be calculated by\n  expanding "
162           "the following escape characters:\n");
163   fprintf(stderr,
164           "\n\t%%w   - Frame width"
165           "\n\t%%h   - Frame height"
166           "\n\t%%<n> - Frame number, zero padded to <n> places (1..9)"
167           "\n\n  Pattern arguments are only supported in conjunction "
168           "with the --yv12 and\n  --i420 options. If the -o option is "
169           "not specified, the output will be\n  directed to stdout.\n"
170          );
171   fprintf(stderr, "\nIncluded decoders:\n\n");
172
173   for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
174     fprintf(stderr, "    %-6s - %s\n",
175             ifaces[i].name,
176             vpx_codec_iface_name(ifaces[i].iface()));
177
178   exit(EXIT_FAILURE);
179 }
180
181 void die(const char *fmt, ...) {
182   va_list ap;
183   va_start(ap, fmt);
184   vfprintf(stderr, fmt, ap);
185   fprintf(stderr, "\n");
186   usage_exit();
187 }
188
189 static unsigned int mem_get_le16(const void *vmem) {
190   unsigned int  val;
191   const unsigned char *mem = (const unsigned char *)vmem;
192
193   val = mem[1] << 8;
194   val |= mem[0];
195   return val;
196 }
197
198 static unsigned int mem_get_le32(const void *vmem) {
199   unsigned int  val;
200   const unsigned char *mem = (const unsigned char *)vmem;
201
202   val = mem[3] << 24;
203   val |= mem[2] << 16;
204   val |= mem[1] << 8;
205   val |= mem[0];
206   return val;
207 }
208
209 enum file_kind {
210   RAW_FILE,
211   IVF_FILE,
212   WEBM_FILE
213 };
214
215 struct input_ctx {
216   enum file_kind  kind;
217   FILE           *infile;
218   nestegg        *nestegg_ctx;
219   nestegg_packet *pkt;
220   unsigned int    chunk;
221   unsigned int    chunks;
222   unsigned int    video_track;
223 };
224
225 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t))
226 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t))
227 static int read_frame(struct input_ctx      *input,
228                       uint8_t               **buf,
229                       size_t                *buf_sz,
230                       size_t                *buf_alloc_sz) {
231   char            raw_hdr[IVF_FRAME_HDR_SZ];
232   size_t          new_buf_sz;
233   FILE           *infile = input->infile;
234   enum file_kind  kind = input->kind;
235   if (kind == WEBM_FILE) {
236     if (input->chunk >= input->chunks) {
237       unsigned int track;
238
239       do {
240         /* End of this packet, get another. */
241         if (input->pkt)
242           nestegg_free_packet(input->pkt);
243
244         if (nestegg_read_packet(input->nestegg_ctx, &input->pkt) <= 0
245             || nestegg_packet_track(input->pkt, &track))
246           return 1;
247
248       } while (track != input->video_track);
249
250       if (nestegg_packet_count(input->pkt, &input->chunks))
251         return 1;
252       input->chunk = 0;
253     }
254
255     if (nestegg_packet_data(input->pkt, input->chunk, buf, buf_sz))
256       return 1;
257     input->chunk++;
258
259     return 0;
260   }
261   /* For both the raw and ivf formats, the frame size is the first 4 bytes
262    * of the frame header. We just need to special case on the header
263    * size.
264    */
265   else if (fread(raw_hdr, kind == IVF_FILE
266                  ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1, infile) != 1) {
267     if (!feof(infile))
268       fprintf(stderr, "Failed to read frame size\n");
269
270     new_buf_sz = 0;
271   } else {
272     new_buf_sz = mem_get_le32(raw_hdr);
273
274     if (new_buf_sz > 256 * 1024 * 1024) {
275       fprintf(stderr, "Error: Read invalid frame size (%u)\n",
276               (unsigned int)new_buf_sz);
277       new_buf_sz = 0;
278     }
279
280     if (kind == RAW_FILE && new_buf_sz > 256 * 1024)
281       fprintf(stderr, "Warning: Read invalid frame size (%u)"
282               " - not a raw file?\n", (unsigned int)new_buf_sz);
283
284     if (new_buf_sz > *buf_alloc_sz) {
285       uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz);
286
287       if (new_buf) {
288         *buf = new_buf;
289         *buf_alloc_sz = 2 * new_buf_sz;
290       } else {
291         fprintf(stderr, "Failed to allocate compressed data buffer\n");
292         new_buf_sz = 0;
293       }
294     }
295   }
296
297   *buf_sz = new_buf_sz;
298
299   if (!feof(infile)) {
300     if (fread(*buf, 1, *buf_sz, infile) != *buf_sz) {
301       fprintf(stderr, "Failed to read full frame\n");
302       return 1;
303     }
304
305     return 0;
306   }
307
308   return 1;
309 }
310
311 void *out_open(const char *out_fn, int do_md5) {
312   void *out = NULL;
313
314   if (do_md5) {
315 #if CONFIG_MD5
316     MD5Context *md5_ctx = out = malloc(sizeof(MD5Context));
317     (void)out_fn;
318     MD5Init(md5_ctx);
319 #endif
320   } else {
321     FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb")
322                           : set_binary_mode(stdout);
323
324     if (!outfile) {
325       fprintf(stderr, "Failed to output file");
326       exit(EXIT_FAILURE);
327     }
328   }
329
330   return out;
331 }
332
333 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5) {
334   if (do_md5) {
335 #if CONFIG_MD5
336     MD5Update(out, buf, len);
337 #endif
338   } else {
339     (void) fwrite(buf, 1, len, out);
340   }
341 }
342
343 void out_close(void *out, const char *out_fn, int do_md5) {
344   if (do_md5) {
345 #if CONFIG_MD5
346     uint8_t md5[16];
347     int i;
348
349     MD5Final(md5, out);
350     free(out);
351
352     for (i = 0; i < 16; i++)
353       printf("%02x", md5[i]);
354
355     printf("  %s\n", out_fn);
356 #endif
357   } else {
358     fclose(out);
359   }
360 }
361
362 unsigned int file_is_ivf(FILE *infile,
363                          unsigned int *fourcc,
364                          unsigned int *width,
365                          unsigned int *height,
366                          unsigned int *fps_den,
367                          unsigned int *fps_num) {
368   char raw_hdr[32];
369   int is_ivf = 0;
370
371   if (fread(raw_hdr, 1, 32, infile) == 32) {
372     if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K'
373         && raw_hdr[2] == 'I' && raw_hdr[3] == 'F') {
374       is_ivf = 1;
375
376       if (mem_get_le16(raw_hdr + 4) != 0)
377         fprintf(stderr, "Error: Unrecognized IVF version! This file may not"
378                 " decode properly.");
379
380       *fourcc = mem_get_le32(raw_hdr + 8);
381       *width = mem_get_le16(raw_hdr + 12);
382       *height = mem_get_le16(raw_hdr + 14);
383       *fps_num = mem_get_le32(raw_hdr + 16);
384       *fps_den = mem_get_le32(raw_hdr + 20);
385
386       /* Some versions of vpxenc used 1/(2*fps) for the timebase, so
387        * we can guess the framerate using only the timebase in this
388        * case. Other files would require reading ahead to guess the
389        * timebase, like we do for webm.
390        */
391       if (*fps_num < 1000) {
392         /* Correct for the factor of 2 applied to the timebase in the
393          * encoder.
394          */
395         if (*fps_num & 1)*fps_den <<= 1;
396         else *fps_num >>= 1;
397       } else {
398         /* Don't know FPS for sure, and don't have readahead code
399          * (yet?), so just default to 30fps.
400          */
401         *fps_num = 30;
402         *fps_den = 1;
403       }
404     }
405   }
406
407   if (!is_ivf)
408     rewind(infile);
409
410   return is_ivf;
411 }
412
413
414 unsigned int file_is_raw(FILE *infile,
415                          unsigned int *fourcc,
416                          unsigned int *width,
417                          unsigned int *height,
418                          unsigned int *fps_den,
419                          unsigned int *fps_num) {
420   unsigned char buf[32];
421   int is_raw = 0;
422   vpx_codec_stream_info_t si;
423
424   si.sz = sizeof(si);
425
426   if (fread(buf, 1, 32, infile) == 32) {
427     int i;
428
429     if (mem_get_le32(buf) < 256 * 1024 * 1024)
430       for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
431         if (!vpx_codec_peek_stream_info(ifaces[i].iface(),
432                                         buf + 4, 32 - 4, &si)) {
433           is_raw = 1;
434           *fourcc = ifaces[i].fourcc;
435           *width = si.w;
436           *height = si.h;
437           *fps_num = 30;
438           *fps_den = 1;
439           break;
440         }
441   }
442
443   rewind(infile);
444   return is_raw;
445 }
446
447
448 static int
449 nestegg_read_cb(void *buffer, size_t length, void *userdata) {
450   FILE *f = userdata;
451
452   if (fread(buffer, 1, length, f) < length) {
453     if (ferror(f))
454       return -1;
455     if (feof(f))
456       return 0;
457   }
458   return 1;
459 }
460
461
462 static int
463 nestegg_seek_cb(int64_t offset, int whence, void *userdata) {
464   switch (whence) {
465     case NESTEGG_SEEK_SET:
466       whence = SEEK_SET;
467       break;
468     case NESTEGG_SEEK_CUR:
469       whence = SEEK_CUR;
470       break;
471     case NESTEGG_SEEK_END:
472       whence = SEEK_END;
473       break;
474   };
475   return fseek(userdata, (long)offset, whence) ? -1 : 0;
476 }
477
478
479 static int64_t
480 nestegg_tell_cb(void *userdata) {
481   return ftell(userdata);
482 }
483
484
485 static void
486 nestegg_log_cb(nestegg *context, unsigned int severity, char const *format,
487                ...) {
488   va_list ap;
489
490   va_start(ap, format);
491   vfprintf(stderr, format, ap);
492   fprintf(stderr, "\n");
493   va_end(ap);
494 }
495
496
497 static int
498 webm_guess_framerate(struct input_ctx *input,
499                      unsigned int     *fps_den,
500                      unsigned int     *fps_num) {
501   unsigned int i;
502   uint64_t     tstamp = 0;
503
504   /* Check to see if we can seek before we parse any data. */
505   if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0)) {
506     fprintf(stderr,
507             "WARNING: Failed to guess framerate (no Cues), set to 30fps.\n");
508     *fps_num = 30;
509     *fps_den = 1;
510     return 0;
511   }
512
513   /* Guess the framerate. Read up to 1 second, or 50 video packets,
514    * whichever comes first.
515    */
516   for (i = 0; tstamp < 1000000000 && i < 50;) {
517     nestegg_packet *pkt;
518     unsigned int track;
519
520     if (nestegg_read_packet(input->nestegg_ctx, &pkt) <= 0)
521       break;
522
523     nestegg_packet_track(pkt, &track);
524     if (track == input->video_track) {
525       nestegg_packet_tstamp(pkt, &tstamp);
526       i++;
527     }
528
529     nestegg_free_packet(pkt);
530   }
531
532   if (nestegg_track_seek(input->nestegg_ctx, input->video_track, 0))
533     goto fail;
534
535   *fps_num = (i - 1) * 1000000;
536   *fps_den = (unsigned int)(tstamp / 1000);
537   return 0;
538 fail:
539   nestegg_destroy(input->nestegg_ctx);
540   input->nestegg_ctx = NULL;
541   rewind(input->infile);
542   return 1;
543 }
544
545
546 static int
547 file_is_webm(struct input_ctx *input,
548              unsigned int     *fourcc,
549              unsigned int     *width,
550              unsigned int     *height,
551              unsigned int     *fps_den,
552              unsigned int     *fps_num) {
553   unsigned int i, n;
554   int          track_type = -1;
555   int          codec_id;
556
557   nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0};
558   nestegg_video_params params;
559
560   io.userdata = input->infile;
561   if (nestegg_init(&input->nestegg_ctx, io, NULL))
562     goto fail;
563
564   if (nestegg_track_count(input->nestegg_ctx, &n))
565     goto fail;
566
567   for (i = 0; i < n; i++) {
568     track_type = nestegg_track_type(input->nestegg_ctx, i);
569
570     if (track_type == NESTEGG_TRACK_VIDEO)
571       break;
572     else if (track_type < 0)
573       goto fail;
574   }
575
576   codec_id = nestegg_track_codec_id(input->nestegg_ctx, i);
577   if (codec_id == NESTEGG_CODEC_VP8) {
578     *fourcc = VP8_FOURCC;
579   } else if (codec_id == NESTEGG_CODEC_VP9) {
580     *fourcc = VP9_FOURCC;
581   } else {
582     fprintf(stderr, "Not VPx video, quitting.\n");
583     exit(1);
584   }
585
586   input->video_track = i;
587
588   if (nestegg_track_video_params(input->nestegg_ctx, i, &params))
589     goto fail;
590
591   *fps_den = 0;
592   *fps_num = 0;
593   *width = params.width;
594   *height = params.height;
595   return 1;
596 fail:
597   input->nestegg_ctx = NULL;
598   rewind(input->infile);
599   return 0;
600 }
601
602
603 void show_progress(int frame_in, int frame_out, unsigned long dx_time) {
604   fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\r",
605           frame_in, frame_out, dx_time,
606           (float)frame_out * 1000000.0 / (float)dx_time);
607 }
608
609
610 void generate_filename(const char *pattern, char *out, size_t q_len,
611                        unsigned int d_w, unsigned int d_h,
612                        unsigned int frame_in) {
613   const char *p = pattern;
614   char *q = out;
615
616   do {
617     char *next_pat = strchr(p, '%');
618
619     if (p == next_pat) {
620       size_t pat_len;
621
622       /* parse the pattern */
623       q[q_len - 1] = '\0';
624       switch (p[1]) {
625         case 'w':
626           snprintf(q, q_len - 1, "%d", d_w);
627           break;
628         case 'h':
629           snprintf(q, q_len - 1, "%d", d_h);
630           break;
631         case '1':
632           snprintf(q, q_len - 1, "%d", frame_in);
633           break;
634         case '2':
635           snprintf(q, q_len - 1, "%02d", frame_in);
636           break;
637         case '3':
638           snprintf(q, q_len - 1, "%03d", frame_in);
639           break;
640         case '4':
641           snprintf(q, q_len - 1, "%04d", frame_in);
642           break;
643         case '5':
644           snprintf(q, q_len - 1, "%05d", frame_in);
645           break;
646         case '6':
647           snprintf(q, q_len - 1, "%06d", frame_in);
648           break;
649         case '7':
650           snprintf(q, q_len - 1, "%07d", frame_in);
651           break;
652         case '8':
653           snprintf(q, q_len - 1, "%08d", frame_in);
654           break;
655         case '9':
656           snprintf(q, q_len - 1, "%09d", frame_in);
657           break;
658         default:
659           die("Unrecognized pattern %%%c\n", p[1]);
660       }
661
662       pat_len = strlen(q);
663       if (pat_len >= q_len - 1)
664         die("Output filename too long.\n");
665       q += pat_len;
666       p += 2;
667       q_len -= pat_len;
668     } else {
669       size_t copy_len;
670
671       /* copy the next segment */
672       if (!next_pat)
673         copy_len = strlen(p);
674       else
675         copy_len = next_pat - p;
676
677       if (copy_len >= q_len - 1)
678         die("Output filename too long.\n");
679
680       memcpy(q, p, copy_len);
681       q[copy_len] = '\0';
682       q += copy_len;
683       p += copy_len;
684       q_len -= copy_len;
685     }
686   } while (*p);
687 }
688
689
690 int main_loop(int argc, const char **argv_) {
691   vpx_codec_ctx_t          decoder;
692   char                  *fn = NULL;
693   int                    i;
694   uint8_t               *buf = NULL;
695   size_t                 buf_sz = 0, buf_alloc_sz = 0;
696   FILE                  *infile;
697   int                    frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0;
698   int                    stop_after = 0, postproc = 0, summary = 0, quiet = 1;
699   int                    arg_skip = 0;
700   int                    ec_enabled = 0;
701   vpx_codec_iface_t       *iface = NULL;
702   unsigned int           fourcc;
703   unsigned long          dx_time = 0;
704   struct arg               arg;
705   char                   **argv, **argi, **argj;
706   const char             *outfile_pattern = 0;
707   char                    outfile[PATH_MAX];
708   int                     single_file;
709   int                     use_y4m = 1;
710   unsigned int            width;
711   unsigned int            height;
712   unsigned int            fps_den;
713   unsigned int            fps_num;
714   void                   *out = NULL;
715   vpx_codec_dec_cfg_t     cfg = {0};
716 #if CONFIG_VP8_DECODER
717   vp8_postproc_cfg_t      vp8_pp_cfg = {0};
718   int                     vp8_dbg_color_ref_frame = 0;
719   int                     vp8_dbg_color_mb_modes = 0;
720   int                     vp8_dbg_color_b_modes = 0;
721   int                     vp8_dbg_display_mv = 0;
722 #endif
723   struct input_ctx        input = {0};
724   int                     frames_corrupted = 0;
725   int                     dec_flags = 0;
726   int                     do_scale = 0;
727   int                     stream_w = 0, stream_h = 0;
728   vpx_image_t             *scaled_img = NULL;
729   int                     frame_avail, got_data;
730
731   /* Parse command line */
732   exec_name = argv_[0];
733   argv = argv_dup(argc - 1, argv_ + 1);
734
735   for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
736     memset(&arg, 0, sizeof(arg));
737     arg.argv_step = 1;
738
739     if (arg_match(&arg, &codecarg, argi)) {
740       int j, k = -1;
741
742       for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++)
743         if (!strcmp(ifaces[j].name, arg.val))
744           k = j;
745
746       if (k >= 0)
747         iface = ifaces[k].iface();
748       else
749         die("Error: Unrecognized argument (%s) to --codec\n",
750             arg.val);
751     } else if (arg_match(&arg, &looparg, argi)) {
752       // no-op
753     } else if (arg_match(&arg, &outputfile, argi))
754       outfile_pattern = arg.val;
755     else if (arg_match(&arg, &use_yv12, argi)) {
756       use_y4m = 0;
757       flipuv = 1;
758     } else if (arg_match(&arg, &use_i420, argi)) {
759       use_y4m = 0;
760       flipuv = 0;
761     } else if (arg_match(&arg, &flipuvarg, argi))
762       flipuv = 1;
763     else if (arg_match(&arg, &noblitarg, argi))
764       noblit = 1;
765     else if (arg_match(&arg, &progressarg, argi))
766       progress = 1;
767     else if (arg_match(&arg, &limitarg, argi))
768       stop_after = arg_parse_uint(&arg);
769     else if (arg_match(&arg, &skiparg, argi))
770       arg_skip = arg_parse_uint(&arg);
771     else if (arg_match(&arg, &postprocarg, argi))
772       postproc = 1;
773     else if (arg_match(&arg, &md5arg, argi))
774       do_md5 = 1;
775     else if (arg_match(&arg, &summaryarg, argi))
776       summary = 1;
777     else if (arg_match(&arg, &threadsarg, argi))
778       cfg.threads = arg_parse_uint(&arg);
779     else if (arg_match(&arg, &verbosearg, argi))
780       quiet = 0;
781     else if (arg_match(&arg, &scalearg, argi))
782       do_scale = 1;
783
784 #if CONFIG_VP8_DECODER
785     else if (arg_match(&arg, &addnoise_level, argi)) {
786       postproc = 1;
787       vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE;
788       vp8_pp_cfg.noise_level = arg_parse_uint(&arg);
789     } else if (arg_match(&arg, &demacroblock_level, argi)) {
790       postproc = 1;
791       vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK;
792       vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg);
793     } else if (arg_match(&arg, &deblock, argi)) {
794       postproc = 1;
795       vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK;
796     } else if (arg_match(&arg, &mfqe, argi)) {
797       postproc = 1;
798       vp8_pp_cfg.post_proc_flag |= VP8_MFQE;
799     } else if (arg_match(&arg, &pp_debug_info, argi)) {
800       unsigned int level = arg_parse_uint(&arg);
801
802       postproc = 1;
803       vp8_pp_cfg.post_proc_flag &= ~0x7;
804
805       if (level)
806         vp8_pp_cfg.post_proc_flag |= level;
807     } else if (arg_match(&arg, &pp_disp_ref_frame, argi)) {
808       unsigned int flags = arg_parse_int(&arg);
809       if (flags) {
810         postproc = 1;
811         vp8_dbg_color_ref_frame = flags;
812       }
813     } else if (arg_match(&arg, &pp_disp_mb_modes, argi)) {
814       unsigned int flags = arg_parse_int(&arg);
815       if (flags) {
816         postproc = 1;
817         vp8_dbg_color_mb_modes = flags;
818       }
819     } else if (arg_match(&arg, &pp_disp_b_modes, argi)) {
820       unsigned int flags = arg_parse_int(&arg);
821       if (flags) {
822         postproc = 1;
823         vp8_dbg_color_b_modes = flags;
824       }
825     } else if (arg_match(&arg, &pp_disp_mvs, argi)) {
826       unsigned int flags = arg_parse_int(&arg);
827       if (flags) {
828         postproc = 1;
829         vp8_dbg_display_mv = flags;
830       }
831     } else if (arg_match(&arg, &error_concealment, argi)) {
832       ec_enabled = 1;
833     }
834
835 #endif
836     else
837       argj++;
838   }
839
840   /* Check for unrecognized options */
841   for (argi = argv; *argi; argi++)
842     if (argi[0][0] == '-' && strlen(argi[0]) > 1)
843       die("Error: Unrecognized option %s\n", *argi);
844
845   /* Handle non-option arguments */
846   fn = argv[0];
847
848   if (!fn)
849     usage_exit();
850
851   /* Open file */
852   infile = strcmp(fn, "-") ? fopen(fn, "rb") : set_binary_mode(stdin);
853
854   if (!infile) {
855     fprintf(stderr, "Failed to open file '%s'",
856             strcmp(fn, "-") ? fn : "stdin");
857     return EXIT_FAILURE;
858   }
859 #if CONFIG_OS_SUPPORT
860   /* Make sure we don't dump to the terminal, unless forced to with -o - */
861   if (!outfile_pattern && isatty(fileno(stdout)) && !do_md5 && !noblit) {
862     fprintf(stderr,
863             "Not dumping raw video to your terminal. Use '-o -' to "
864             "override.\n");
865     return EXIT_FAILURE;
866   }
867 #endif
868   input.infile = infile;
869   if (file_is_ivf(infile, &fourcc, &width, &height, &fps_den,
870                   &fps_num))
871     input.kind = IVF_FILE;
872   else if (file_is_webm(&input, &fourcc, &width, &height, &fps_den, &fps_num))
873     input.kind = WEBM_FILE;
874   else if (file_is_raw(infile, &fourcc, &width, &height, &fps_den, &fps_num))
875     input.kind = RAW_FILE;
876   else {
877     fprintf(stderr, "Unrecognized input file type.\n");
878     return EXIT_FAILURE;
879   }
880
881   /* If the output file is not set or doesn't have a sequence number in
882    * it, then we only open it once.
883    */
884   outfile_pattern = outfile_pattern ? outfile_pattern : "-";
885   single_file = 1;
886   {
887     const char *p = outfile_pattern;
888     do {
889       p = strchr(p, '%');
890       if (p && p[1] >= '1' && p[1] <= '9') {
891         /* pattern contains sequence number, so it's not unique. */
892         single_file = 0;
893         break;
894       }
895       if (p)
896         p++;
897     } while (p);
898   }
899
900   if (single_file && !noblit) {
901     generate_filename(outfile_pattern, outfile, sizeof(outfile) - 1,
902                       width, height, 0);
903     out = out_open(outfile, do_md5);
904   }
905
906   if (use_y4m && !noblit) {
907     char buffer[128];
908
909     if (!single_file) {
910       fprintf(stderr, "YUV4MPEG2 not supported with output patterns,"
911               " try --i420 or --yv12.\n");
912       return EXIT_FAILURE;
913     }
914
915     if (input.kind == WEBM_FILE)
916       if (webm_guess_framerate(&input, &fps_den, &fps_num)) {
917         fprintf(stderr, "Failed to guess framerate -- error parsing "
918                 "webm file?\n");
919         return EXIT_FAILURE;
920       }
921
922
923     /*Note: We can't output an aspect ratio here because IVF doesn't
924        store one, and neither does VP8.
925       That will have to wait until these tools support WebM natively.*/
926     snprintf(buffer, sizeof(buffer), "YUV4MPEG2 W%u H%u F%u:%u I%c ",
927              width, height, fps_num, fps_den, 'p');
928     out_put(out, (unsigned char *)buffer,
929             (unsigned int)strlen(buffer), do_md5);
930   }
931
932   /* Try to determine the codec from the fourcc. */
933   for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
934     if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc) {
935       vpx_codec_iface_t  *ivf_iface = ifaces[i].iface();
936
937       if (iface && iface != ivf_iface)
938         fprintf(stderr, "Notice -- IVF header indicates codec: %s\n",
939                 ifaces[i].name);
940       else
941         iface = ivf_iface;
942
943       break;
944     }
945
946   dec_flags = (postproc ? VPX_CODEC_USE_POSTPROC : 0) |
947               (ec_enabled ? VPX_CODEC_USE_ERROR_CONCEALMENT : 0);
948   if (vpx_codec_dec_init(&decoder, iface ? iface :  ifaces[0].iface(), &cfg,
949                          dec_flags)) {
950     fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder));
951     return EXIT_FAILURE;
952   }
953
954   if (!quiet)
955     fprintf(stderr, "%s\n", decoder.name);
956
957 #if CONFIG_VP8_DECODER
958
959   if (vp8_pp_cfg.post_proc_flag
960       && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg)) {
961     fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder));
962     return EXIT_FAILURE;
963   }
964
965   if (vp8_dbg_color_ref_frame
966       && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_REF_FRAME, vp8_dbg_color_ref_frame)) {
967     fprintf(stderr, "Failed to configure reference block visualizer: %s\n", vpx_codec_error(&decoder));
968     return EXIT_FAILURE;
969   }
970
971   if (vp8_dbg_color_mb_modes
972       && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_MB_MODES, vp8_dbg_color_mb_modes)) {
973     fprintf(stderr, "Failed to configure macro block visualizer: %s\n", vpx_codec_error(&decoder));
974     return EXIT_FAILURE;
975   }
976
977   if (vp8_dbg_color_b_modes
978       && vpx_codec_control(&decoder, VP8_SET_DBG_COLOR_B_MODES, vp8_dbg_color_b_modes)) {
979     fprintf(stderr, "Failed to configure block visualizer: %s\n", vpx_codec_error(&decoder));
980     return EXIT_FAILURE;
981   }
982
983   if (vp8_dbg_display_mv
984       && vpx_codec_control(&decoder, VP8_SET_DBG_DISPLAY_MV, vp8_dbg_display_mv)) {
985     fprintf(stderr, "Failed to configure motion vector visualizer: %s\n", vpx_codec_error(&decoder));
986     return EXIT_FAILURE;
987   }
988 #endif
989
990
991   if(arg_skip)
992     fprintf(stderr, "Skiping first %d frames.\n", arg_skip);
993   while (arg_skip) {
994     if (read_frame(&input, &buf, &buf_sz, &buf_alloc_sz))
995       break;
996     arg_skip--;
997   }
998
999   frame_avail = 1;
1000   got_data = 0;
1001
1002   /* Decode file */
1003   while (frame_avail || got_data) {
1004     vpx_codec_iter_t  iter = NULL;
1005     vpx_image_t    *img;
1006     struct vpx_usec_timer timer;
1007     int                   corrupted;
1008
1009     frame_avail = 0;
1010     if (!stop_after || frame_in < stop_after) {
1011       if(!read_frame(&input, &buf, &buf_sz, &buf_alloc_sz)) {
1012         frame_avail = 1;
1013         frame_in++;
1014
1015         vpx_usec_timer_start(&timer);
1016
1017         if (vpx_codec_decode(&decoder, buf, (unsigned int)buf_sz, NULL, 0)) {
1018           const char *detail = vpx_codec_error_detail(&decoder);
1019           fprintf(stderr, "Failed to decode frame: %s\n",
1020                   vpx_codec_error(&decoder));
1021
1022           if (detail)
1023             fprintf(stderr, "  Additional information: %s\n", detail);
1024           goto fail;
1025         }
1026
1027         vpx_usec_timer_mark(&timer);
1028         dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer);
1029       }
1030     }
1031
1032     vpx_usec_timer_start(&timer);
1033
1034     got_data = 0;
1035     if ((img = vpx_codec_get_frame(&decoder, &iter))) {
1036       ++frame_out;
1037       got_data = 1;
1038     }
1039
1040     vpx_usec_timer_mark(&timer);
1041     dx_time += (unsigned int)vpx_usec_timer_elapsed(&timer);
1042
1043     if (vpx_codec_control(&decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) {
1044       fprintf(stderr, "Failed VP8_GET_FRAME_CORRUPTED: %s\n",
1045               vpx_codec_error(&decoder));
1046       goto fail;
1047     }
1048     frames_corrupted += corrupted;
1049
1050     if (progress)
1051       show_progress(frame_in, frame_out, dx_time);
1052
1053     if (!noblit) {
1054       if (frame_out == 1 && img && use_y4m) {
1055         /* Write out the color format to terminate the header line */
1056         const char *color =
1057             img->fmt == VPX_IMG_FMT_444A ? "C444alpha\n" :
1058             img->fmt == VPX_IMG_FMT_I444 ? "C444\n" :
1059             img->fmt == VPX_IMG_FMT_I422 ? "C422\n" :
1060             "C420jpeg\n";
1061
1062         out_put(out, (const unsigned char*)color, strlen(color), do_md5);
1063       }
1064
1065       if (do_scale) {
1066         if (img && frame_out == 1) {
1067           stream_w = img->d_w;
1068           stream_h = img->d_h;
1069           scaled_img = vpx_img_alloc(NULL, VPX_IMG_FMT_I420,
1070                                      stream_w, stream_h, 16);
1071         }
1072         if (img && (img->d_w != stream_w || img->d_h != stream_h)) {
1073           assert(img->fmt == VPX_IMG_FMT_I420);
1074           I420Scale(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y],
1075                     img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U],
1076                     img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
1077                     img->d_w, img->d_h,
1078                     scaled_img->planes[VPX_PLANE_Y],
1079                     scaled_img->stride[VPX_PLANE_Y],
1080                     scaled_img->planes[VPX_PLANE_U],
1081                     scaled_img->stride[VPX_PLANE_U],
1082                     scaled_img->planes[VPX_PLANE_V],
1083                     scaled_img->stride[VPX_PLANE_V],
1084                     stream_w, stream_h,
1085                     kFilterBox);
1086           img = scaled_img;
1087         }
1088       }
1089
1090       if (img) {
1091         unsigned int y;
1092         char out_fn[PATH_MAX];
1093         uint8_t *buf;
1094         unsigned int c_w =
1095             img->x_chroma_shift ? (1 + img->d_w) >> img->x_chroma_shift
1096                                 : img->d_w;
1097         unsigned int c_h =
1098             img->y_chroma_shift ? (1 + img->d_h) >> img->y_chroma_shift
1099                                 : img->d_h;
1100
1101         if (!single_file) {
1102           size_t len = sizeof(out_fn) - 1;
1103
1104           out_fn[len] = '\0';
1105           generate_filename(outfile_pattern, out_fn, len - 1,
1106                             img->d_w, img->d_h, frame_in);
1107           out = out_open(out_fn, do_md5);
1108         } else if (use_y4m)
1109           out_put(out, (unsigned char *)"FRAME\n", 6, do_md5);
1110
1111         buf = img->planes[VPX_PLANE_Y];
1112
1113         for (y = 0; y < img->d_h; y++) {
1114           out_put(out, buf, img->d_w, do_md5);
1115           buf += img->stride[VPX_PLANE_Y];
1116         }
1117
1118         buf = img->planes[flipuv ? VPX_PLANE_V : VPX_PLANE_U];
1119
1120         for (y = 0; y < c_h; y++) {
1121           out_put(out, buf, c_w, do_md5);
1122           buf += img->stride[VPX_PLANE_U];
1123         }
1124
1125         buf = img->planes[flipuv ? VPX_PLANE_U : VPX_PLANE_V];
1126
1127         for (y = 0; y < c_h; y++) {
1128           out_put(out, buf, c_w, do_md5);
1129           buf += img->stride[VPX_PLANE_V];
1130         }
1131
1132         if (!single_file)
1133           out_close(out, out_fn, do_md5);
1134       }
1135     }
1136
1137     if (stop_after && frame_in >= stop_after)
1138       break;
1139   }
1140
1141   if (summary || progress) {
1142     show_progress(frame_in, frame_out, dx_time);
1143     fprintf(stderr, "\n");
1144   }
1145
1146   if (frames_corrupted)
1147     fprintf(stderr, "WARNING: %d frames corrupted.\n", frames_corrupted);
1148
1149 fail:
1150
1151   if (vpx_codec_destroy(&decoder)) {
1152     fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder));
1153     return EXIT_FAILURE;
1154   }
1155
1156   if (single_file && !noblit)
1157     out_close(out, outfile, do_md5);
1158
1159   if (input.nestegg_ctx)
1160     nestegg_destroy(input.nestegg_ctx);
1161   if (input.kind != WEBM_FILE)
1162     free(buf);
1163   fclose(infile);
1164   free(argv);
1165
1166   return frames_corrupted ? EXIT_FAILURE : EXIT_SUCCESS;
1167 }
1168
1169 int main(int argc, const char **argv_) {
1170   unsigned int loops = 1, i;
1171   char **argv, **argi, **argj;
1172   struct arg arg;
1173   int error = 0;
1174
1175   argv = argv_dup(argc - 1, argv_ + 1);
1176   for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) {
1177     memset(&arg, 0, sizeof(arg));
1178     arg.argv_step = 1;
1179
1180     if (arg_match(&arg, &looparg, argi)) {
1181       loops = arg_parse_uint(&arg);
1182       break;
1183     }
1184   }
1185   free(argv);
1186   for (i = 0; !error && i < loops; i++)
1187     error = main_loop(argc, argv_);
1188   return error;
1189 }