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 <math.h> 12 #include <string.h> 13 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "webrtc/common_video/interface/i420_video_frame.h" 16 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 17 #include "webrtc/system_wrappers/interface/scoped_ptr.h" 18 #include "webrtc/system_wrappers/interface/tick_util.h" 19 #include "webrtc/test/testsupport/fileutils.h" 20 21 namespace webrtc { 22 23 int PrintBuffer(const uint8_t* buffer, int width, int height, int stride) { 24 if (buffer == NULL) 25 return -1; 26 int k; 27 const uint8_t* tmp_buffer = buffer; 28 for (int i = 0; i < height; i++) { 29 k = 0; 30 for (int j = 0; j < width; j++) { 31 printf("%d ", tmp_buffer[k++]); 32 } 33 tmp_buffer += stride; 34 printf(" \n"); 35 } 36 printf(" \n"); 37 return 0; 38 } 39 40 41 int PrintFrame(const I420VideoFrame* frame, const char* str) { 42 if (frame == NULL) 43 return -1; 44 printf("%s %dx%d \n", str, frame->width(), frame->height()); 45 46 int ret = 0; 47 for (int plane_num = 0; plane_num < kNumOfPlanes; ++plane_num) { 48 PlaneType plane_type = static_cast<PlaneType>(plane_num); 49 int width = (plane_num ? (frame->width() + 1) / 2 : frame->width()); 50 int height = (plane_num ? (frame->height() + 1) / 2 : frame->height()); 51 ret += PrintBuffer(frame->buffer(plane_type), width, height, 52 frame->stride(plane_type)); 53 } 54 return ret; 55 } 56 57 58 // Create an image from on a YUV frame. Every plane value starts with a start 59 // value, and will be set to increasing values. 60 void CreateImage(I420VideoFrame* frame, int plane_offset[kNumOfPlanes]) { 61 if (frame == NULL) 62 return; 63 for (int plane_num = 0; plane_num < kNumOfPlanes; ++plane_num) { 64 int width = (plane_num != kYPlane ? (frame->width() + 1) / 2 : 65 frame->width()); 66 int height = (plane_num != kYPlane ? (frame->height() + 1) / 2 : 67 frame->height()); 68 PlaneType plane_type = static_cast<PlaneType>(plane_num); 69 uint8_t *data = frame->buffer(plane_type); 70 for (int i = 0; i < height; i++) { 71 for (int j = 0; j < width; j++) { 72 data[j] = static_cast<uint8_t>(i + plane_offset[plane_num] + j); 73 } 74 data += frame->stride(plane_type); 75 } 76 } 77 } 78 79 class TestLibYuv : public ::testing::Test { 80 protected: 81 TestLibYuv(); 82 virtual void SetUp(); 83 virtual void TearDown(); 84 85 FILE* source_file_; 86 I420VideoFrame orig_frame_; 87 scoped_ptr<uint8_t[]> orig_buffer_; 88 const int width_; 89 const int height_; 90 const int size_y_; 91 const int size_uv_; 92 const int frame_length_; 93 }; 94 95 TestLibYuv::TestLibYuv() 96 : source_file_(NULL), 97 orig_frame_(), 98 width_(352), 99 height_(288), 100 size_y_(width_ * height_), 101 size_uv_(((width_ + 1 ) / 2) * ((height_ + 1) / 2)), 102 frame_length_(CalcBufferSize(kI420, 352, 288)) { 103 orig_buffer_.reset(new uint8_t[frame_length_]); 104 } 105 106 void TestLibYuv::SetUp() { 107 const std::string input_file_name = webrtc::test::ProjectRootPath() + 108 "resources/foreman_cif.yuv"; 109 source_file_ = fopen(input_file_name.c_str(), "rb"); 110 ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<< 111 input_file_name << "\n"; 112 113 EXPECT_EQ(fread(orig_buffer_.get(), 1, frame_length_, source_file_), 114 static_cast<unsigned int>(frame_length_)); 115 EXPECT_EQ(0, orig_frame_.CreateFrame(size_y_, orig_buffer_.get(), 116 size_uv_, orig_buffer_.get() + size_y_, 117 size_uv_, orig_buffer_.get() + 118 size_y_ + size_uv_, 119 width_, height_, 120 width_, (width_ + 1) / 2, 121 (width_ + 1) / 2)); 122 } 123 124 void TestLibYuv::TearDown() { 125 if (source_file_ != NULL) { 126 ASSERT_EQ(0, fclose(source_file_)); 127 } 128 source_file_ = NULL; 129 } 130 131 TEST_F(TestLibYuv, ConvertSanityTest) { 132 // TODO(mikhal) 133 } 134 135 TEST_F(TestLibYuv, ConvertTest) { 136 // Reading YUV frame - testing on the first frame of the foreman sequence 137 int j = 0; 138 std::string output_file_name = webrtc::test::OutputPath() + 139 "LibYuvTest_conversion.yuv"; 140 FILE* output_file = fopen(output_file_name.c_str(), "wb"); 141 ASSERT_TRUE(output_file != NULL); 142 143 double psnr = 0.0; 144 145 I420VideoFrame res_i420_frame; 146 EXPECT_EQ(0,res_i420_frame.CreateEmptyFrame(width_, height_, width_, 147 (width_ + 1) / 2, 148 (width_ + 1) / 2)); 149 printf("\nConvert #%d I420 <-> I420 \n", j); 150 scoped_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]); 151 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0, 152 out_i420_buffer.get())); 153 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, 154 width_, height_, 155 0, kRotateNone, &res_i420_frame)); 156 157 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 158 return; 159 } 160 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 161 EXPECT_EQ(48.0, psnr); 162 j++; 163 164 printf("\nConvert #%d I420 <-> RGB24\n", j); 165 scoped_ptr<uint8_t[]> res_rgb_buffer2(new uint8_t[width_ * height_ * 3]); 166 // Align the stride values for the output frame. 167 int stride_y = 0; 168 int stride_uv = 0; 169 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv); 170 res_i420_frame.CreateEmptyFrame(width_, height_, stride_y, 171 stride_uv, stride_uv); 172 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kRGB24, 0, res_rgb_buffer2.get())); 173 174 EXPECT_EQ(0, ConvertToI420(kRGB24, res_rgb_buffer2.get(), 0, 0, width_, 175 height_, 0, kRotateNone, &res_i420_frame)); 176 177 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 178 return; 179 } 180 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 181 182 // Optimization Speed- quality trade-off => 45 dB only (platform dependant). 183 EXPECT_GT(ceil(psnr), 44); 184 j++; 185 186 printf("\nConvert #%d I420 <-> UYVY\n", j); 187 scoped_ptr<uint8_t[]> out_uyvy_buffer(new uint8_t[width_ * height_ * 2]); 188 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kUYVY, 0, out_uyvy_buffer.get())); 189 EXPECT_EQ(0, ConvertToI420(kUYVY, out_uyvy_buffer.get(), 0, 0, width_, 190 height_, 0, kRotateNone, &res_i420_frame)); 191 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 192 EXPECT_EQ(48.0, psnr); 193 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 194 return; 195 } 196 j++; 197 198 printf("\nConvert #%d I420 <-> YV12\n", j); 199 scoped_ptr<uint8_t[]> outYV120Buffer(new uint8_t[frame_length_]); 200 scoped_ptr<uint8_t[]> res_i420_buffer(new uint8_t[frame_length_]); 201 I420VideoFrame yv12_frame; 202 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYV12, 0, outYV120Buffer.get())); 203 yv12_frame.CreateFrame(size_y_, outYV120Buffer.get(), 204 size_uv_, outYV120Buffer.get() + size_y_, 205 size_uv_, outYV120Buffer.get() + size_y_ + size_uv_, 206 width_, height_, 207 width_, (width_ + 1) / 2, (width_ + 1) / 2); 208 EXPECT_EQ(0, ConvertFromYV12(yv12_frame, kI420, 0, res_i420_buffer.get())); 209 if (fwrite(res_i420_buffer.get(), 1, frame_length_, 210 output_file) != static_cast<unsigned int>(frame_length_)) { 211 return; 212 } 213 214 ConvertToI420(kI420, res_i420_buffer.get(), 0, 0, 215 width_, height_, 0, kRotateNone, &res_i420_frame); 216 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 217 EXPECT_EQ(48.0, psnr); 218 j++; 219 220 printf("\nConvert #%d I420 <-> YUY2\n", j); 221 scoped_ptr<uint8_t[]> out_yuy2_buffer(new uint8_t[width_ * height_ * 2]); 222 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYUY2, 0, out_yuy2_buffer.get())); 223 224 EXPECT_EQ(0, ConvertToI420(kYUY2, out_yuy2_buffer.get(), 0, 0, width_, 225 height_, 0, kRotateNone, &res_i420_frame)); 226 227 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 228 return; 229 } 230 231 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 232 EXPECT_EQ(48.0, psnr); 233 printf("\nConvert #%d I420 <-> RGB565\n", j); 234 scoped_ptr<uint8_t[]> out_rgb565_buffer(new uint8_t[width_ * height_ * 2]); 235 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kRGB565, 0, 236 out_rgb565_buffer.get())); 237 238 EXPECT_EQ(0, ConvertToI420(kRGB565, out_rgb565_buffer.get(), 0, 0, width_, 239 height_, 0, kRotateNone, &res_i420_frame)); 240 241 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 242 return; 243 } 244 j++; 245 246 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 247 // TODO(leozwang) Investigate the right psnr should be set for I420ToRGB565, 248 // Another example is I420ToRGB24, the psnr is 44 249 // TODO(mikhal): Add psnr for RGB565, 1555, 4444, convert to ARGB. 250 EXPECT_GT(ceil(psnr), 40); 251 252 printf("\nConvert #%d I420 <-> ARGB8888\n", j); 253 scoped_ptr<uint8_t[]> out_argb8888_buffer(new uint8_t[width_ * height_ * 4]); 254 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kARGB, 0, 255 out_argb8888_buffer.get())); 256 257 EXPECT_EQ(0, ConvertToI420(kARGB, out_argb8888_buffer.get(), 0, 0, width_, 258 height_, 0, kRotateNone, &res_i420_frame)); 259 260 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 261 return; 262 } 263 264 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 265 // TODO(leozwang) Investigate the right psnr should be set for I420ToARGB8888, 266 EXPECT_GT(ceil(psnr), 42); 267 268 ASSERT_EQ(0, fclose(output_file)); 269 } 270 271 TEST_F(TestLibYuv, ConvertAlignedFrame) { 272 // Reading YUV frame - testing on the first frame of the foreman sequence 273 std::string output_file_name = webrtc::test::OutputPath() + 274 "LibYuvTest_conversion.yuv"; 275 FILE* output_file = fopen(output_file_name.c_str(), "wb"); 276 ASSERT_TRUE(output_file != NULL); 277 278 double psnr = 0.0; 279 280 I420VideoFrame res_i420_frame; 281 int stride_y = 0; 282 int stride_uv = 0; 283 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv); 284 EXPECT_EQ(0,res_i420_frame.CreateEmptyFrame(width_, height_, 285 stride_y, stride_uv, stride_uv)); 286 scoped_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]); 287 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0, 288 out_i420_buffer.get())); 289 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, 290 width_, height_, 291 0, kRotateNone, &res_i420_frame)); 292 293 if (PrintI420VideoFrame(res_i420_frame, output_file) < 0) { 294 return; 295 } 296 psnr = I420PSNR(&orig_frame_, &res_i420_frame); 297 EXPECT_EQ(48.0, psnr); 298 } 299 300 301 TEST_F(TestLibYuv, RotateTest) { 302 // Use ConvertToI420 for multiple roatations - see that nothing breaks, all 303 // memory is properly allocated and end result is equal to the starting point. 304 I420VideoFrame rotated_res_i420_frame; 305 int rotated_width = height_; 306 int rotated_height = width_; 307 int stride_y ; 308 int stride_uv; 309 Calc16ByteAlignedStride(rotated_width, &stride_y, &stride_uv); 310 EXPECT_EQ(0,rotated_res_i420_frame.CreateEmptyFrame(rotated_width, 311 rotated_height, 312 stride_y, 313 stride_uv, 314 stride_uv)); 315 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, 316 width_, height_, 317 0, kRotate90, &rotated_res_i420_frame)); 318 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, 319 width_, height_, 320 0, kRotate270, &rotated_res_i420_frame)); 321 EXPECT_EQ(0,rotated_res_i420_frame.CreateEmptyFrame(width_, height_, 322 width_, (width_ + 1) / 2, 323 (width_ + 1) / 2)); 324 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, 325 width_, height_, 326 0, kRotate180, &rotated_res_i420_frame)); 327 } 328 329 TEST_F(TestLibYuv, MirrorTest) { 330 // TODO(mikhal): Add an automated test to confirm output. 331 std::string str; 332 int width = 16; 333 int half_width = (width + 1) / 2; 334 int height = 8; 335 int half_height = (height + 1) / 2; 336 337 I420VideoFrame test_frame; 338 test_frame.CreateEmptyFrame(width, height, width, 339 half_width, half_width); 340 memset(test_frame.buffer(kYPlane), 255, width * height); 341 memset(test_frame.buffer(kUPlane), 255, half_width * half_height); 342 memset(test_frame.buffer(kVPlane), 255, half_width * half_height); 343 344 // Create input frame. 345 I420VideoFrame in_frame, test_in_frame; 346 in_frame.CreateEmptyFrame(width, height, width, 347 half_width ,half_width); 348 int plane_offset[kNumOfPlanes]; 349 plane_offset[kYPlane] = 10; 350 plane_offset[kUPlane] = 100; 351 plane_offset[kVPlane] = 200; 352 CreateImage(&in_frame, plane_offset); 353 EXPECT_EQ(0, PrintFrame(&in_frame, "InputFrame")); 354 test_in_frame.CopyFrame(in_frame); 355 356 I420VideoFrame out_frame, test_out_frame; 357 out_frame.CreateEmptyFrame(width, height, width, 358 half_width ,half_width); 359 CreateImage(&out_frame, plane_offset); 360 test_out_frame.CopyFrame(out_frame); 361 362 // Left-Right. 363 std::cout << "Test Mirror function: LeftRight" << std::endl; 364 EXPECT_EQ(0, MirrorI420LeftRight(&in_frame, &out_frame)); 365 EXPECT_EQ(0, PrintFrame(&out_frame, "OutputFrame")); 366 EXPECT_EQ(0, MirrorI420LeftRight(&out_frame, &in_frame)); 367 368 EXPECT_EQ(0, memcmp(in_frame.buffer(kYPlane), 369 test_in_frame.buffer(kYPlane), width * height)); 370 EXPECT_EQ(0, memcmp(in_frame.buffer(kUPlane), 371 test_in_frame.buffer(kUPlane), half_width * half_height)); 372 EXPECT_EQ(0, memcmp(in_frame.buffer(kVPlane), 373 test_in_frame.buffer(kVPlane), half_width * half_height)); 374 375 // UpDown 376 std::cout << "Test Mirror function: UpDown" << std::endl; 377 EXPECT_EQ(0, MirrorI420UpDown(&in_frame, &out_frame)); 378 EXPECT_EQ(0, PrintFrame(&out_frame, "OutputFrame")); 379 EXPECT_EQ(0, MirrorI420UpDown(&out_frame, &test_frame)); 380 EXPECT_EQ(0, memcmp(in_frame.buffer(kYPlane), 381 test_in_frame.buffer(kYPlane), width * height)); 382 EXPECT_EQ(0, memcmp(in_frame.buffer(kUPlane), 383 test_in_frame.buffer(kUPlane), half_width * half_height)); 384 EXPECT_EQ(0, memcmp(in_frame.buffer(kVPlane), 385 test_in_frame.buffer(kVPlane), half_width * half_height)); 386 387 // TODO(mikhal): Write to a file, and ask to look at the file. 388 389 std::cout << "Do the mirrored frames look correct?" << std::endl; 390 } 391 392 TEST_F(TestLibYuv, alignment) { 393 int value = 0x3FF; // 1023 394 EXPECT_EQ(0x400, AlignInt(value, 128)); // Low 7 bits are zero. 395 EXPECT_EQ(0x400, AlignInt(value, 64)); // Low 6 bits are zero. 396 EXPECT_EQ(0x400, AlignInt(value, 32)); // Low 5 bits are zero. 397 } 398 399 TEST_F(TestLibYuv, StrideAlignment) { 400 int stride_y = 0; 401 int stride_uv = 0; 402 int width = 52; 403 Calc16ByteAlignedStride(width, &stride_y, &stride_uv); 404 EXPECT_EQ(64, stride_y); 405 EXPECT_EQ(32, stride_uv); 406 width = 128; 407 Calc16ByteAlignedStride(width, &stride_y, &stride_uv); 408 EXPECT_EQ(128, stride_y); 409 EXPECT_EQ(64, stride_uv); 410 width = 127; 411 Calc16ByteAlignedStride(width, &stride_y, &stride_uv); 412 EXPECT_EQ(128, stride_y); 413 EXPECT_EQ(64, stride_uv); 414 } 415 416 } // namespace 417