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 #include <math.h>
     12 #include <stdarg.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 
     17 #include "./tools_common.h"
     18 
     19 #if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
     20 #include "vpx/vp8cx.h"
     21 #endif
     22 
     23 #if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
     24 #include "vpx/vp8dx.h"
     25 #endif
     26 
     27 #if defined(_WIN32) || defined(__OS2__)
     28 #include <io.h>
     29 #include <fcntl.h>
     30 
     31 #ifdef __OS2__
     32 #define _setmode setmode
     33 #define _fileno fileno
     34 #define _O_BINARY O_BINARY
     35 #endif
     36 #endif
     37 
     38 #define LOG_ERROR(label)               \
     39   do {                                 \
     40     const char *l = label;             \
     41     va_list ap;                        \
     42     va_start(ap, fmt);                 \
     43     if (l) fprintf(stderr, "%s: ", l); \
     44     vfprintf(stderr, fmt, ap);         \
     45     fprintf(stderr, "\n");             \
     46     va_end(ap);                        \
     47   } while (0)
     48 
     49 FILE *set_binary_mode(FILE *stream) {
     50   (void)stream;
     51 #if defined(_WIN32) || defined(__OS2__)
     52   _setmode(_fileno(stream), _O_BINARY);
     53 #endif
     54   return stream;
     55 }
     56 
     57 void die(const char *fmt, ...) {
     58   LOG_ERROR(NULL);
     59   usage_exit();
     60 }
     61 
     62 void fatal(const char *fmt, ...) {
     63   LOG_ERROR("Fatal");
     64   exit(EXIT_FAILURE);
     65 }
     66 
     67 void warn(const char *fmt, ...) { LOG_ERROR("Warning"); }
     68 
     69 void die_codec(vpx_codec_ctx_t *ctx, const char *s) {
     70   const char *detail = vpx_codec_error_detail(ctx);
     71 
     72   printf("%s: %s\n", s, vpx_codec_error(ctx));
     73   if (detail) printf("    %s\n", detail);
     74   exit(EXIT_FAILURE);
     75 }
     76 
     77 int read_yuv_frame(struct VpxInputContext *input_ctx, vpx_image_t *yuv_frame) {
     78   FILE *f = input_ctx->file;
     79   struct FileTypeDetectionBuffer *detect = &input_ctx->detect;
     80   int plane = 0;
     81   int shortread = 0;
     82   const int bytespp = (yuv_frame->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
     83 
     84   for (plane = 0; plane < 3; ++plane) {
     85     uint8_t *ptr;
     86     const int w = vpx_img_plane_width(yuv_frame, plane);
     87     const int h = vpx_img_plane_height(yuv_frame, plane);
     88     int r;
     89 
     90     /* Determine the correct plane based on the image format. The for-loop
     91      * always counts in Y,U,V order, but this may not match the order of
     92      * the data on disk.
     93      */
     94     switch (plane) {
     95       case 1:
     96         ptr =
     97             yuv_frame->planes[yuv_frame->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_V
     98                                                                  : VPX_PLANE_U];
     99         break;
    100       case 2:
    101         ptr =
    102             yuv_frame->planes[yuv_frame->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_U
    103                                                                  : VPX_PLANE_V];
    104         break;
    105       default: ptr = yuv_frame->planes[plane];
    106     }
    107 
    108     for (r = 0; r < h; ++r) {
    109       size_t needed = w * bytespp;
    110       size_t buf_position = 0;
    111       const size_t left = detect->buf_read - detect->position;
    112       if (left > 0) {
    113         const size_t more = (left < needed) ? left : needed;
    114         memcpy(ptr, detect->buf + detect->position, more);
    115         buf_position = more;
    116         needed -= more;
    117         detect->position += more;
    118       }
    119       if (needed > 0) {
    120         shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
    121       }
    122 
    123       ptr += yuv_frame->stride[plane];
    124     }
    125   }
    126 
    127   return shortread;
    128 }
    129 
    130 #if CONFIG_ENCODERS
    131 
    132 static const VpxInterface vpx_encoders[] = {
    133 #if CONFIG_VP8_ENCODER
    134   { "vp8", VP8_FOURCC, &vpx_codec_vp8_cx },
    135 #endif
    136 
    137 #if CONFIG_VP9_ENCODER
    138   { "vp9", VP9_FOURCC, &vpx_codec_vp9_cx },
    139 #endif
    140 };
    141 
    142 int get_vpx_encoder_count(void) {
    143   return sizeof(vpx_encoders) / sizeof(vpx_encoders[0]);
    144 }
    145 
    146 const VpxInterface *get_vpx_encoder_by_index(int i) { return &vpx_encoders[i]; }
    147 
    148 const VpxInterface *get_vpx_encoder_by_name(const char *name) {
    149   int i;
    150 
    151   for (i = 0; i < get_vpx_encoder_count(); ++i) {
    152     const VpxInterface *encoder = get_vpx_encoder_by_index(i);
    153     if (strcmp(encoder->name, name) == 0) return encoder;
    154   }
    155 
    156   return NULL;
    157 }
    158 
    159 #endif  // CONFIG_ENCODERS
    160 
    161 #if CONFIG_DECODERS
    162 
    163 static const VpxInterface vpx_decoders[] = {
    164 #if CONFIG_VP8_DECODER
    165   { "vp8", VP8_FOURCC, &vpx_codec_vp8_dx },
    166 #endif
    167 
    168 #if CONFIG_VP9_DECODER
    169   { "vp9", VP9_FOURCC, &vpx_codec_vp9_dx },
    170 #endif
    171 };
    172 
    173 int get_vpx_decoder_count(void) {
    174   return sizeof(vpx_decoders) / sizeof(vpx_decoders[0]);
    175 }
    176 
    177 const VpxInterface *get_vpx_decoder_by_index(int i) { return &vpx_decoders[i]; }
    178 
    179 const VpxInterface *get_vpx_decoder_by_name(const char *name) {
    180   int i;
    181 
    182   for (i = 0; i < get_vpx_decoder_count(); ++i) {
    183     const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
    184     if (strcmp(decoder->name, name) == 0) return decoder;
    185   }
    186 
    187   return NULL;
    188 }
    189 
    190 const VpxInterface *get_vpx_decoder_by_fourcc(uint32_t fourcc) {
    191   int i;
    192 
    193   for (i = 0; i < get_vpx_decoder_count(); ++i) {
    194     const VpxInterface *const decoder = get_vpx_decoder_by_index(i);
    195     if (decoder->fourcc == fourcc) return decoder;
    196   }
    197 
    198   return NULL;
    199 }
    200 
    201 #endif  // CONFIG_DECODERS
    202 
    203 // TODO(dkovalev): move this function to vpx_image.{c, h}, so it will be part
    204 // of vpx_image_t support
    205 int vpx_img_plane_width(const vpx_image_t *img, int plane) {
    206   if (plane > 0 && img->x_chroma_shift > 0)
    207     return (img->d_w + 1) >> img->x_chroma_shift;
    208   else
    209     return img->d_w;
    210 }
    211 
    212 int vpx_img_plane_height(const vpx_image_t *img, int plane) {
    213   if (plane > 0 && img->y_chroma_shift > 0)
    214     return (img->d_h + 1) >> img->y_chroma_shift;
    215   else
    216     return img->d_h;
    217 }
    218 
    219 void vpx_img_write(const vpx_image_t *img, FILE *file) {
    220   int plane;
    221 
    222   for (plane = 0; plane < 3; ++plane) {
    223     const unsigned char *buf = img->planes[plane];
    224     const int stride = img->stride[plane];
    225     const int w = vpx_img_plane_width(img, plane) *
    226                   ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    227     const int h = vpx_img_plane_height(img, plane);
    228     int y;
    229 
    230     for (y = 0; y < h; ++y) {
    231       fwrite(buf, 1, w, file);
    232       buf += stride;
    233     }
    234   }
    235 }
    236 
    237 int vpx_img_read(vpx_image_t *img, FILE *file) {
    238   int plane;
    239 
    240   for (plane = 0; plane < 3; ++plane) {
    241     unsigned char *buf = img->planes[plane];
    242     const int stride = img->stride[plane];
    243     const int w = vpx_img_plane_width(img, plane) *
    244                   ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    245     const int h = vpx_img_plane_height(img, plane);
    246     int y;
    247 
    248     for (y = 0; y < h; ++y) {
    249       if (fread(buf, 1, w, file) != (size_t)w) return 0;
    250       buf += stride;
    251     }
    252   }
    253 
    254   return 1;
    255 }
    256 
    257 // TODO(dkovalev) change sse_to_psnr signature: double -> int64_t
    258 double sse_to_psnr(double samples, double peak, double sse) {
    259   static const double kMaxPSNR = 100.0;
    260 
    261   if (sse > 0.0) {
    262     const double psnr = 10.0 * log10(samples * peak * peak / sse);
    263     return psnr > kMaxPSNR ? kMaxPSNR : psnr;
    264   } else {
    265     return kMaxPSNR;
    266   }
    267 }
    268 
    269 // TODO(debargha): Consolidate the functions below into a separate file.
    270 #if CONFIG_VP9_HIGHBITDEPTH
    271 static void highbd_img_upshift(vpx_image_t *dst, vpx_image_t *src,
    272                                int input_shift) {
    273   // Note the offset is 1 less than half.
    274   const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
    275   int plane;
    276   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    277       dst->x_chroma_shift != src->x_chroma_shift ||
    278       dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
    279       input_shift < 0) {
    280     fatal("Unsupported image conversion");
    281   }
    282   switch (src->fmt) {
    283     case VPX_IMG_FMT_I42016:
    284     case VPX_IMG_FMT_I42216:
    285     case VPX_IMG_FMT_I44416:
    286     case VPX_IMG_FMT_I44016: break;
    287     default: fatal("Unsupported image conversion"); break;
    288   }
    289   for (plane = 0; plane < 3; plane++) {
    290     int w = src->d_w;
    291     int h = src->d_h;
    292     int x, y;
    293     if (plane) {
    294       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    295       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    296     }
    297     for (y = 0; y < h; y++) {
    298       uint16_t *p_src =
    299           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    300       uint16_t *p_dst =
    301           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    302       for (x = 0; x < w; x++) *p_dst++ = (*p_src++ << input_shift) + offset;
    303     }
    304   }
    305 }
    306 
    307 static void lowbd_img_upshift(vpx_image_t *dst, vpx_image_t *src,
    308                               int input_shift) {
    309   // Note the offset is 1 less than half.
    310   const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
    311   int plane;
    312   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    313       dst->x_chroma_shift != src->x_chroma_shift ||
    314       dst->y_chroma_shift != src->y_chroma_shift ||
    315       dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH || input_shift < 0) {
    316     fatal("Unsupported image conversion");
    317   }
    318   switch (src->fmt) {
    319     case VPX_IMG_FMT_I420:
    320     case VPX_IMG_FMT_I422:
    321     case VPX_IMG_FMT_I444:
    322     case VPX_IMG_FMT_I440: break;
    323     default: fatal("Unsupported image conversion"); break;
    324   }
    325   for (plane = 0; plane < 3; plane++) {
    326     int w = src->d_w;
    327     int h = src->d_h;
    328     int x, y;
    329     if (plane) {
    330       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    331       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    332     }
    333     for (y = 0; y < h; y++) {
    334       uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
    335       uint16_t *p_dst =
    336           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    337       for (x = 0; x < w; x++) {
    338         *p_dst++ = (*p_src++ << input_shift) + offset;
    339       }
    340     }
    341   }
    342 }
    343 
    344 void vpx_img_upshift(vpx_image_t *dst, vpx_image_t *src, int input_shift) {
    345   if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
    346     highbd_img_upshift(dst, src, input_shift);
    347   } else {
    348     lowbd_img_upshift(dst, src, input_shift);
    349   }
    350 }
    351 
    352 void vpx_img_truncate_16_to_8(vpx_image_t *dst, vpx_image_t *src) {
    353   int plane;
    354   if (dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH != src->fmt || dst->d_w != src->d_w ||
    355       dst->d_h != src->d_h || dst->x_chroma_shift != src->x_chroma_shift ||
    356       dst->y_chroma_shift != src->y_chroma_shift) {
    357     fatal("Unsupported image conversion");
    358   }
    359   switch (dst->fmt) {
    360     case VPX_IMG_FMT_I420:
    361     case VPX_IMG_FMT_I422:
    362     case VPX_IMG_FMT_I444:
    363     case VPX_IMG_FMT_I440: break;
    364     default: fatal("Unsupported image conversion"); break;
    365   }
    366   for (plane = 0; plane < 3; plane++) {
    367     int w = src->d_w;
    368     int h = src->d_h;
    369     int x, y;
    370     if (plane) {
    371       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    372       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    373     }
    374     for (y = 0; y < h; y++) {
    375       uint16_t *p_src =
    376           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    377       uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
    378       for (x = 0; x < w; x++) {
    379         *p_dst++ = (uint8_t)(*p_src++);
    380       }
    381     }
    382   }
    383 }
    384 
    385 static void highbd_img_downshift(vpx_image_t *dst, vpx_image_t *src,
    386                                  int down_shift) {
    387   int plane;
    388   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    389       dst->x_chroma_shift != src->x_chroma_shift ||
    390       dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
    391       down_shift < 0) {
    392     fatal("Unsupported image conversion");
    393   }
    394   switch (src->fmt) {
    395     case VPX_IMG_FMT_I42016:
    396     case VPX_IMG_FMT_I42216:
    397     case VPX_IMG_FMT_I44416:
    398     case VPX_IMG_FMT_I44016: break;
    399     default: fatal("Unsupported image conversion"); break;
    400   }
    401   for (plane = 0; plane < 3; plane++) {
    402     int w = src->d_w;
    403     int h = src->d_h;
    404     int x, y;
    405     if (plane) {
    406       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    407       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    408     }
    409     for (y = 0; y < h; y++) {
    410       uint16_t *p_src =
    411           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    412       uint16_t *p_dst =
    413           (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    414       for (x = 0; x < w; x++) *p_dst++ = *p_src++ >> down_shift;
    415     }
    416   }
    417 }
    418 
    419 static void lowbd_img_downshift(vpx_image_t *dst, vpx_image_t *src,
    420                                 int down_shift) {
    421   int plane;
    422   if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    423       dst->x_chroma_shift != src->x_chroma_shift ||
    424       dst->y_chroma_shift != src->y_chroma_shift ||
    425       src->fmt != dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH || down_shift < 0) {
    426     fatal("Unsupported image conversion");
    427   }
    428   switch (dst->fmt) {
    429     case VPX_IMG_FMT_I420:
    430     case VPX_IMG_FMT_I422:
    431     case VPX_IMG_FMT_I444:
    432     case VPX_IMG_FMT_I440: break;
    433     default: fatal("Unsupported image conversion"); break;
    434   }
    435   for (plane = 0; plane < 3; plane++) {
    436     int w = src->d_w;
    437     int h = src->d_h;
    438     int x, y;
    439     if (plane) {
    440       w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    441       h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    442     }
    443     for (y = 0; y < h; y++) {
    444       uint16_t *p_src =
    445           (uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    446       uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
    447       for (x = 0; x < w; x++) {
    448         *p_dst++ = *p_src++ >> down_shift;
    449       }
    450     }
    451   }
    452 }
    453 
    454 void vpx_img_downshift(vpx_image_t *dst, vpx_image_t *src, int down_shift) {
    455   if (dst->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
    456     highbd_img_downshift(dst, src, down_shift);
    457   } else {
    458     lowbd_img_downshift(dst, src, down_shift);
    459   }
    460 }
    461 #endif  // CONFIG_VP9_HIGHBITDEPTH
    462