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