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