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 size_t CalcBufferSize(VideoType type, int width, int height) { 70 assert(width >= 0); 71 assert(height >= 0); 72 size_t buffer_size = 0; 73 switch (type) { 74 case kI420: 75 case kNV12: 76 case kNV21: 77 case kIYUV: 78 case kYV12: { 79 int half_width = (width + 1) >> 1; 80 int half_height = (height + 1) >> 1; 81 buffer_size = width * height + half_width * half_height * 2; 82 break; 83 } 84 case kARGB4444: 85 case kRGB565: 86 case kARGB1555: 87 case kYUY2: 88 case kUYVY: 89 buffer_size = width * height * 2; 90 break; 91 case kRGB24: 92 buffer_size = width * height * 3; 93 break; 94 case kBGRA: 95 case kARGB: 96 buffer_size = width * height * 4; 97 break; 98 default: 99 assert(false); 100 break; 101 } 102 return buffer_size; 103 } 104 105 int PrintVideoFrame(const VideoFrame& frame, FILE* file) { 106 if (file == NULL) 107 return -1; 108 if (frame.IsZeroSize()) 109 return -1; 110 for (int planeNum = 0; planeNum < kNumOfPlanes; ++planeNum) { 111 int width = (planeNum ? (frame.width() + 1) / 2 : frame.width()); 112 int height = (planeNum ? (frame.height() + 1) / 2 : frame.height()); 113 PlaneType plane_type = static_cast<PlaneType>(planeNum); 114 const uint8_t* plane_buffer = frame.buffer(plane_type); 115 for (int y = 0; y < height; y++) { 116 if (fwrite(plane_buffer, 1, width, file) != 117 static_cast<unsigned int>(width)) { 118 return -1; 119 } 120 plane_buffer += frame.stride(plane_type); 121 } 122 } 123 return 0; 124 } 125 126 int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer) { 127 assert(buffer); 128 if (input_frame.IsZeroSize()) 129 return -1; 130 size_t length = 131 CalcBufferSize(kI420, input_frame.width(), input_frame.height()); 132 if (size < length) { 133 return -1; 134 } 135 136 int pos = 0; 137 uint8_t* buffer_ptr = buffer; 138 139 for (int plane = 0; plane < kNumOfPlanes; ++plane) { 140 int width = (plane ? (input_frame.width() + 1) / 2 : 141 input_frame.width()); 142 int height = (plane ? (input_frame.height() + 1) / 2 : 143 input_frame.height()); 144 const uint8_t* plane_ptr = input_frame.buffer( 145 static_cast<PlaneType>(plane)); 146 for (int y = 0; y < height; y++) { 147 memcpy(&buffer_ptr[pos], plane_ptr, width); 148 pos += width; 149 plane_ptr += input_frame.stride(static_cast<PlaneType>(plane)); 150 } 151 } 152 return static_cast<int>(length); 153 } 154 155 156 int ConvertNV12ToRGB565(const uint8_t* src_frame, 157 uint8_t* dst_frame, 158 int width, int height) { 159 int abs_height = (height < 0) ? -height : height; 160 const uint8_t* yplane = src_frame; 161 const uint8_t* uvInterlaced = src_frame + (width * abs_height); 162 163 return libyuv::NV12ToRGB565(yplane, width, 164 uvInterlaced, (width + 1) >> 1, 165 dst_frame, width, 166 width, height); 167 } 168 169 int ConvertRGB24ToARGB(const uint8_t* src_frame, uint8_t* dst_frame, 170 int width, int height, int dst_stride) { 171 if (dst_stride == 0) 172 dst_stride = width; 173 return libyuv::RGB24ToARGB(src_frame, width, 174 dst_frame, dst_stride, 175 width, height); 176 } 177 178 libyuv::RotationMode ConvertRotationMode(VideoRotation rotation) { 179 switch (rotation) { 180 case kVideoRotation_0: 181 return libyuv::kRotate0; 182 case kVideoRotation_90: 183 return libyuv::kRotate90; 184 case kVideoRotation_180: 185 return libyuv::kRotate180; 186 case kVideoRotation_270: 187 return libyuv::kRotate270; 188 } 189 assert(false); 190 return libyuv::kRotate0; 191 } 192 193 int ConvertVideoType(VideoType video_type) { 194 switch (video_type) { 195 case kUnknown: 196 return libyuv::FOURCC_ANY; 197 case kI420: 198 return libyuv::FOURCC_I420; 199 case kIYUV: // same as KYV12 200 case kYV12: 201 return libyuv::FOURCC_YV12; 202 case kRGB24: 203 return libyuv::FOURCC_24BG; 204 case kABGR: 205 return libyuv::FOURCC_ABGR; 206 case kRGB565: 207 return libyuv::FOURCC_RGBP; 208 case kYUY2: 209 return libyuv::FOURCC_YUY2; 210 case kUYVY: 211 return libyuv::FOURCC_UYVY; 212 case kMJPG: 213 return libyuv::FOURCC_MJPG; 214 case kNV21: 215 return libyuv::FOURCC_NV21; 216 case kNV12: 217 return libyuv::FOURCC_NV12; 218 case kARGB: 219 return libyuv::FOURCC_ARGB; 220 case kBGRA: 221 return libyuv::FOURCC_BGRA; 222 case kARGB4444: 223 return libyuv::FOURCC_R444; 224 case kARGB1555: 225 return libyuv::FOURCC_RGBO; 226 } 227 assert(false); 228 return libyuv::FOURCC_ANY; 229 } 230 231 int ConvertToI420(VideoType src_video_type, 232 const uint8_t* src_frame, 233 int crop_x, 234 int crop_y, 235 int src_width, 236 int src_height, 237 size_t sample_size, 238 VideoRotation rotation, 239 VideoFrame* dst_frame) { 240 int dst_width = dst_frame->width(); 241 int dst_height = dst_frame->height(); 242 // LibYuv expects pre-rotation values for dst. 243 // Stride values should correspond to the destination values. 244 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 245 dst_width = dst_frame->height(); 246 dst_height = dst_frame->width(); 247 } 248 return libyuv::ConvertToI420(src_frame, sample_size, 249 dst_frame->buffer(kYPlane), 250 dst_frame->stride(kYPlane), 251 dst_frame->buffer(kUPlane), 252 dst_frame->stride(kUPlane), 253 dst_frame->buffer(kVPlane), 254 dst_frame->stride(kVPlane), 255 crop_x, crop_y, 256 src_width, src_height, 257 dst_width, dst_height, 258 ConvertRotationMode(rotation), 259 ConvertVideoType(src_video_type)); 260 } 261 262 int ConvertFromI420(const VideoFrame& src_frame, 263 VideoType dst_video_type, 264 int dst_sample_size, 265 uint8_t* dst_frame) { 266 return libyuv::ConvertFromI420(src_frame.buffer(kYPlane), 267 src_frame.stride(kYPlane), 268 src_frame.buffer(kUPlane), 269 src_frame.stride(kUPlane), 270 src_frame.buffer(kVPlane), 271 src_frame.stride(kVPlane), 272 dst_frame, dst_sample_size, 273 src_frame.width(), src_frame.height(), 274 ConvertVideoType(dst_video_type)); 275 } 276 277 // TODO(mikhal): Create a designated VideoFrame for non I420. 278 int ConvertFromYV12(const VideoFrame& src_frame, 279 VideoType dst_video_type, 280 int dst_sample_size, 281 uint8_t* dst_frame) { 282 // YV12 = Y, V, U 283 return libyuv::ConvertFromI420(src_frame.buffer(kYPlane), 284 src_frame.stride(kYPlane), 285 src_frame.buffer(kVPlane), 286 src_frame.stride(kVPlane), 287 src_frame.buffer(kUPlane), 288 src_frame.stride(kUPlane), 289 dst_frame, dst_sample_size, 290 src_frame.width(), src_frame.height(), 291 ConvertVideoType(dst_video_type)); 292 } 293 294 // Compute PSNR for an I420 frame (all planes) 295 double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) { 296 if (!ref_frame || !test_frame) 297 return -1; 298 else if ((ref_frame->width() != test_frame->width()) || 299 (ref_frame->height() != test_frame->height())) 300 return -1; 301 else if (ref_frame->width() < 0 || ref_frame->height() < 0) 302 return -1; 303 304 double psnr = libyuv::I420Psnr(ref_frame->buffer(kYPlane), 305 ref_frame->stride(kYPlane), 306 ref_frame->buffer(kUPlane), 307 ref_frame->stride(kUPlane), 308 ref_frame->buffer(kVPlane), 309 ref_frame->stride(kVPlane), 310 test_frame->buffer(kYPlane), 311 test_frame->stride(kYPlane), 312 test_frame->buffer(kUPlane), 313 test_frame->stride(kUPlane), 314 test_frame->buffer(kVPlane), 315 test_frame->stride(kVPlane), 316 test_frame->width(), test_frame->height()); 317 // LibYuv sets the max psnr value to 128, we restrict it here. 318 // In case of 0 mse in one frame, 128 can skew the results significantly. 319 return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr; 320 } 321 322 // Compute SSIM for an I420 frame (all planes) 323 double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) { 324 if (!ref_frame || !test_frame) 325 return -1; 326 else if ((ref_frame->width() != test_frame->width()) || 327 (ref_frame->height() != test_frame->height())) 328 return -1; 329 else if (ref_frame->width() < 0 || ref_frame->height() < 0) 330 return -1; 331 332 return libyuv::I420Ssim(ref_frame->buffer(kYPlane), 333 ref_frame->stride(kYPlane), 334 ref_frame->buffer(kUPlane), 335 ref_frame->stride(kUPlane), 336 ref_frame->buffer(kVPlane), 337 ref_frame->stride(kVPlane), 338 test_frame->buffer(kYPlane), 339 test_frame->stride(kYPlane), 340 test_frame->buffer(kUPlane), 341 test_frame->stride(kUPlane), 342 test_frame->buffer(kVPlane), 343 test_frame->stride(kVPlane), 344 test_frame->width(), test_frame->height()); 345 } 346 } // namespace webrtc 347