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