1 /* 2 * Copyright (c) 2012 The WebRTC 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 "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 12 13 #include <assert.h> 14 #include <string.h> 15 16 // NOTE(ajm): Path provided by gyp. 17 #include "libyuv.h" // NOLINT 18 19 namespace webrtc { 20 21 const int k16ByteAlignment = 16; 22 23 VideoType RawVideoTypeToCommonVideoVideoType(RawVideoType type) { 24 switch (type) { 25 case kVideoI420: 26 return kI420; 27 case kVideoIYUV: 28 return kIYUV; 29 case kVideoRGB24: 30 return kRGB24; 31 case kVideoARGB: 32 return kARGB; 33 case kVideoARGB4444: 34 return kARGB4444; 35 case kVideoRGB565: 36 return kRGB565; 37 case kVideoARGB1555: 38 return kARGB1555; 39 case kVideoYUY2: 40 return kYUY2; 41 case kVideoYV12: 42 return kYV12; 43 case kVideoUYVY: 44 return kUYVY; 45 case kVideoNV21: 46 return kNV21; 47 case kVideoNV12: 48 return kNV12; 49 case kVideoBGRA: 50 return kBGRA; 51 case kVideoMJPEG: 52 return kMJPG; 53 default: 54 assert(false); 55 } 56 return kUnknown; 57 } 58 59 int AlignInt(int value, int alignment) { 60 assert(!((alignment - 1) & alignment)); 61 return ((value + alignment - 1) & ~ (alignment - 1)); 62 } 63 64 void Calc16ByteAlignedStride(int width, int* stride_y, int* stride_uv) { 65 *stride_y = AlignInt(width, k16ByteAlignment); 66 *stride_uv = AlignInt((width + 1) / 2, k16ByteAlignment); 67 } 68 69 int CalcBufferSize(VideoType type, int width, int height) { 70 int buffer_size = 0; 71 switch (type) { 72 case kI420: 73 case kNV12: 74 case kNV21: 75 case kIYUV: 76 case kYV12: { 77 int half_width = (width + 1) >> 1; 78 int half_height = (height + 1) >> 1; 79 buffer_size = width * height + half_width * half_height * 2; 80 break; 81 } 82 case kARGB4444: 83 case kRGB565: 84 case kARGB1555: 85 case kYUY2: 86 case kUYVY: 87 buffer_size = width * height * 2; 88 break; 89 case kRGB24: 90 buffer_size = width * height * 3; 91 break; 92 case kBGRA: 93 case kARGB: 94 buffer_size = width * height * 4; 95 break; 96 default: 97 assert(false); 98 return -1; 99 } 100 return buffer_size; 101 } 102 103 int PrintI420VideoFrame(const I420VideoFrame& frame, FILE* file) { 104 if (file == NULL) 105 return -1; 106 if (frame.IsZeroSize()) 107 return -1; 108 for (int planeNum = 0; planeNum < kNumOfPlanes; ++planeNum) { 109 int width = (planeNum ? (frame.width() + 1) / 2 : frame.width()); 110 int height = (planeNum ? (frame.height() + 1) / 2 : frame.height()); 111 PlaneType plane_type = static_cast<PlaneType>(planeNum); 112 const uint8_t* plane_buffer = frame.buffer(plane_type); 113 for (int y = 0; y < height; y++) { 114 if (fwrite(plane_buffer, 1, width, file) != 115 static_cast<unsigned int>(width)) { 116 return -1; 117 } 118 plane_buffer += frame.stride(plane_type); 119 } 120 } 121 return 0; 122 } 123 124 int ExtractBuffer(const I420VideoFrame& input_frame, 125 int size, uint8_t* buffer) { 126 assert(buffer); 127 if (input_frame.IsZeroSize()) 128 return -1; 129 int length = CalcBufferSize(kI420, input_frame.width(), input_frame.height()); 130 if (size < length) { 131 return -1; 132 } 133 134 int pos = 0; 135 uint8_t* buffer_ptr = buffer; 136 137 for (int plane = 0; plane < kNumOfPlanes; ++plane) { 138 int width = (plane ? (input_frame.width() + 1) / 2 : 139 input_frame.width()); 140 int height = (plane ? (input_frame.height() + 1) / 2 : 141 input_frame.height()); 142 const uint8_t* plane_ptr = input_frame.buffer( 143 static_cast<PlaneType>(plane)); 144 for (int y = 0; y < height; y++) { 145 memcpy(&buffer_ptr[pos], plane_ptr, width); 146 pos += width; 147 plane_ptr += input_frame.stride(static_cast<PlaneType>(plane)); 148 } 149 } 150 return length; 151 } 152 153 154 int ConvertNV12ToRGB565(const uint8_t* src_frame, 155 uint8_t* dst_frame, 156 int width, int height) { 157 int abs_height = (height < 0) ? -height : height; 158 const uint8_t* yplane = src_frame; 159 const uint8_t* uvInterlaced = src_frame + (width * abs_height); 160 161 return libyuv::NV12ToRGB565(yplane, width, 162 uvInterlaced, (width + 1) >> 1, 163 dst_frame, width, 164 width, height); 165 } 166 167 int ConvertRGB24ToARGB(const uint8_t* src_frame, uint8_t* dst_frame, 168 int width, int height, int dst_stride) { 169 if (dst_stride == 0) 170 dst_stride = width; 171 return libyuv::RGB24ToARGB(src_frame, width, 172 dst_frame, dst_stride, 173 width, height); 174 } 175 176 libyuv::RotationMode ConvertRotationMode(VideoRotationMode rotation) { 177 switch(rotation) { 178 case kRotateNone: 179 return libyuv::kRotate0; 180 case kRotate90: 181 return libyuv::kRotate90; 182 case kRotate180: 183 return libyuv::kRotate180; 184 case kRotate270: 185 return libyuv::kRotate270; 186 } 187 assert(false); 188 return libyuv::kRotate0; 189 } 190 191 int ConvertVideoType(VideoType video_type) { 192 switch(video_type) { 193 case kUnknown: 194 return libyuv::FOURCC_ANY; 195 case kI420: 196 return libyuv::FOURCC_I420; 197 case kIYUV: // same as KYV12 198 case kYV12: 199 return libyuv::FOURCC_YV12; 200 case kRGB24: 201 return libyuv::FOURCC_24BG; 202 case kABGR: 203 return libyuv::FOURCC_ABGR; 204 case kRGB565: 205 return libyuv::FOURCC_RGBP; 206 case kYUY2: 207 return libyuv::FOURCC_YUY2; 208 case kUYVY: 209 return libyuv::FOURCC_UYVY; 210 case kMJPG: 211 return libyuv::FOURCC_MJPG; 212 case kNV21: 213 return libyuv::FOURCC_NV21; 214 case kNV12: 215 return libyuv::FOURCC_NV12; 216 case kARGB: 217 return libyuv::FOURCC_ARGB; 218 case kBGRA: 219 return libyuv::FOURCC_BGRA; 220 case kARGB4444: 221 return libyuv::FOURCC_R444; 222 case kARGB1555: 223 return libyuv::FOURCC_RGBO; 224 } 225 assert(false); 226 return libyuv::FOURCC_ANY; 227 } 228 229 int ConvertToI420(VideoType src_video_type, 230 const uint8_t* src_frame, 231 int crop_x, int crop_y, 232 int src_width, int src_height, 233 int sample_size, 234 VideoRotationMode rotation, 235 I420VideoFrame* dst_frame) { 236 int dst_width = dst_frame->width(); 237 int dst_height = dst_frame->height(); 238 // LibYuv expects pre-rotation values for dst. 239 // Stride values should correspond to the destination values. 240 if (rotation == kRotate90 || rotation == kRotate270) { 241 dst_width = dst_frame->height(); 242 dst_height =dst_frame->width(); 243 } 244 return libyuv::ConvertToI420(src_frame, sample_size, 245 dst_frame->buffer(kYPlane), 246 dst_frame->stride(kYPlane), 247 dst_frame->buffer(kUPlane), 248 dst_frame->stride(kUPlane), 249 dst_frame->buffer(kVPlane), 250 dst_frame->stride(kVPlane), 251 crop_x, crop_y, 252 src_width, src_height, 253 dst_width, dst_height, 254 ConvertRotationMode(rotation), 255 ConvertVideoType(src_video_type)); 256 } 257 258 int ConvertFromI420(const I420VideoFrame& src_frame, 259 VideoType dst_video_type, int dst_sample_size, 260 uint8_t* dst_frame) { 261 return libyuv::ConvertFromI420(src_frame.buffer(kYPlane), 262 src_frame.stride(kYPlane), 263 src_frame.buffer(kUPlane), 264 src_frame.stride(kUPlane), 265 src_frame.buffer(kVPlane), 266 src_frame.stride(kVPlane), 267 dst_frame, dst_sample_size, 268 src_frame.width(), src_frame.height(), 269 ConvertVideoType(dst_video_type)); 270 } 271 272 // TODO(mikhal): Create a designated VideoFrame for non I420. 273 int ConvertFromYV12(const I420VideoFrame& src_frame, 274 VideoType dst_video_type, int dst_sample_size, 275 uint8_t* dst_frame) { 276 // YV12 = Y, V, U 277 return libyuv::ConvertFromI420(src_frame.buffer(kYPlane), 278 src_frame.stride(kYPlane), 279 src_frame.buffer(kVPlane), 280 src_frame.stride(kVPlane), 281 src_frame.buffer(kUPlane), 282 src_frame.stride(kUPlane), 283 dst_frame, dst_sample_size, 284 src_frame.width(), src_frame.height(), 285 ConvertVideoType(dst_video_type)); 286 } 287 288 int MirrorI420LeftRight(const I420VideoFrame* src_frame, 289 I420VideoFrame* dst_frame) { 290 // Source and destination frames should have equal resolution. 291 if (src_frame->width() != dst_frame->width() || 292 src_frame->height() != dst_frame->height()) 293 return -1; 294 return libyuv::I420Mirror(src_frame->buffer(kYPlane), 295 src_frame->stride(kYPlane), 296 src_frame->buffer(kUPlane), 297 src_frame->stride(kUPlane), 298 src_frame->buffer(kVPlane), 299 src_frame->stride(kVPlane), 300 dst_frame->buffer(kYPlane), 301 dst_frame->stride(kYPlane), 302 dst_frame->buffer(kUPlane), 303 dst_frame->stride(kUPlane), 304 dst_frame->buffer(kVPlane), 305 dst_frame->stride(kVPlane), 306 src_frame->width(), src_frame->height()); 307 } 308 309 int MirrorI420UpDown(const I420VideoFrame* src_frame, 310 I420VideoFrame* dst_frame) { 311 // Source and destination frames should have equal resolution 312 if (src_frame->width() != dst_frame->width() || 313 src_frame->height() != dst_frame->height()) 314 return -1; 315 316 // Inserting negative height flips the frame. 317 return libyuv::I420Copy(src_frame->buffer(kYPlane), 318 src_frame->stride(kYPlane), 319 src_frame->buffer(kUPlane), 320 src_frame->stride(kUPlane), 321 src_frame->buffer(kVPlane), 322 src_frame->stride(kVPlane), 323 dst_frame->buffer(kYPlane), 324 dst_frame->stride(kYPlane), 325 dst_frame->buffer(kUPlane), 326 dst_frame->stride(kUPlane), 327 dst_frame->buffer(kVPlane), 328 dst_frame->stride(kVPlane), 329 src_frame->width(), -(src_frame->height())); 330 } 331 332 // Compute PSNR for an I420 frame (all planes) 333 double I420PSNR(const I420VideoFrame* ref_frame, 334 const I420VideoFrame* test_frame) { 335 if (!ref_frame || !test_frame) 336 return -1; 337 else if ((ref_frame->width() != test_frame->width()) || 338 (ref_frame->height() != test_frame->height())) 339 return -1; 340 else if (ref_frame->width() < 0 || ref_frame->height() < 0) 341 return -1; 342 343 double psnr = libyuv::I420Psnr(ref_frame->buffer(kYPlane), 344 ref_frame->stride(kYPlane), 345 ref_frame->buffer(kUPlane), 346 ref_frame->stride(kUPlane), 347 ref_frame->buffer(kVPlane), 348 ref_frame->stride(kVPlane), 349 test_frame->buffer(kYPlane), 350 test_frame->stride(kYPlane), 351 test_frame->buffer(kUPlane), 352 test_frame->stride(kUPlane), 353 test_frame->buffer(kVPlane), 354 test_frame->stride(kVPlane), 355 test_frame->width(), test_frame->height()); 356 // LibYuv sets the max psnr value to 128, we restrict it here. 357 // In case of 0 mse in one frame, 128 can skew the results significantly. 358 return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr; 359 } 360 361 // Compute SSIM for an I420 frame (all planes) 362 double I420SSIM(const I420VideoFrame* ref_frame, 363 const I420VideoFrame* test_frame) { 364 if (!ref_frame || !test_frame) 365 return -1; 366 else if ((ref_frame->width() != test_frame->width()) || 367 (ref_frame->height() != test_frame->height())) 368 return -1; 369 else if (ref_frame->width() < 0 || ref_frame->height() < 0) 370 return -1; 371 372 return libyuv::I420Ssim(ref_frame->buffer(kYPlane), 373 ref_frame->stride(kYPlane), 374 ref_frame->buffer(kUPlane), 375 ref_frame->stride(kUPlane), 376 ref_frame->buffer(kVPlane), 377 ref_frame->stride(kVPlane), 378 test_frame->buffer(kYPlane), 379 test_frame->stride(kYPlane), 380 test_frame->buffer(kUPlane), 381 test_frame->stride(kUPlane), 382 test_frame->buffer(kVPlane), 383 test_frame->stride(kVPlane), 384 test_frame->width(), test_frame->height()); 385 } 386 } // namespace webrtc 387