Home | History | Annotate | Download | only in libvpx
      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 #define VPX_CODEC_DISABLE_COMPAT 1
     20 #include "vpx_config.h"
     21 #include "vpx/vpx_decoder.h"
     22 #include "vpx_ports/vpx_timer.h"
     23 #if CONFIG_VP8_DECODER
     24 #include "vpx/vp8dx.h"
     25 #endif
     26 #if CONFIG_MD5
     27 #include "md5_utils.h"
     28 #endif
     29 
     30 static const char *exec_name;
     31 
     32 static const struct
     33 {
     34     char const *name;
     35     const vpx_codec_iface_t *iface;
     36     unsigned int             fourcc;
     37     unsigned int             fourcc_mask;
     38 } ifaces[] =
     39 {
     40 #if CONFIG_VP8_DECODER
     41     {"vp8",  &vpx_codec_vp8_dx_algo,   0x00385056, 0x00FFFFFF},
     42 #endif
     43 };
     44 
     45 #include "args.h"
     46 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1,
     47                                   "Codec to use");
     48 static const arg_def_t prefixarg = ARG_DEF("p", "prefix", 1,
     49                                    "Prefix to use when saving frames");
     50 static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0,
     51                                   "Output file is YV12 ");
     52 static const arg_def_t use_i420 = ARG_DEF(NULL, "i420", 0,
     53                                   "Output file is I420 (default)");
     54 static const arg_def_t flipuvarg = ARG_DEF(NULL, "flipuv", 0,
     55                                    "Synonym for --yv12");
     56 static const arg_def_t noblitarg = ARG_DEF(NULL, "noblit", 0,
     57                                    "Don't process the decoded frames");
     58 static const arg_def_t progressarg = ARG_DEF(NULL, "progress", 0,
     59                                      "Show progress after each frame decodes");
     60 static const arg_def_t limitarg = ARG_DEF(NULL, "limit", 1,
     61                                   "Stop decoding after n frames");
     62 static const arg_def_t postprocarg = ARG_DEF(NULL, "postproc", 0,
     63                                      "Postprocess decoded frames");
     64 static const arg_def_t summaryarg = ARG_DEF(NULL, "summary", 0,
     65                                     "Show timing summary");
     66 static const arg_def_t outputfile = ARG_DEF("o", "output", 1,
     67                                     "Output raw yv12 file instead of images");
     68 static const arg_def_t usey4marg = ARG_DEF("y", "y4m", 0,
     69                                     "Output file is YUV4MPEG2");
     70 static const arg_def_t threadsarg = ARG_DEF("t", "threads", 1,
     71                                     "Max threads to use");
     72 static const arg_def_t quietarg = ARG_DEF("q", "quiet", 0,
     73                                   "Suppress version string");
     74 
     75 #if CONFIG_MD5
     76 static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
     77                                         "Compute the MD5 sum of the decoded frame");
     78 #endif
     79 static const arg_def_t *all_args[] =
     80 {
     81     &codecarg, &prefixarg, &use_yv12, &use_i420, &flipuvarg, &noblitarg,
     82     &progressarg, &limitarg, &postprocarg, &summaryarg, &outputfile,
     83     &usey4marg, &threadsarg, &quietarg,
     84 #if CONFIG_MD5
     85     &md5arg,
     86 #endif
     87     NULL
     88 };
     89 
     90 #if CONFIG_VP8_DECODER
     91 static const arg_def_t addnoise_level = ARG_DEF(NULL, "noise-level", 1,
     92                                         "Enable VP8 postproc add noise");
     93 static const arg_def_t deblock = ARG_DEF(NULL, "deblock", 0,
     94                                  "Enable VP8 deblocking");
     95 static const arg_def_t demacroblock_level = ARG_DEF(NULL, "demacroblock-level", 1,
     96         "Enable VP8 demacroblocking, w/ level");
     97 static const arg_def_t pp_debug_info = ARG_DEF(NULL, "pp-debug-info", 1,
     98                                        "Enable VP8 visible debug info");
     99 
    100 
    101 static const arg_def_t *vp8_pp_args[] =
    102 {
    103     &addnoise_level, &deblock, &demacroblock_level, &pp_debug_info,
    104     NULL
    105 };
    106 #endif
    107 
    108 static void usage_exit()
    109 {
    110     int i;
    111 
    112     fprintf(stderr, "Usage: %s <options> filename\n\n"
    113             "Options:\n", exec_name);
    114     arg_show_usage(stderr, all_args);
    115 #if CONFIG_VP8_DECODER
    116     fprintf(stderr, "\nvp8 Postprocessing Options:\n");
    117     arg_show_usage(stderr, vp8_pp_args);
    118 #endif
    119     fprintf(stderr, "\nIncluded decoders:\n\n");
    120 
    121     for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
    122         fprintf(stderr, "    %-6s - %s\n",
    123                 ifaces[i].name,
    124                 vpx_codec_iface_name(ifaces[i].iface));
    125 
    126     exit(EXIT_FAILURE);
    127 }
    128 
    129 void die(const char *fmt, ...)
    130 {
    131     va_list ap;
    132     va_start(ap, fmt);
    133     vfprintf(stderr, fmt, ap);
    134     fprintf(stderr, "\n");
    135     usage_exit();
    136 }
    137 
    138 static unsigned int mem_get_le16(const void *vmem)
    139 {
    140     unsigned int  val;
    141     const unsigned char *mem = (const unsigned char *)vmem;
    142 
    143     val = mem[1] << 8;
    144     val |= mem[0];
    145     return val;
    146 }
    147 
    148 static unsigned int mem_get_le32(const void *vmem)
    149 {
    150     unsigned int  val;
    151     const unsigned char *mem = (const unsigned char *)vmem;
    152 
    153     val = mem[3] << 24;
    154     val |= mem[2] << 16;
    155     val |= mem[1] << 8;
    156     val |= mem[0];
    157     return val;
    158 }
    159 
    160 #define IVF_FRAME_HDR_SZ (sizeof(uint32_t) + sizeof(uint64_t))
    161 #define RAW_FRAME_HDR_SZ (sizeof(uint32_t))
    162 static int read_frame(FILE                  *infile,
    163                       uint8_t               **buf,
    164                       uint32_t              *buf_sz,
    165                       uint32_t              *buf_alloc_sz,
    166                       int                    is_ivf)
    167 {
    168     char     raw_hdr[IVF_FRAME_HDR_SZ];
    169     uint32_t new_buf_sz;
    170 
    171     /* For both the raw and ivf formats, the frame size is the first 4 bytes
    172      * of the frame header. We just need to special case on the header
    173      * size.
    174      */
    175     if (fread(raw_hdr, is_ivf ? IVF_FRAME_HDR_SZ : RAW_FRAME_HDR_SZ, 1,
    176               infile) != 1)
    177     {
    178         if (!feof(infile))
    179             fprintf(stderr, "Failed to read frame size\n");
    180 
    181         new_buf_sz = 0;
    182     }
    183     else
    184     {
    185         new_buf_sz = mem_get_le32(raw_hdr);
    186 
    187         if (new_buf_sz > 256 * 1024 * 1024)
    188         {
    189             fprintf(stderr, "Error: Read invalid frame size (%u)\n",
    190                     new_buf_sz);
    191             new_buf_sz = 0;
    192         }
    193 
    194         if (!is_ivf && new_buf_sz > 256 * 1024)
    195             fprintf(stderr, "Warning: Read invalid frame size (%u)"
    196                     " - not a raw file?\n", new_buf_sz);
    197 
    198         if (new_buf_sz > *buf_alloc_sz)
    199         {
    200             uint8_t *new_buf = realloc(*buf, 2 * new_buf_sz);
    201 
    202             if (new_buf)
    203             {
    204                 *buf = new_buf;
    205                 *buf_alloc_sz = 2 * new_buf_sz;
    206             }
    207             else
    208             {
    209                 fprintf(stderr, "Failed to allocate compressed data buffer\n");
    210                 new_buf_sz = 0;
    211             }
    212         }
    213     }
    214 
    215     *buf_sz = new_buf_sz;
    216 
    217     if (*buf_sz)
    218     {
    219         if (fread(*buf, 1, *buf_sz, infile) != *buf_sz)
    220         {
    221             fprintf(stderr, "Failed to read full frame\n");
    222             return 1;
    223         }
    224 
    225         return 0;
    226     }
    227 
    228     return 1;
    229 }
    230 
    231 void *out_open(const char *out_fn, int do_md5)
    232 {
    233     void *out = NULL;
    234 
    235     if (do_md5)
    236     {
    237 #if CONFIG_MD5
    238         MD5Context *md5_ctx = out = malloc(sizeof(MD5Context));
    239         (void)out_fn;
    240         MD5Init(md5_ctx);
    241 #endif
    242     }
    243     else
    244     {
    245         FILE *outfile = out = strcmp("-", out_fn) ? fopen(out_fn, "wb") : stdout;
    246 
    247         if (!outfile)
    248         {
    249             fprintf(stderr, "Failed to output file");
    250             exit(EXIT_FAILURE);
    251         }
    252     }
    253 
    254     return out;
    255 }
    256 
    257 void out_put(void *out, const uint8_t *buf, unsigned int len, int do_md5)
    258 {
    259     if (do_md5)
    260     {
    261 #if CONFIG_MD5
    262         MD5Update(out, buf, len);
    263 #endif
    264     }
    265     else
    266     {
    267         fwrite(buf, 1, len, out);
    268     }
    269 }
    270 
    271 void out_close(void *out, const char *out_fn, int do_md5)
    272 {
    273     if (do_md5)
    274     {
    275 #if CONFIG_MD5
    276         uint8_t md5[16];
    277         int i;
    278 
    279         MD5Final(md5, out);
    280         free(out);
    281 
    282         for (i = 0; i < 16; i++)
    283             printf("%02x", md5[i]);
    284 
    285         printf("  %s\n", out_fn);
    286 #endif
    287     }
    288     else
    289     {
    290         fclose(out);
    291     }
    292 }
    293 
    294 unsigned int file_is_ivf(FILE *infile,
    295                          unsigned int *fourcc,
    296                          unsigned int *width,
    297                          unsigned int *height,
    298                          unsigned int *timebase_num,
    299                          unsigned int *timebase_den)
    300 {
    301     char raw_hdr[32];
    302     int is_ivf = 0;
    303 
    304     if (fread(raw_hdr, 1, 32, infile) == 32)
    305     {
    306         if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K'
    307             && raw_hdr[2] == 'I' && raw_hdr[3] == 'F')
    308         {
    309             is_ivf = 1;
    310 
    311             if (mem_get_le16(raw_hdr + 4) != 0)
    312                 fprintf(stderr, "Error: Unrecognized IVF version! This file may not"
    313                         " decode properly.");
    314 
    315             *fourcc = mem_get_le32(raw_hdr + 8);
    316             *width = mem_get_le16(raw_hdr + 12);
    317             *height = mem_get_le16(raw_hdr + 14);
    318             *timebase_den = mem_get_le32(raw_hdr + 16);
    319             *timebase_num = mem_get_le32(raw_hdr + 20);
    320         }
    321     }
    322 
    323     if (!is_ivf)
    324         rewind(infile);
    325 
    326     return is_ivf;
    327 }
    328 
    329 int main(int argc, const char **argv_)
    330 {
    331     vpx_codec_ctx_t          decoder;
    332     char                  *prefix = NULL, *fn = NULL;
    333     int                    i;
    334     uint8_t               *buf = NULL;
    335     uint32_t               buf_sz = 0, buf_alloc_sz = 0;
    336     FILE                  *infile;
    337     int                    frame_in = 0, frame_out = 0, flipuv = 0, noblit = 0, do_md5 = 0, progress = 0;
    338     int                    stop_after = 0, postproc = 0, summary = 0, quiet = 0;
    339     vpx_codec_iface_t       *iface = NULL;
    340     unsigned int           is_ivf, fourcc;
    341     unsigned long          dx_time = 0;
    342     struct arg               arg;
    343     char                   **argv, **argi, **argj;
    344     const char                   *fn2 = 0;
    345     int                     use_y4m = 0;
    346     unsigned int            width;
    347     unsigned int            height;
    348     unsigned int            timebase_num;
    349     unsigned int            timebase_den;
    350     void                   *out = NULL;
    351     vpx_codec_dec_cfg_t     cfg = {0};
    352 #if CONFIG_VP8_DECODER
    353     vp8_postproc_cfg_t      vp8_pp_cfg = {0};
    354 #endif
    355 
    356     /* Parse command line */
    357     exec_name = argv_[0];
    358     argv = argv_dup(argc - 1, argv_ + 1);
    359 
    360     for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step)
    361     {
    362         memset(&arg, 0, sizeof(arg));
    363         arg.argv_step = 1;
    364 
    365         if (arg_match(&arg, &codecarg, argi))
    366         {
    367             int j, k = -1;
    368 
    369             for (j = 0; j < sizeof(ifaces) / sizeof(ifaces[0]); j++)
    370                 if (!strcmp(ifaces[j].name, arg.val))
    371                     k = j;
    372 
    373             if (k >= 0)
    374                 iface = ifaces[k].iface;
    375             else
    376                 die("Error: Unrecognized argument (%s) to --codec\n",
    377                     arg.val);
    378         }
    379         else if (arg_match(&arg, &outputfile, argi))
    380             fn2 = arg.val;
    381         else if (arg_match(&arg, &usey4marg, argi))
    382             use_y4m = 1;
    383         else if (arg_match(&arg, &prefixarg, argi))
    384             prefix = strdup(arg.val);
    385         else if (arg_match(&arg, &use_yv12, argi))
    386             flipuv = 1;
    387         else if (arg_match(&arg, &use_i420, argi))
    388             flipuv = 0;
    389         else if (arg_match(&arg, &flipuvarg, argi))
    390             flipuv = 1;
    391         else if (arg_match(&arg, &noblitarg, argi))
    392             noblit = 1;
    393         else if (arg_match(&arg, &progressarg, argi))
    394             progress = 1;
    395         else if (arg_match(&arg, &limitarg, argi))
    396             stop_after = arg_parse_uint(&arg);
    397         else if (arg_match(&arg, &postprocarg, argi))
    398             postproc = 1;
    399         else if (arg_match(&arg, &md5arg, argi))
    400             do_md5 = 1;
    401         else if (arg_match(&arg, &summaryarg, argi))
    402             summary = 1;
    403         else if (arg_match(&arg, &threadsarg, argi))
    404             cfg.threads = arg_parse_uint(&arg);
    405         else if (arg_match(&arg, &quietarg, argi))
    406             quiet = 1;
    407 
    408 #if CONFIG_VP8_DECODER
    409         else if (arg_match(&arg, &addnoise_level, argi))
    410         {
    411             postproc = 1;
    412             vp8_pp_cfg.post_proc_flag |= VP8_ADDNOISE;
    413             vp8_pp_cfg.noise_level = arg_parse_uint(&arg);
    414         }
    415         else if (arg_match(&arg, &demacroblock_level, argi))
    416         {
    417             postproc = 1;
    418             vp8_pp_cfg.post_proc_flag |= VP8_DEMACROBLOCK;
    419             vp8_pp_cfg.deblocking_level = arg_parse_uint(&arg);
    420         }
    421         else if (arg_match(&arg, &deblock, argi))
    422         {
    423             postproc = 1;
    424             vp8_pp_cfg.post_proc_flag |= VP8_DEBLOCK;
    425         }
    426         else if (arg_match(&arg, &pp_debug_info, argi))
    427         {
    428             unsigned int level = arg_parse_uint(&arg);
    429 
    430             postproc = 1;
    431             vp8_pp_cfg.post_proc_flag &= ~0x7;
    432 
    433             if (level)
    434                 vp8_pp_cfg.post_proc_flag |= 8 << (level - 1);
    435         }
    436 
    437 #endif
    438         else
    439             argj++;
    440     }
    441 
    442     /* Check for unrecognized options */
    443     for (argi = argv; *argi; argi++)
    444         if (argi[0][0] == '-' && strlen(argi[0]) > 1)
    445             die("Error: Unrecognized option %s\n", *argi);
    446 
    447     /* Handle non-option arguments */
    448     fn = argv[0];
    449 
    450     if (!fn)
    451         usage_exit();
    452 
    453     if (!prefix)
    454         prefix = strdup("img");
    455 
    456     /* Open file */
    457     infile = strcmp(fn, "-") ? fopen(fn, "rb") : stdin;
    458 
    459     if (!infile)
    460     {
    461         fprintf(stderr, "Failed to open file");
    462         return EXIT_FAILURE;
    463     }
    464 
    465     if (fn2)
    466         out = out_open(fn2, do_md5);
    467 
    468     is_ivf = file_is_ivf(infile, &fourcc, &width, &height,
    469                          &timebase_num, &timebase_den);
    470 
    471     if (is_ivf)
    472     {
    473         if (use_y4m)
    474         {
    475             char buffer[128];
    476             if (!fn2)
    477             {
    478                 fprintf(stderr, "YUV4MPEG2 output only supported with -o.\n");
    479                 return EXIT_FAILURE;
    480             }
    481             /*Correct for the factor of 2 applied to the timebase in the
    482                encoder.*/
    483             if(timebase_den&1)timebase_num<<=1;
    484             else timebase_den>>=1;
    485             /*Note: We can't output an aspect ratio here because IVF doesn't
    486                store one, and neither does VP8.
    487               That will have to wait until these tools support WebM natively.*/
    488             sprintf(buffer, "YUV4MPEG2 C%s W%u H%u F%u:%u I%c\n",
    489                     "420jpeg", width, height, timebase_den, timebase_num, 'p');
    490             out_put(out, (unsigned char *)buffer, strlen(buffer), do_md5);
    491         }
    492 
    493         /* Try to determine the codec from the fourcc. */
    494         for (i = 0; i < sizeof(ifaces) / sizeof(ifaces[0]); i++)
    495             if ((fourcc & ifaces[i].fourcc_mask) == ifaces[i].fourcc)
    496             {
    497                 vpx_codec_iface_t  *ivf_iface = ifaces[i].iface;
    498 
    499                 if (iface && iface != ivf_iface)
    500                     fprintf(stderr, "Notice -- IVF header indicates codec: %s\n",
    501                             ifaces[i].name);
    502                 else
    503                     iface = ivf_iface;
    504 
    505                 break;
    506             }
    507     }
    508     else if(use_y4m)
    509     {
    510         fprintf(stderr, "YUV4MPEG2 output only supported from IVF input.\n");
    511         return EXIT_FAILURE;
    512     }
    513 
    514     if (vpx_codec_dec_init(&decoder, iface ? iface :  ifaces[0].iface, &cfg,
    515                            postproc ? VPX_CODEC_USE_POSTPROC : 0))
    516     {
    517         fprintf(stderr, "Failed to initialize decoder: %s\n", vpx_codec_error(&decoder));
    518         return EXIT_FAILURE;
    519     }
    520 
    521     if (!quiet)
    522         fprintf(stderr, "%s\n", decoder.name);
    523 
    524 #if CONFIG_VP8_DECODER
    525 
    526     if (vp8_pp_cfg.post_proc_flag
    527         && vpx_codec_control(&decoder, VP8_SET_POSTPROC, &vp8_pp_cfg))
    528     {
    529         fprintf(stderr, "Failed to configure postproc: %s\n", vpx_codec_error(&decoder));
    530         return EXIT_FAILURE;
    531     }
    532 
    533 #endif
    534 
    535     /* Decode file */
    536     while (!read_frame(infile, &buf, &buf_sz, &buf_alloc_sz, is_ivf))
    537     {
    538         vpx_codec_iter_t  iter = NULL;
    539         vpx_image_t    *img;
    540         struct vpx_usec_timer timer;
    541 
    542         vpx_usec_timer_start(&timer);
    543 
    544         if (vpx_codec_decode(&decoder, buf, buf_sz, NULL, 0))
    545         {
    546             const char *detail = vpx_codec_error_detail(&decoder);
    547             fprintf(stderr, "Failed to decode frame: %s\n", vpx_codec_error(&decoder));
    548 
    549             if (detail)
    550                 fprintf(stderr, "  Additional information: %s\n", detail);
    551 
    552             goto fail;
    553         }
    554 
    555         vpx_usec_timer_mark(&timer);
    556         dx_time += vpx_usec_timer_elapsed(&timer);
    557 
    558         ++frame_in;
    559 
    560         if (progress)
    561             fprintf(stderr, "decoded frame %d.\n", frame_in);
    562 
    563         if ((img = vpx_codec_get_frame(&decoder, &iter)))
    564             ++frame_out;
    565 
    566         if (!noblit)
    567         {
    568             if (img)
    569             {
    570                 unsigned int y;
    571                 char out_fn[128+24];
    572                 uint8_t *buf;
    573                 const char *sfx = flipuv ? "yv12" : "i420";
    574 
    575                 if (!fn2)
    576                 {
    577                     sprintf(out_fn, "%s-%dx%d-%04d.%s",
    578                             prefix, img->d_w, img->d_h, frame_in, sfx);
    579                     out = out_open(out_fn, do_md5);
    580                 }
    581                 else if(use_y4m)
    582                     out_put(out, (unsigned char *)"FRAME\n", 6, do_md5);
    583 
    584                 buf = img->planes[VPX_PLANE_Y];
    585 
    586                 for (y = 0; y < img->d_h; y++)
    587                 {
    588                     out_put(out, buf, img->d_w, do_md5);
    589                     buf += img->stride[VPX_PLANE_Y];
    590                 }
    591 
    592                 buf = img->planes[flipuv?VPX_PLANE_V:VPX_PLANE_U];
    593 
    594                 for (y = 0; y < (1 + img->d_h) / 2; y++)
    595                 {
    596                     out_put(out, buf, (1 + img->d_w) / 2, do_md5);
    597                     buf += img->stride[VPX_PLANE_U];
    598                 }
    599 
    600                 buf = img->planes[flipuv?VPX_PLANE_U:VPX_PLANE_V];
    601 
    602                 for (y = 0; y < (1 + img->d_h) / 2; y++)
    603                 {
    604                     out_put(out, buf, (1 + img->d_w) / 2, do_md5);
    605                     buf += img->stride[VPX_PLANE_V];
    606                 }
    607 
    608                 if (!fn2)
    609                     out_close(out, out_fn, do_md5);
    610             }
    611         }
    612 
    613         if (stop_after && frame_in >= stop_after)
    614             break;
    615     }
    616 
    617     if (summary)
    618     {
    619         fprintf(stderr, "%d decoded frames/%d showed frames in %lu us (%.2f fps)\n",
    620                 frame_in, frame_out, dx_time, (float)frame_out * 1000000.0 / (float)dx_time);
    621     }
    622 
    623 fail:
    624 
    625     if (vpx_codec_destroy(&decoder))
    626     {
    627         fprintf(stderr, "Failed to destroy decoder: %s\n", vpx_codec_error(&decoder));
    628         return EXIT_FAILURE;
    629     }
    630 
    631     if (fn2)
    632         out_close(out, fn2, do_md5);
    633 
    634     free(buf);
    635     fclose(infile);
    636     free(prefix);
    637     free(argv);
    638 
    639     return EXIT_SUCCESS;
    640 }
    641