1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/base_paths.h" 6 #include "base/cpu.h" 7 #include "base/files/file_util.h" 8 #include "base/logging.h" 9 #include "base/path_service.h" 10 #include "media/base/djb2.h" 11 #include "media/base/simd/convert_rgb_to_yuv.h" 12 #include "media/base/simd/convert_yuv_to_rgb.h" 13 #include "media/base/simd/filter_yuv.h" 14 #include "media/base/simd/yuv_to_rgb_table.h" 15 #include "media/base/yuv_convert.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "ui/gfx/rect.h" 18 19 // Size of raw image. 20 static const int kSourceWidth = 640; 21 static const int kSourceHeight = 360; 22 static const int kSourceYSize = kSourceWidth * kSourceHeight; 23 static const int kSourceUOffset = kSourceYSize; 24 static const int kSourceVOffset = kSourceYSize * 5 / 4; 25 static const int kScaledWidth = 1024; 26 static const int kScaledHeight = 768; 27 static const int kDownScaledWidth = 512; 28 static const int kDownScaledHeight = 320; 29 static const int kBpp = 4; 30 31 // Surface sizes for various test files. 32 static const int kYUV12Size = kSourceYSize * 12 / 8; 33 static const int kYUV16Size = kSourceYSize * 16 / 8; 34 static const int kYUY2Size = kSourceYSize * 16 / 8; 35 static const int kRGBSize = kSourceYSize * kBpp; 36 static const int kRGBSizeScaled = kScaledWidth * kScaledHeight * kBpp; 37 static const int kRGB24Size = kSourceYSize * 3; 38 static const int kRGBSizeConverted = kSourceYSize * kBpp; 39 40 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) 41 static const int kSourceAOffset = kSourceYSize * 12 / 8; 42 static const int kYUVA12Size = kSourceYSize * 20 / 8; 43 #endif 44 45 // Helper for reading test data into a scoped_ptr<uint8[]>. 46 static void ReadData(const base::FilePath::CharType* filename, 47 int expected_size, 48 scoped_ptr<uint8[]>* data) { 49 data->reset(new uint8[expected_size]); 50 51 base::FilePath path; 52 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); 53 path = path.Append(FILE_PATH_LITERAL("media")) 54 .Append(FILE_PATH_LITERAL("test")) 55 .Append(FILE_PATH_LITERAL("data")) 56 .Append(filename); 57 58 // Verify file size is correct. 59 int64 actual_size = 0; 60 base::GetFileSize(path, &actual_size); 61 CHECK_EQ(actual_size, expected_size); 62 63 // Verify bytes read are correct. 64 int bytes_read = base::ReadFile( 65 path, reinterpret_cast<char*>(data->get()), expected_size); 66 CHECK_EQ(bytes_read, expected_size); 67 } 68 69 static void ReadYV12Data(scoped_ptr<uint8[]>* data) { 70 ReadData(FILE_PATH_LITERAL("bali_640x360_P420.yuv"), kYUV12Size, data); 71 } 72 73 static void ReadYV16Data(scoped_ptr<uint8[]>* data) { 74 ReadData(FILE_PATH_LITERAL("bali_640x360_P422.yuv"), kYUV16Size, data); 75 } 76 77 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) 78 static void ReadYV12AData(scoped_ptr<uint8[]>* data) { 79 ReadData(FILE_PATH_LITERAL("bali_640x360_P420_alpha.yuv"), kYUVA12Size, data); 80 } 81 #endif 82 83 static void ReadRGB24Data(scoped_ptr<uint8[]>* data) { 84 ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data); 85 } 86 87 static void ReadYUY2Data(scoped_ptr<uint8[]>* data) { 88 ReadData(FILE_PATH_LITERAL("bali_640x360_YUY2.yuv"), kYUY2Size, data); 89 } 90 91 #if defined(OS_ANDROID) 92 // Helper for swapping red and blue channels of RGBA or BGRA. 93 static void SwapRedAndBlueChannels(unsigned char* pixels, size_t buffer_size) { 94 for (size_t i = 0; i < buffer_size; i += 4) { 95 std::swap(pixels[i], pixels[i + 2]); 96 } 97 } 98 #endif 99 100 namespace media { 101 102 TEST(YUVConvertTest, YV12) { 103 // Allocate all surfaces. 104 scoped_ptr<uint8[]> yuv_bytes; 105 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 106 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]); 107 108 // Read YUV reference data from file. 109 ReadYV12Data(&yuv_bytes); 110 111 // Convert a frame of YUV to 32 bit ARGB. 112 media::ConvertYUVToRGB32(yuv_bytes.get(), 113 yuv_bytes.get() + kSourceUOffset, 114 yuv_bytes.get() + kSourceVOffset, 115 rgb_converted_bytes.get(), // RGB output 116 kSourceWidth, kSourceHeight, // Dimensions 117 kSourceWidth, // YStride 118 kSourceWidth / 2, // UVStride 119 kSourceWidth * kBpp, // RGBStride 120 media::YV12); 121 122 #if defined(OS_ANDROID) 123 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); 124 #endif 125 126 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, 127 kDJB2HashSeed); 128 EXPECT_EQ(2413171226u, rgb_hash); 129 } 130 131 TEST(YUVConvertTest, YV16) { 132 // Allocate all surfaces. 133 scoped_ptr<uint8[]> yuv_bytes; 134 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 135 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]); 136 137 // Read YUV reference data from file. 138 ReadYV16Data(&yuv_bytes); 139 140 // Convert a frame of YUV to 32 bit ARGB. 141 media::ConvertYUVToRGB32(yuv_bytes.get(), // Y 142 yuv_bytes.get() + kSourceUOffset, // U 143 yuv_bytes.get() + kSourceYSize * 3 / 2, // V 144 rgb_converted_bytes.get(), // RGB output 145 kSourceWidth, kSourceHeight, // Dimensions 146 kSourceWidth, // YStride 147 kSourceWidth / 2, // UVStride 148 kSourceWidth * kBpp, // RGBStride 149 media::YV16); 150 151 #if defined(OS_ANDROID) 152 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); 153 #endif 154 155 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, 156 kDJB2HashSeed); 157 EXPECT_EQ(4222342047u, rgb_hash); 158 } 159 160 struct YUVScaleTestData { 161 YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32 r) 162 : yuv_type(y), 163 scale_filter(s), 164 rgb_hash(r) { 165 } 166 167 media::YUVType yuv_type; 168 media::ScaleFilter scale_filter; 169 uint32 rgb_hash; 170 }; 171 172 class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> { 173 public: 174 YUVScaleTest() { 175 switch (GetParam().yuv_type) { 176 case media::YV12: 177 case media::YV12J: 178 ReadYV12Data(&yuv_bytes_); 179 break; 180 case media::YV16: 181 ReadYV16Data(&yuv_bytes_); 182 break; 183 } 184 185 rgb_bytes_.reset(new uint8[kRGBSizeScaled]); 186 } 187 188 // Helpers for getting the proper Y, U and V plane offsets. 189 uint8* y_plane() { return yuv_bytes_.get(); } 190 uint8* u_plane() { return yuv_bytes_.get() + kSourceYSize; } 191 uint8* v_plane() { 192 switch (GetParam().yuv_type) { 193 case media::YV12: 194 case media::YV12J: 195 return yuv_bytes_.get() + kSourceVOffset; 196 case media::YV16: 197 return yuv_bytes_.get() + kSourceYSize * 3 / 2; 198 } 199 return NULL; 200 } 201 202 scoped_ptr<uint8[]> yuv_bytes_; 203 scoped_ptr<uint8[]> rgb_bytes_; 204 }; 205 206 TEST_P(YUVScaleTest, NoScale) { 207 media::ScaleYUVToRGB32(y_plane(), // Y 208 u_plane(), // U 209 v_plane(), // V 210 rgb_bytes_.get(), // RGB output 211 kSourceWidth, kSourceHeight, // Dimensions 212 kSourceWidth, kSourceHeight, // Dimensions 213 kSourceWidth, // YStride 214 kSourceWidth / 2, // UvStride 215 kSourceWidth * kBpp, // RgbStride 216 GetParam().yuv_type, 217 media::ROTATE_0, 218 GetParam().scale_filter); 219 220 uint32 yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); 221 222 media::ConvertYUVToRGB32(y_plane(), // Y 223 u_plane(), // U 224 v_plane(), // V 225 rgb_bytes_.get(), // RGB output 226 kSourceWidth, kSourceHeight, // Dimensions 227 kSourceWidth, // YStride 228 kSourceWidth / 2, // UVStride 229 kSourceWidth * kBpp, // RGBStride 230 GetParam().yuv_type); 231 232 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); 233 234 EXPECT_EQ(yuv_hash, rgb_hash); 235 } 236 237 TEST_P(YUVScaleTest, Normal) { 238 media::ScaleYUVToRGB32(y_plane(), // Y 239 u_plane(), // U 240 v_plane(), // V 241 rgb_bytes_.get(), // RGB output 242 kSourceWidth, kSourceHeight, // Dimensions 243 kScaledWidth, kScaledHeight, // Dimensions 244 kSourceWidth, // YStride 245 kSourceWidth / 2, // UvStride 246 kScaledWidth * kBpp, // RgbStride 247 GetParam().yuv_type, 248 media::ROTATE_0, 249 GetParam().scale_filter); 250 251 #if defined(OS_ANDROID) 252 SwapRedAndBlueChannels(rgb_bytes_.get(), kRGBSizeScaled); 253 #endif 254 255 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed); 256 EXPECT_EQ(GetParam().rgb_hash, rgb_hash); 257 } 258 259 TEST_P(YUVScaleTest, ZeroSourceSize) { 260 media::ScaleYUVToRGB32(y_plane(), // Y 261 u_plane(), // U 262 v_plane(), // V 263 rgb_bytes_.get(), // RGB output 264 0, 0, // Dimensions 265 kScaledWidth, kScaledHeight, // Dimensions 266 kSourceWidth, // YStride 267 kSourceWidth / 2, // UvStride 268 kScaledWidth * kBpp, // RgbStride 269 GetParam().yuv_type, 270 media::ROTATE_0, 271 GetParam().scale_filter); 272 273 // Testing for out-of-bound read/writes with AddressSanitizer. 274 } 275 276 TEST_P(YUVScaleTest, ZeroDestinationSize) { 277 media::ScaleYUVToRGB32(y_plane(), // Y 278 u_plane(), // U 279 v_plane(), // V 280 rgb_bytes_.get(), // RGB output 281 kSourceWidth, kSourceHeight, // Dimensions 282 0, 0, // Dimensions 283 kSourceWidth, // YStride 284 kSourceWidth / 2, // UvStride 285 kScaledWidth * kBpp, // RgbStride 286 GetParam().yuv_type, 287 media::ROTATE_0, 288 GetParam().scale_filter); 289 290 // Testing for out-of-bound read/writes with AddressSanitizer. 291 } 292 293 TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) { 294 media::ScaleYUVToRGB32(y_plane(), // Y 295 u_plane(), // U 296 v_plane(), // V 297 rgb_bytes_.get(), // RGB output 298 kSourceWidth, kSourceHeight, // Dimensions 299 3, 3, // Dimensions 300 kSourceWidth, // YStride 301 kSourceWidth / 2, // UvStride 302 kScaledWidth * kBpp, // RgbStride 303 GetParam().yuv_type, 304 media::ROTATE_0, 305 GetParam().scale_filter); 306 } 307 308 INSTANTIATE_TEST_CASE_P( 309 YUVScaleFormats, YUVScaleTest, 310 ::testing::Values( 311 YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u), 312 YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u), 313 YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u), 314 YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u))); 315 316 // This tests a known worst case YUV value, and for overflow. 317 TEST(YUVConvertTest, Clamp) { 318 // Allocate all surfaces. 319 scoped_ptr<uint8[]> yuv_bytes(new uint8[1]); 320 scoped_ptr<uint8[]> rgb_bytes(new uint8[1]); 321 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[1]); 322 323 // Values that failed previously in bug report. 324 unsigned char y = 255u; 325 unsigned char u = 255u; 326 unsigned char v = 19u; 327 328 // Prefill extra large destination buffer to test for overflow. 329 unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; 330 unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 }; 331 // Convert a frame of YUV to 32 bit ARGB. 332 media::ConvertYUVToRGB32(&y, // Y 333 &u, // U 334 &v, // V 335 &rgb[0], // RGB output 336 1, 1, // Dimensions 337 0, // YStride 338 0, // UVStride 339 0, // RGBStride 340 media::YV12); 341 342 #if defined(OS_ANDROID) 343 SwapRedAndBlueChannels(rgb, kBpp); 344 #endif 345 346 int expected_test = memcmp(rgb, expected, sizeof(expected)); 347 EXPECT_EQ(0, expected_test); 348 } 349 350 TEST(YUVConvertTest, RGB24ToYUV) { 351 // Allocate all surfaces. 352 scoped_ptr<uint8[]> rgb_bytes; 353 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 354 355 // Read RGB24 reference data from file. 356 ReadRGB24Data(&rgb_bytes); 357 358 // Convert to I420. 359 media::ConvertRGB24ToYUV(rgb_bytes.get(), 360 yuv_converted_bytes.get(), 361 yuv_converted_bytes.get() + kSourceUOffset, 362 yuv_converted_bytes.get() + kSourceVOffset, 363 kSourceWidth, kSourceHeight, // Dimensions 364 kSourceWidth * 3, // RGBStride 365 kSourceWidth, // YStride 366 kSourceWidth / 2); // UVStride 367 368 uint32 rgb_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, 369 kDJB2HashSeed); 370 EXPECT_EQ(320824432u, rgb_hash); 371 } 372 373 TEST(YUVConvertTest, RGB32ToYUV) { 374 // Allocate all surfaces. 375 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 376 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 377 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 378 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSize]); 379 380 // Read YUV reference data from file. 381 base::FilePath yuv_url; 382 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); 383 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) 384 .Append(FILE_PATH_LITERAL("test")) 385 .Append(FILE_PATH_LITERAL("data")) 386 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); 387 EXPECT_EQ(static_cast<int>(kYUV12Size), 388 base::ReadFile(yuv_url, 389 reinterpret_cast<char*>(yuv_bytes.get()), 390 static_cast<int>(kYUV12Size))); 391 392 // Convert a frame of YUV to 32 bit ARGB. 393 media::ConvertYUVToRGB32(yuv_bytes.get(), 394 yuv_bytes.get() + kSourceUOffset, 395 yuv_bytes.get() + kSourceVOffset, 396 rgb_bytes.get(), // RGB output 397 kSourceWidth, kSourceHeight, // Dimensions 398 kSourceWidth, // YStride 399 kSourceWidth / 2, // UVStride 400 kSourceWidth * kBpp, // RGBStride 401 media::YV12); 402 403 // Convert RGB32 to YV12. 404 media::ConvertRGB32ToYUV(rgb_bytes.get(), 405 yuv_converted_bytes.get(), 406 yuv_converted_bytes.get() + kSourceUOffset, 407 yuv_converted_bytes.get() + kSourceVOffset, 408 kSourceWidth, kSourceHeight, // Dimensions 409 kSourceWidth * 4, // RGBStride 410 kSourceWidth, // YStride 411 kSourceWidth / 2); // UVStride 412 413 // Convert YV12 back to RGB32. 414 media::ConvertYUVToRGB32(yuv_converted_bytes.get(), 415 yuv_converted_bytes.get() + kSourceUOffset, 416 yuv_converted_bytes.get() + kSourceVOffset, 417 rgb_converted_bytes.get(), // RGB output 418 kSourceWidth, kSourceHeight, // Dimensions 419 kSourceWidth, // YStride 420 kSourceWidth / 2, // UVStride 421 kSourceWidth * kBpp, // RGBStride 422 media::YV12); 423 424 int error = 0; 425 for (int i = 0; i < kRGBSize; ++i) { 426 int diff = rgb_converted_bytes[i] - rgb_bytes[i]; 427 if (diff < 0) 428 diff = -diff; 429 error += diff; 430 } 431 432 // Make sure error is within bound. 433 DVLOG(1) << "Average error per channel: " << error / kRGBSize; 434 EXPECT_GT(5, error / kRGBSize); 435 } 436 437 TEST(YUVConvertTest, YUY2ToYUV) { 438 // Allocate all surfaces. 439 scoped_ptr<uint8[]> yuy_bytes; 440 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 441 442 // Read YUY reference data from file. 443 ReadYUY2Data(&yuy_bytes); 444 445 // Convert to I420. 446 media::ConvertYUY2ToYUV(yuy_bytes.get(), 447 yuv_converted_bytes.get(), 448 yuv_converted_bytes.get() + kSourceUOffset, 449 yuv_converted_bytes.get() + kSourceVOffset, 450 kSourceWidth, kSourceHeight); 451 452 uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, 453 kDJB2HashSeed); 454 EXPECT_EQ(666823187u, yuy_hash); 455 } 456 457 TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { 458 // Read YUV reference data from file. 459 base::FilePath yuv_url; 460 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); 461 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) 462 .Append(FILE_PATH_LITERAL("test")) 463 .Append(FILE_PATH_LITERAL("data")) 464 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); 465 const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp. 466 scoped_ptr<uint8[]> yuv_bytes(new uint8[size_of_yuv]); 467 EXPECT_EQ(static_cast<int>(size_of_yuv), 468 base::ReadFile(yuv_url, 469 reinterpret_cast<char*>(yuv_bytes.get()), 470 static_cast<int>(size_of_yuv))); 471 472 // Scale the full frame of YUV to 32 bit ARGB. 473 // The API currently only supports down-scaling, so we don't test up-scaling. 474 const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp; 475 scoped_ptr<uint8[]> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]); 476 gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight); 477 478 // We can't compare with the full-frame scaler because it uses slightly 479 // different sampling coordinates. 480 media::ScaleYUVToRGB32WithRect( 481 yuv_bytes.get(), // Y 482 yuv_bytes.get() + kSourceUOffset, // U 483 yuv_bytes.get() + kSourceVOffset, // V 484 rgb_scaled_bytes.get(), // Rgb output 485 kSourceWidth, kSourceHeight, // Dimensions 486 kDownScaledWidth, kDownScaledHeight, // Dimensions 487 sub_rect.x(), sub_rect.y(), // Dest rect 488 sub_rect.right(), sub_rect.bottom(), // Dest rect 489 kSourceWidth, // YStride 490 kSourceWidth / 2, // UvStride 491 kDownScaledWidth * kBpp); // RgbStride 492 493 uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(), 494 size_of_rgb_scaled, 495 kDJB2HashSeed); 496 497 // Re-scale sub-rectangles and verify the results are the same. 498 int next_sub_rect = 0; 499 while (!sub_rect.IsEmpty()) { 500 // Scale a partial rectangle. 501 media::ScaleYUVToRGB32WithRect( 502 yuv_bytes.get(), // Y 503 yuv_bytes.get() + kSourceUOffset, // U 504 yuv_bytes.get() + kSourceVOffset, // V 505 rgb_scaled_bytes.get(), // Rgb output 506 kSourceWidth, kSourceHeight, // Dimensions 507 kDownScaledWidth, kDownScaledHeight, // Dimensions 508 sub_rect.x(), sub_rect.y(), // Dest rect 509 sub_rect.right(), sub_rect.bottom(), // Dest rect 510 kSourceWidth, // YStride 511 kSourceWidth / 2, // UvStride 512 kDownScaledWidth * kBpp); // RgbStride 513 uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(), 514 size_of_rgb_scaled, 515 kDJB2HashSeed); 516 517 EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect); 518 519 // Now pick choose a quarter rect of this sub-rect. 520 if (next_sub_rect & 1) 521 sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2); 522 if (next_sub_rect & 2) 523 sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2); 524 sub_rect.set_width(sub_rect.width() / 2); 525 sub_rect.set_height(sub_rect.height() / 2); 526 next_sub_rect++; 527 } 528 } 529 530 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) 531 TEST(YUVConvertTest, YUVAtoARGB_MMX_MatchReference) { 532 // Allocate all surfaces. 533 scoped_ptr<uint8[]> yuv_bytes; 534 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 535 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]); 536 scoped_ptr<uint8[]> rgb_converted_bytes_ref(new uint8[kRGBSizeConverted]); 537 538 // Read YUV reference data from file. 539 ReadYV12AData(&yuv_bytes); 540 541 // Convert a frame of YUV to 32 bit ARGB using both C and MMX versions. 542 media::ConvertYUVAToARGB_C(yuv_bytes.get(), 543 yuv_bytes.get() + kSourceUOffset, 544 yuv_bytes.get() + kSourceVOffset, 545 yuv_bytes.get() + kSourceAOffset, 546 rgb_converted_bytes_ref.get(), 547 kSourceWidth, 548 kSourceHeight, 549 kSourceWidth, 550 kSourceWidth / 2, 551 kSourceWidth, 552 kSourceWidth * kBpp, 553 media::YV12); 554 media::ConvertYUVAToARGB_MMX(yuv_bytes.get(), 555 yuv_bytes.get() + kSourceUOffset, 556 yuv_bytes.get() + kSourceVOffset, 557 yuv_bytes.get() + kSourceAOffset, 558 rgb_converted_bytes.get(), 559 kSourceWidth, 560 kSourceHeight, 561 kSourceWidth, 562 kSourceWidth / 2, 563 kSourceWidth, 564 kSourceWidth * kBpp, 565 media::YV12); 566 567 EXPECT_EQ(0, 568 memcmp(rgb_converted_bytes.get(), 569 rgb_converted_bytes_ref.get(), 570 kRGBSizeConverted)); 571 } 572 573 TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { 574 base::CPU cpu; 575 if (!cpu.has_sse2()) { 576 LOG(WARNING) << "System doesn't support SSE2, test not executed."; 577 return; 578 } 579 580 // Allocate all surfaces. 581 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 582 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 583 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 584 scoped_ptr<uint8[]> yuv_reference_bytes(new uint8[kYUV12Size]); 585 586 ReadYV12Data(&yuv_bytes); 587 588 // Convert a frame of YUV to 32 bit ARGB. 589 media::ConvertYUVToRGB32( 590 yuv_bytes.get(), 591 yuv_bytes.get() + kSourceUOffset, 592 yuv_bytes.get() + kSourceVOffset, 593 rgb_bytes.get(), // RGB output 594 kSourceWidth, kSourceHeight, // Dimensions 595 kSourceWidth, // YStride 596 kSourceWidth / 2, // UVStride 597 kSourceWidth * kBpp, // RGBStride 598 media::YV12); 599 600 // Convert RGB32 to YV12 with SSE2 version. 601 media::ConvertRGB32ToYUV_SSE2( 602 rgb_bytes.get(), 603 yuv_converted_bytes.get(), 604 yuv_converted_bytes.get() + kSourceUOffset, 605 yuv_converted_bytes.get() + kSourceVOffset, 606 kSourceWidth, kSourceHeight, // Dimensions 607 kSourceWidth * 4, // RGBStride 608 kSourceWidth, // YStride 609 kSourceWidth / 2); // UVStride 610 611 // Convert RGB32 to YV12 with reference version. 612 media::ConvertRGB32ToYUV_SSE2_Reference( 613 rgb_bytes.get(), 614 yuv_reference_bytes.get(), 615 yuv_reference_bytes.get() + kSourceUOffset, 616 yuv_reference_bytes.get() + kSourceVOffset, 617 kSourceWidth, kSourceHeight, // Dimensions 618 kSourceWidth * 4, // RGBStride 619 kSourceWidth, // YStride 620 kSourceWidth / 2); // UVStride 621 622 // Now convert a odd width and height, this overrides part of the buffer 623 // generated above but that is fine because the point of this test is to 624 // match the result with the reference code. 625 626 // Convert RGB32 to YV12 with SSE2 version. 627 media::ConvertRGB32ToYUV_SSE2( 628 rgb_bytes.get(), 629 yuv_converted_bytes.get(), 630 yuv_converted_bytes.get() + kSourceUOffset, 631 yuv_converted_bytes.get() + kSourceVOffset, 632 7, 7, // Dimensions 633 kSourceWidth * 4, // RGBStride 634 kSourceWidth, // YStride 635 kSourceWidth / 2); // UVStride 636 637 // Convert RGB32 to YV12 with reference version. 638 media::ConvertRGB32ToYUV_SSE2_Reference( 639 rgb_bytes.get(), 640 yuv_reference_bytes.get(), 641 yuv_reference_bytes.get() + kSourceUOffset, 642 yuv_reference_bytes.get() + kSourceVOffset, 643 7, 7, // Dimensions 644 kSourceWidth * 4, // RGBStride 645 kSourceWidth, // YStride 646 kSourceWidth / 2); // UVStride 647 648 int error = 0; 649 for (int i = 0; i < kYUV12Size; ++i) { 650 int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i]; 651 if (diff < 0) 652 diff = -diff; 653 error += diff; 654 } 655 656 // Make sure there's no difference from the reference. 657 EXPECT_EQ(0, error); 658 } 659 660 TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) { 661 base::CPU cpu; 662 if (!cpu.has_sse()) { 663 LOG(WARNING) << "System not supported. Test skipped."; 664 return; 665 } 666 667 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 668 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 669 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 670 ReadYV12Data(&yuv_bytes); 671 672 const int kWidth = 167; 673 ConvertYUVToRGB32Row_C(yuv_bytes.get(), 674 yuv_bytes.get() + kSourceUOffset, 675 yuv_bytes.get() + kSourceVOffset, 676 rgb_bytes_reference.get(), 677 kWidth, 678 GetLookupTable(YV12)); 679 ConvertYUVToRGB32Row_SSE(yuv_bytes.get(), 680 yuv_bytes.get() + kSourceUOffset, 681 yuv_bytes.get() + kSourceVOffset, 682 rgb_bytes_converted.get(), 683 kWidth, 684 GetLookupTable(YV12)); 685 media::EmptyRegisterState(); 686 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 687 rgb_bytes_converted.get(), 688 kWidth * kBpp)); 689 } 690 691 // 64-bit release + component builds on Windows are too smart and optimizes 692 // away the function being tested. 693 #if defined(OS_WIN) && (defined(ARCH_CPU_X86) || !defined(COMPONENT_BUILD)) 694 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) { 695 base::CPU cpu; 696 if (!cpu.has_sse()) { 697 LOG(WARNING) << "System not supported. Test skipped."; 698 return; 699 } 700 701 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 702 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 703 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 704 ReadYV12Data(&yuv_bytes); 705 706 const int kWidth = 167; 707 const int kSourceDx = 80000; // This value means a scale down. 708 ScaleYUVToRGB32Row_C(yuv_bytes.get(), 709 yuv_bytes.get() + kSourceUOffset, 710 yuv_bytes.get() + kSourceVOffset, 711 rgb_bytes_reference.get(), 712 kWidth, 713 kSourceDx, 714 GetLookupTable(YV12)); 715 ScaleYUVToRGB32Row_SSE(yuv_bytes.get(), 716 yuv_bytes.get() + kSourceUOffset, 717 yuv_bytes.get() + kSourceVOffset, 718 rgb_bytes_converted.get(), 719 kWidth, 720 kSourceDx, 721 GetLookupTable(YV12)); 722 media::EmptyRegisterState(); 723 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 724 rgb_bytes_converted.get(), 725 kWidth * kBpp)); 726 } 727 728 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) { 729 base::CPU cpu; 730 if (!cpu.has_sse()) { 731 LOG(WARNING) << "System not supported. Test skipped."; 732 return; 733 } 734 735 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 736 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 737 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 738 ReadYV12Data(&yuv_bytes); 739 740 const int kWidth = 167; 741 const int kSourceDx = 80000; // This value means a scale down. 742 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), 743 yuv_bytes.get() + kSourceUOffset, 744 yuv_bytes.get() + kSourceVOffset, 745 rgb_bytes_reference.get(), 746 kWidth, 747 kSourceDx, 748 GetLookupTable(YV12)); 749 LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(), 750 yuv_bytes.get() + kSourceUOffset, 751 yuv_bytes.get() + kSourceVOffset, 752 rgb_bytes_converted.get(), 753 kWidth, 754 kSourceDx, 755 GetLookupTable(YV12)); 756 media::EmptyRegisterState(); 757 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 758 rgb_bytes_converted.get(), 759 kWidth * kBpp)); 760 } 761 #endif // defined(OS_WIN) && (ARCH_CPU_X86 || COMPONENT_BUILD) 762 763 TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) { 764 scoped_ptr<uint8[]> src(new uint8[16]); 765 scoped_ptr<uint8[]> dst(new uint8[16]); 766 767 memset(src.get(), 0xff, 16); 768 memset(dst.get(), 0, 16); 769 770 media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255); 771 772 EXPECT_EQ(255u, dst[0]); 773 for (int i = 1; i < 16; ++i) { 774 EXPECT_EQ(0u, dst[i]) << " not equal at " << i; 775 } 776 } 777 778 TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) { 779 base::CPU cpu; 780 if (!cpu.has_sse2()) { 781 LOG(WARNING) << "System not supported. Test skipped."; 782 return; 783 } 784 785 scoped_ptr<uint8[]> src(new uint8[16]); 786 scoped_ptr<uint8[]> dst(new uint8[16]); 787 788 memset(src.get(), 0xff, 16); 789 memset(dst.get(), 0, 16); 790 791 media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255); 792 793 EXPECT_EQ(255u, dst[0]); 794 for (int i = 1; i < 16; ++i) { 795 EXPECT_EQ(0u, dst[i]); 796 } 797 } 798 799 TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) { 800 base::CPU cpu; 801 if (!cpu.has_sse2()) { 802 LOG(WARNING) << "System not supported. Test skipped."; 803 return; 804 } 805 806 const int kSize = 64; 807 scoped_ptr<uint8[]> src(new uint8[kSize]); 808 scoped_ptr<uint8[]> dst_sample(new uint8[kSize]); 809 scoped_ptr<uint8[]> dst(new uint8[kSize]); 810 811 memset(dst_sample.get(), 0, kSize); 812 memset(dst.get(), 0, kSize); 813 for (int i = 0; i < kSize; ++i) 814 src[i] = 100 + i; 815 816 media::FilterYUVRows_C(dst_sample.get(), 817 src.get(), src.get(), 37, 128); 818 819 // Generate an unaligned output address. 820 uint8* dst_ptr = 821 reinterpret_cast<uint8*>( 822 (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1); 823 media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128); 824 media::EmptyRegisterState(); 825 826 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37)); 827 } 828 829 #if defined(ARCH_CPU_X86_64) 830 831 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) { 832 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 833 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 834 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 835 ReadYV12Data(&yuv_bytes); 836 837 const int kWidth = 167; 838 const int kSourceDx = 80000; // This value means a scale down. 839 ScaleYUVToRGB32Row_C(yuv_bytes.get(), 840 yuv_bytes.get() + kSourceUOffset, 841 yuv_bytes.get() + kSourceVOffset, 842 rgb_bytes_reference.get(), 843 kWidth, 844 kSourceDx, 845 GetLookupTable(YV12)); 846 ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(), 847 yuv_bytes.get() + kSourceUOffset, 848 yuv_bytes.get() + kSourceVOffset, 849 rgb_bytes_converted.get(), 850 kWidth, 851 kSourceDx, 852 GetLookupTable(YV12)); 853 media::EmptyRegisterState(); 854 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 855 rgb_bytes_converted.get(), 856 kWidth * kBpp)); 857 } 858 859 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) { 860 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 861 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 862 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 863 ReadYV12Data(&yuv_bytes); 864 865 const int kWidth = 167; 866 const int kSourceDx = 80000; // This value means a scale down. 867 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), 868 yuv_bytes.get() + kSourceUOffset, 869 yuv_bytes.get() + kSourceVOffset, 870 rgb_bytes_reference.get(), 871 kWidth, 872 kSourceDx, 873 GetLookupTable(YV12)); 874 LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(), 875 yuv_bytes.get() + kSourceUOffset, 876 yuv_bytes.get() + kSourceVOffset, 877 rgb_bytes_converted.get(), 878 kWidth, 879 kSourceDx, 880 GetLookupTable(YV12)); 881 media::EmptyRegisterState(); 882 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 883 rgb_bytes_converted.get(), 884 kWidth * kBpp)); 885 } 886 887 #endif // defined(ARCH_CPU_X86_64) 888 889 #endif // defined(ARCH_CPU_X86_FAMILY) 890 891 } // namespace media 892