1 /* 2 * libjingle 3 * Copyright 2004 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ 29 #define TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ 30 31 #include <algorithm> 32 #include <string> 33 34 #include "libyuv/convert.h" 35 #include "libyuv/convert_from.h" 36 #include "libyuv/planar_functions.h" 37 #include "libyuv/rotate.h" 38 #include "talk/media/base/testutils.h" 39 #include "talk/media/base/videocommon.h" 40 #include "talk/media/base/videoframe.h" 41 #include "webrtc/base/gunit.h" 42 #include "webrtc/base/pathutils.h" 43 #include "webrtc/base/stream.h" 44 #include "webrtc/base/stringutils.h" 45 #include "webrtc/common_video/rotation.h" 46 47 #if defined(_MSC_VER) 48 #define ALIGN16(var) __declspec(align(16)) var 49 #else 50 #define ALIGN16(var) var __attribute__((aligned(16))) 51 #endif 52 53 #define kImageFilename "faces.1280x720_P420.yuv" 54 #define kJpeg420Filename "faces_I420.jpg" 55 #define kJpeg422Filename "faces_I422.jpg" 56 #define kJpeg444Filename "faces_I444.jpg" 57 #define kJpeg411Filename "faces_I411.jpg" 58 #define kJpeg400Filename "faces_I400.jpg" 59 60 // Generic test class for testing various video frame implementations. 61 template <class T> 62 class VideoFrameTest : public testing::Test { 63 public: 64 VideoFrameTest() : repeat_(1) {} 65 66 protected: 67 static const int kWidth = 1280; 68 static const int kHeight = 720; 69 static const int kAlignment = 16; 70 static const int kMinWidthAll = 1; // Constants for ConstructYUY2AllSizes. 71 static const int kMinHeightAll = 1; 72 static const int kMaxWidthAll = 17; 73 static const int kMaxHeightAll = 23; 74 75 // Load a video frame from disk. 76 bool LoadFrameNoRepeat(T* frame) { 77 int save_repeat = repeat_; // This LoadFrame disables repeat. 78 repeat_ = 1; 79 bool success = LoadFrame(kImageFilename, cricket::FOURCC_I420, 80 kWidth, kHeight, frame); 81 repeat_ = save_repeat; 82 return success; 83 } 84 85 bool LoadFrame(const std::string& filename, 86 uint32_t format, 87 int32_t width, 88 int32_t height, 89 T* frame) { 90 return LoadFrame(filename, format, width, height, width, abs(height), 91 webrtc::kVideoRotation_0, frame); 92 } 93 bool LoadFrame(const std::string& filename, 94 uint32_t format, 95 int32_t width, 96 int32_t height, 97 int dw, 98 int dh, 99 webrtc::VideoRotation rotation, 100 T* frame) { 101 rtc::scoped_ptr<rtc::MemoryStream> ms(LoadSample(filename)); 102 return LoadFrame(ms.get(), format, width, height, dw, dh, rotation, frame); 103 } 104 // Load a video frame from a memory stream. 105 bool LoadFrame(rtc::MemoryStream* ms, 106 uint32_t format, 107 int32_t width, 108 int32_t height, 109 T* frame) { 110 return LoadFrame(ms, format, width, height, width, abs(height), 111 webrtc::kVideoRotation_0, frame); 112 } 113 bool LoadFrame(rtc::MemoryStream* ms, 114 uint32_t format, 115 int32_t width, 116 int32_t height, 117 int dw, 118 int dh, 119 webrtc::VideoRotation rotation, 120 T* frame) { 121 if (!ms) { 122 return false; 123 } 124 size_t data_size; 125 bool ret = ms->GetSize(&data_size); 126 EXPECT_TRUE(ret); 127 if (ret) { 128 ret = LoadFrame(reinterpret_cast<uint8_t*>(ms->GetBuffer()), data_size, 129 format, width, height, dw, dh, rotation, frame); 130 } 131 return ret; 132 } 133 // Load a frame from a raw buffer. 134 bool LoadFrame(uint8_t* sample, 135 size_t sample_size, 136 uint32_t format, 137 int32_t width, 138 int32_t height, 139 T* frame) { 140 return LoadFrame(sample, sample_size, format, width, height, width, 141 abs(height), webrtc::kVideoRotation_0, frame); 142 } 143 bool LoadFrame(uint8_t* sample, 144 size_t sample_size, 145 uint32_t format, 146 int32_t width, 147 int32_t height, 148 int dw, 149 int dh, 150 webrtc::VideoRotation rotation, 151 T* frame) { 152 bool ret = false; 153 for (int i = 0; i < repeat_; ++i) { 154 ret = frame->Init(format, width, height, dw, dh, 155 sample, sample_size, 1, 1, 0, rotation); 156 } 157 return ret; 158 } 159 160 rtc::MemoryStream* LoadSample(const std::string& filename) { 161 rtc::Pathname path(cricket::GetTestFilePath(filename)); 162 rtc::scoped_ptr<rtc::FileStream> fs( 163 rtc::Filesystem::OpenFile(path, "rb")); 164 if (!fs.get()) { 165 LOG(LS_ERROR) << "Could not open test file path: " << path.pathname() 166 << " from current dir " 167 << rtc::Filesystem::GetCurrentDirectory().pathname(); 168 return NULL; 169 } 170 171 char buf[4096]; 172 rtc::scoped_ptr<rtc::MemoryStream> ms( 173 new rtc::MemoryStream()); 174 rtc::StreamResult res = Flow(fs.get(), buf, sizeof(buf), ms.get()); 175 if (res != rtc::SR_SUCCESS) { 176 LOG(LS_ERROR) << "Could not load test file path: " << path.pathname(); 177 return NULL; 178 } 179 180 return ms.release(); 181 } 182 183 // Write an I420 frame out to disk. 184 bool DumpFrame(const std::string& prefix, 185 const cricket::VideoFrame& frame) { 186 char filename[256]; 187 rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv", 188 prefix.c_str(), frame.GetWidth(), frame.GetHeight()); 189 size_t out_size = cricket::VideoFrame::SizeOf(frame.GetWidth(), 190 frame.GetHeight()); 191 rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size]); 192 frame.CopyToBuffer(out.get(), out_size); 193 return DumpSample(filename, out.get(), out_size); 194 } 195 196 bool DumpSample(const std::string& filename, const void* buffer, int size) { 197 rtc::Pathname path(filename); 198 rtc::scoped_ptr<rtc::FileStream> fs( 199 rtc::Filesystem::OpenFile(path, "wb")); 200 if (!fs.get()) { 201 return false; 202 } 203 204 return (fs->Write(buffer, size, NULL, NULL) == rtc::SR_SUCCESS); 205 } 206 207 // Create a test image in the desired color space. 208 // The image is a checkerboard pattern with 63x63 squares, which allows 209 // I420 chroma artifacts to easily be seen on the square boundaries. 210 // The pattern is { { green, orange }, { blue, purple } } 211 // There is also a gradient within each square to ensure that the luma 212 // values are handled properly. 213 rtc::MemoryStream* CreateYuv422Sample(uint32_t fourcc, 214 uint32_t width, 215 uint32_t height) { 216 int y1_pos, y2_pos, u_pos, v_pos; 217 if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) { 218 return NULL; 219 } 220 221 rtc::scoped_ptr<rtc::MemoryStream> ms( 222 new rtc::MemoryStream); 223 int awidth = (width + 1) & ~1; 224 int size = awidth * 2 * height; 225 if (!ms->ReserveSize(size)) { 226 return NULL; 227 } 228 for (uint32_t y = 0; y < height; ++y) { 229 for (int x = 0; x < awidth; x += 2) { 230 uint8_t quad[4]; 231 quad[y1_pos] = (x % 63 + y % 63) + 64; 232 quad[y2_pos] = ((x + 1) % 63 + y % 63) + 64; 233 quad[u_pos] = ((x / 63) & 1) ? 192 : 64; 234 quad[v_pos] = ((y / 63) & 1) ? 192 : 64; 235 ms->Write(quad, sizeof(quad), NULL, NULL); 236 } 237 } 238 return ms.release(); 239 } 240 241 // Create a test image for YUV 420 formats with 12 bits per pixel. 242 rtc::MemoryStream* CreateYuvSample(uint32_t width, 243 uint32_t height, 244 uint32_t bpp) { 245 rtc::scoped_ptr<rtc::MemoryStream> ms( 246 new rtc::MemoryStream); 247 if (!ms->ReserveSize(width * height * bpp / 8)) { 248 return NULL; 249 } 250 251 for (uint32_t i = 0; i < width * height * bpp / 8; ++i) { 252 char value = ((i / 63) & 1) ? 192 : 64; 253 ms->Write(&value, sizeof(value), NULL, NULL); 254 } 255 return ms.release(); 256 } 257 258 rtc::MemoryStream* CreateRgbSample(uint32_t fourcc, 259 uint32_t width, 260 uint32_t height) { 261 int r_pos, g_pos, b_pos, bytes; 262 if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) { 263 return NULL; 264 } 265 266 rtc::scoped_ptr<rtc::MemoryStream> ms( 267 new rtc::MemoryStream); 268 if (!ms->ReserveSize(width * height * bytes)) { 269 return NULL; 270 } 271 272 for (uint32_t y = 0; y < height; ++y) { 273 for (uint32_t x = 0; x < width; ++x) { 274 uint8_t rgb[4] = {255, 255, 255, 255}; 275 rgb[r_pos] = ((x / 63) & 1) ? 224 : 32; 276 rgb[g_pos] = (x % 63 + y % 63) + 96; 277 rgb[b_pos] = ((y / 63) & 1) ? 224 : 32; 278 ms->Write(rgb, bytes, NULL, NULL); 279 } 280 } 281 return ms.release(); 282 } 283 284 // Simple conversion routines to verify the optimized VideoFrame routines. 285 // Converts from the specified colorspace to I420. 286 bool ConvertYuv422(const rtc::MemoryStream* ms, 287 uint32_t fourcc, 288 uint32_t width, 289 uint32_t height, 290 T* frame) { 291 int y1_pos, y2_pos, u_pos, v_pos; 292 if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) { 293 return false; 294 } 295 296 const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer()); 297 int awidth = (width + 1) & ~1; 298 frame->InitToBlack(width, height, 1, 1, 0); 299 int stride_y = frame->GetYPitch(); 300 int stride_u = frame->GetUPitch(); 301 int stride_v = frame->GetVPitch(); 302 for (uint32_t y = 0; y < height; ++y) { 303 for (uint32_t x = 0; x < width; x += 2) { 304 const uint8_t* quad1 = start + (y * awidth + x) * 2; 305 frame->GetYPlane()[stride_y * y + x] = quad1[y1_pos]; 306 if ((x + 1) < width) { 307 frame->GetYPlane()[stride_y * y + x + 1] = quad1[y2_pos]; 308 } 309 if ((y & 1) == 0) { 310 const uint8_t* quad2 = quad1 + awidth * 2; 311 if ((y + 1) >= height) { 312 quad2 = quad1; 313 } 314 frame->GetUPlane()[stride_u * (y / 2) + x / 2] = 315 (quad1[u_pos] + quad2[u_pos] + 1) / 2; 316 frame->GetVPlane()[stride_v * (y / 2) + x / 2] = 317 (quad1[v_pos] + quad2[v_pos] + 1) / 2; 318 } 319 } 320 } 321 return true; 322 } 323 324 // Convert RGB to 420. 325 // A negative height inverts the image. 326 bool ConvertRgb(const rtc::MemoryStream* ms, 327 uint32_t fourcc, 328 int32_t width, 329 int32_t height, 330 T* frame) { 331 int r_pos, g_pos, b_pos, bytes; 332 if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) { 333 return false; 334 } 335 int pitch = width * bytes; 336 const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer()); 337 if (height < 0) { 338 height = -height; 339 start = start + pitch * (height - 1); 340 pitch = -pitch; 341 } 342 frame->InitToBlack(width, height, 1, 1, 0); 343 int stride_y = frame->GetYPitch(); 344 int stride_u = frame->GetUPitch(); 345 int stride_v = frame->GetVPitch(); 346 for (int32_t y = 0; y < height; y += 2) { 347 for (int32_t x = 0; x < width; x += 2) { 348 const uint8_t* rgb[4]; 349 uint8_t yuv[4][3]; 350 rgb[0] = start + y * pitch + x * bytes; 351 rgb[1] = rgb[0] + ((x + 1) < width ? bytes : 0); 352 rgb[2] = rgb[0] + ((y + 1) < height ? pitch : 0); 353 rgb[3] = rgb[2] + ((x + 1) < width ? bytes : 0); 354 for (size_t i = 0; i < 4; ++i) { 355 ConvertRgbPixel(rgb[i][r_pos], rgb[i][g_pos], rgb[i][b_pos], 356 &yuv[i][0], &yuv[i][1], &yuv[i][2]); 357 } 358 frame->GetYPlane()[stride_y * y + x] = yuv[0][0]; 359 if ((x + 1) < width) { 360 frame->GetYPlane()[stride_y * y + x + 1] = yuv[1][0]; 361 } 362 if ((y + 1) < height) { 363 frame->GetYPlane()[stride_y * (y + 1) + x] = yuv[2][0]; 364 if ((x + 1) < width) { 365 frame->GetYPlane()[stride_y * (y + 1) + x + 1] = yuv[3][0]; 366 } 367 } 368 frame->GetUPlane()[stride_u * (y / 2) + x / 2] = 369 (yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1] + 2) / 4; 370 frame->GetVPlane()[stride_v * (y / 2) + x / 2] = 371 (yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2] + 2) / 4; 372 } 373 } 374 return true; 375 } 376 377 // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia. 378 void ConvertRgbPixel(uint8_t r, 379 uint8_t g, 380 uint8_t b, 381 uint8_t* y, 382 uint8_t* u, 383 uint8_t* v) { 384 *y = static_cast<int>(.257 * r + .504 * g + .098 * b) + 16; 385 *u = static_cast<int>(-.148 * r - .291 * g + .439 * b) + 128; 386 *v = static_cast<int>(.439 * r - .368 * g - .071 * b) + 128; 387 } 388 389 bool GetYuv422Packing(uint32_t fourcc, 390 int* y1_pos, 391 int* y2_pos, 392 int* u_pos, 393 int* v_pos) { 394 if (fourcc == cricket::FOURCC_YUY2) { 395 *y1_pos = 0; *u_pos = 1; *y2_pos = 2; *v_pos = 3; 396 } else if (fourcc == cricket::FOURCC_UYVY) { 397 *u_pos = 0; *y1_pos = 1; *v_pos = 2; *y2_pos = 3; 398 } else { 399 return false; 400 } 401 return true; 402 } 403 404 bool GetRgbPacking(uint32_t fourcc, 405 int* r_pos, 406 int* g_pos, 407 int* b_pos, 408 int* bytes) { 409 if (fourcc == cricket::FOURCC_RAW) { 410 *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 3; // RGB in memory. 411 } else if (fourcc == cricket::FOURCC_24BG) { 412 *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 3; // BGR in memory. 413 } else if (fourcc == cricket::FOURCC_ABGR) { 414 *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 4; // RGBA in memory. 415 } else if (fourcc == cricket::FOURCC_BGRA) { 416 *r_pos = 1; *g_pos = 2; *b_pos = 3; *bytes = 4; // ARGB in memory. 417 } else if (fourcc == cricket::FOURCC_ARGB) { 418 *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 4; // BGRA in memory. 419 } else { 420 return false; 421 } 422 return true; 423 } 424 425 // Comparison functions for testing. 426 static bool IsNull(const cricket::VideoFrame& frame) { 427 return !frame.GetYPlane(); 428 } 429 430 static bool IsSize(const cricket::VideoFrame& frame, 431 uint32_t width, 432 uint32_t height) { 433 return !IsNull(frame) && frame.GetYPitch() >= static_cast<int32_t>(width) && 434 frame.GetUPitch() >= static_cast<int32_t>(width) / 2 && 435 frame.GetVPitch() >= static_cast<int32_t>(width) / 2 && 436 frame.GetWidth() == width && frame.GetHeight() == height; 437 } 438 439 static bool IsPlaneEqual(const std::string& name, 440 const uint8_t* plane1, 441 uint32_t pitch1, 442 const uint8_t* plane2, 443 uint32_t pitch2, 444 uint32_t width, 445 uint32_t height, 446 int max_error) { 447 const uint8_t* r1 = plane1; 448 const uint8_t* r2 = plane2; 449 for (uint32_t y = 0; y < height; ++y) { 450 for (uint32_t x = 0; x < width; ++x) { 451 if (abs(static_cast<int>(r1[x] - r2[x])) > max_error) { 452 LOG(LS_INFO) << "IsPlaneEqual(" << name << "): pixel[" 453 << x << "," << y << "] differs: " 454 << static_cast<int>(r1[x]) << " vs " 455 << static_cast<int>(r2[x]); 456 return false; 457 } 458 } 459 r1 += pitch1; 460 r2 += pitch2; 461 } 462 return true; 463 } 464 465 static bool IsEqual(const cricket::VideoFrame& frame, 466 size_t width, 467 size_t height, 468 size_t pixel_width, 469 size_t pixel_height, 470 int64_t time_stamp, 471 const uint8_t* y, 472 uint32_t ypitch, 473 const uint8_t* u, 474 uint32_t upitch, 475 const uint8_t* v, 476 uint32_t vpitch, 477 int max_error) { 478 return IsSize(frame, static_cast<uint32_t>(width), 479 static_cast<uint32_t>(height)) && 480 frame.GetPixelWidth() == pixel_width && 481 frame.GetPixelHeight() == pixel_height && 482 frame.GetTimeStamp() == time_stamp && 483 IsPlaneEqual("y", frame.GetYPlane(), frame.GetYPitch(), y, ypitch, 484 static_cast<uint32_t>(width), 485 static_cast<uint32_t>(height), max_error) && 486 IsPlaneEqual("u", frame.GetUPlane(), frame.GetUPitch(), u, upitch, 487 static_cast<uint32_t>((width + 1) / 2), 488 static_cast<uint32_t>((height + 1) / 2), max_error) && 489 IsPlaneEqual("v", frame.GetVPlane(), frame.GetVPitch(), v, vpitch, 490 static_cast<uint32_t>((width + 1) / 2), 491 static_cast<uint32_t>((height + 1) / 2), max_error); 492 } 493 494 static bool IsEqual(const cricket::VideoFrame& frame1, 495 const cricket::VideoFrame& frame2, 496 int max_error) { 497 return IsEqual(frame1, 498 frame2.GetWidth(), frame2.GetHeight(), 499 frame2.GetPixelWidth(), frame2.GetPixelHeight(), 500 frame2.GetTimeStamp(), 501 frame2.GetYPlane(), frame2.GetYPitch(), 502 frame2.GetUPlane(), frame2.GetUPitch(), 503 frame2.GetVPlane(), frame2.GetVPitch(), 504 max_error); 505 } 506 507 static bool IsEqualWithCrop(const cricket::VideoFrame& frame1, 508 const cricket::VideoFrame& frame2, 509 int hcrop, int vcrop, int max_error) { 510 return frame1.GetWidth() <= frame2.GetWidth() && 511 frame1.GetHeight() <= frame2.GetHeight() && 512 IsEqual(frame1, 513 frame2.GetWidth() - hcrop * 2, 514 frame2.GetHeight() - vcrop * 2, 515 frame2.GetPixelWidth(), frame2.GetPixelHeight(), 516 frame2.GetTimeStamp(), 517 frame2.GetYPlane() + vcrop * frame2.GetYPitch() 518 + hcrop, 519 frame2.GetYPitch(), 520 frame2.GetUPlane() + vcrop * frame2.GetUPitch() / 2 521 + hcrop / 2, 522 frame2.GetUPitch(), 523 frame2.GetVPlane() + vcrop * frame2.GetVPitch() / 2 524 + hcrop / 2, 525 frame2.GetVPitch(), 526 max_error); 527 } 528 529 static bool IsBlack(const cricket::VideoFrame& frame) { 530 return !IsNull(frame) && 531 *frame.GetYPlane() == 16 && 532 *frame.GetUPlane() == 128 && 533 *frame.GetVPlane() == 128; 534 } 535 536 //////////////////////// 537 // Construction tests // 538 //////////////////////// 539 540 // Test constructing an image from a I420 buffer. 541 void ConstructI420() { 542 T frame; 543 EXPECT_TRUE(IsNull(frame)); 544 rtc::scoped_ptr<rtc::MemoryStream> ms( 545 CreateYuvSample(kWidth, kHeight, 12)); 546 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, 547 kWidth, kHeight, &frame)); 548 549 const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer()); 550 const uint8_t* u = y + kWidth * kHeight; 551 const uint8_t* v = u + kWidth * kHeight / 4; 552 EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 1, 1, 0, y, kWidth, u, 553 kWidth / 2, v, kWidth / 2, 0)); 554 } 555 556 // Test constructing an image from a YV12 buffer. 557 void ConstructYV12() { 558 T frame; 559 rtc::scoped_ptr<rtc::MemoryStream> ms( 560 CreateYuvSample(kWidth, kHeight, 12)); 561 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YV12, 562 kWidth, kHeight, &frame)); 563 564 const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer()); 565 const uint8_t* v = y + kWidth * kHeight; 566 const uint8_t* u = v + kWidth * kHeight / 4; 567 EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 1, 1, 0, y, kWidth, u, 568 kWidth / 2, v, kWidth / 2, 0)); 569 } 570 571 // Test constructing an image from a I422 buffer. 572 void ConstructI422() { 573 T frame1, frame2; 574 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 575 size_t buf_size = kWidth * kHeight * 2; 576 rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]); 577 uint8_t* y = ALIGNP(buf.get(), kAlignment); 578 uint8_t* u = y + kWidth * kHeight; 579 uint8_t* v = u + (kWidth / 2) * kHeight; 580 EXPECT_EQ(0, libyuv::I420ToI422(frame1.GetYPlane(), frame1.GetYPitch(), 581 frame1.GetUPlane(), frame1.GetUPitch(), 582 frame1.GetVPlane(), frame1.GetVPitch(), 583 y, kWidth, 584 u, kWidth / 2, 585 v, kWidth / 2, 586 kWidth, kHeight)); 587 EXPECT_TRUE(LoadFrame(y, buf_size, cricket::FOURCC_I422, 588 kWidth, kHeight, &frame2)); 589 EXPECT_TRUE(IsEqual(frame1, frame2, 1)); 590 } 591 592 // Test constructing an image from a YUY2 buffer. 593 void ConstructYuy2() { 594 T frame1, frame2; 595 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 596 size_t buf_size = kWidth * kHeight * 2; 597 rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]); 598 uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment); 599 EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), 600 frame1.GetUPlane(), frame1.GetUPitch(), 601 frame1.GetVPlane(), frame1.GetVPitch(), 602 yuy2, kWidth * 2, 603 kWidth, kHeight)); 604 EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2, 605 kWidth, kHeight, &frame2)); 606 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 607 } 608 609 // Test constructing an image from a YUY2 buffer with buffer unaligned. 610 void ConstructYuy2Unaligned() { 611 T frame1, frame2; 612 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 613 size_t buf_size = kWidth * kHeight * 2; 614 rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment + 1]); 615 uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment) + 1; 616 EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), 617 frame1.GetUPlane(), frame1.GetUPitch(), 618 frame1.GetVPlane(), frame1.GetVPitch(), 619 yuy2, kWidth * 2, 620 kWidth, kHeight)); 621 EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2, 622 kWidth, kHeight, &frame2)); 623 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 624 } 625 626 // Test constructing an image from a wide YUY2 buffer. 627 // Normal is 1280x720. Wide is 12800x72 628 void ConstructYuy2Wide() { 629 T frame1, frame2; 630 rtc::scoped_ptr<rtc::MemoryStream> ms( 631 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth * 10, kHeight / 10)); 632 ASSERT_TRUE(ms.get() != NULL); 633 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, 634 kWidth * 10, kHeight / 10, 635 &frame1)); 636 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, 637 kWidth * 10, kHeight / 10, &frame2)); 638 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 639 } 640 641 // Test constructing an image from a UYVY buffer. 642 void ConstructUyvy() { 643 T frame1, frame2; 644 rtc::scoped_ptr<rtc::MemoryStream> ms( 645 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); 646 ASSERT_TRUE(ms.get() != NULL); 647 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, 648 &frame1)); 649 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, 650 kWidth, kHeight, &frame2)); 651 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 652 } 653 654 // Test constructing an image from a random buffer. 655 // We are merely verifying that the code succeeds and is free of crashes. 656 void ConstructM420() { 657 T frame; 658 rtc::scoped_ptr<rtc::MemoryStream> ms( 659 CreateYuvSample(kWidth, kHeight, 12)); 660 ASSERT_TRUE(ms.get() != NULL); 661 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_M420, 662 kWidth, kHeight, &frame)); 663 } 664 665 void ConstructNV21() { 666 T frame; 667 rtc::scoped_ptr<rtc::MemoryStream> ms( 668 CreateYuvSample(kWidth, kHeight, 12)); 669 ASSERT_TRUE(ms.get() != NULL); 670 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV21, 671 kWidth, kHeight, &frame)); 672 } 673 674 void ConstructNV12() { 675 T frame; 676 rtc::scoped_ptr<rtc::MemoryStream> ms( 677 CreateYuvSample(kWidth, kHeight, 12)); 678 ASSERT_TRUE(ms.get() != NULL); 679 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV12, 680 kWidth, kHeight, &frame)); 681 } 682 683 // Test constructing an image from a ABGR buffer 684 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. 685 void ConstructABGR() { 686 T frame1, frame2; 687 rtc::scoped_ptr<rtc::MemoryStream> ms( 688 CreateRgbSample(cricket::FOURCC_ABGR, kWidth, kHeight)); 689 ASSERT_TRUE(ms.get() != NULL); 690 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ABGR, kWidth, kHeight, 691 &frame1)); 692 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ABGR, 693 kWidth, kHeight, &frame2)); 694 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 695 } 696 697 // Test constructing an image from a ARGB buffer 698 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. 699 void ConstructARGB() { 700 T frame1, frame2; 701 rtc::scoped_ptr<rtc::MemoryStream> ms( 702 CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight)); 703 ASSERT_TRUE(ms.get() != NULL); 704 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight, 705 &frame1)); 706 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, 707 kWidth, kHeight, &frame2)); 708 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 709 } 710 711 // Test constructing an image from a wide ARGB buffer 712 // Normal is 1280x720. Wide is 12800x72 713 void ConstructARGBWide() { 714 T frame1, frame2; 715 rtc::scoped_ptr<rtc::MemoryStream> ms( 716 CreateRgbSample(cricket::FOURCC_ARGB, kWidth * 10, kHeight / 10)); 717 ASSERT_TRUE(ms.get() != NULL); 718 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, 719 kWidth * 10, kHeight / 10, &frame1)); 720 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, 721 kWidth * 10, kHeight / 10, &frame2)); 722 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 723 } 724 725 // Test constructing an image from an BGRA buffer. 726 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. 727 void ConstructBGRA() { 728 T frame1, frame2; 729 rtc::scoped_ptr<rtc::MemoryStream> ms( 730 CreateRgbSample(cricket::FOURCC_BGRA, kWidth, kHeight)); 731 ASSERT_TRUE(ms.get() != NULL); 732 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_BGRA, kWidth, kHeight, 733 &frame1)); 734 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_BGRA, 735 kWidth, kHeight, &frame2)); 736 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 737 } 738 739 // Test constructing an image from a 24BG buffer. 740 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. 741 void Construct24BG() { 742 T frame1, frame2; 743 rtc::scoped_ptr<rtc::MemoryStream> ms( 744 CreateRgbSample(cricket::FOURCC_24BG, kWidth, kHeight)); 745 ASSERT_TRUE(ms.get() != NULL); 746 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_24BG, kWidth, kHeight, 747 &frame1)); 748 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_24BG, 749 kWidth, kHeight, &frame2)); 750 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 751 } 752 753 // Test constructing an image from a raw RGB buffer. 754 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. 755 void ConstructRaw() { 756 T frame1, frame2; 757 rtc::scoped_ptr<rtc::MemoryStream> ms( 758 CreateRgbSample(cricket::FOURCC_RAW, kWidth, kHeight)); 759 ASSERT_TRUE(ms.get() != NULL); 760 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_RAW, kWidth, kHeight, 761 &frame1)); 762 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_RAW, 763 kWidth, kHeight, &frame2)); 764 EXPECT_TRUE(IsEqual(frame1, frame2, 2)); 765 } 766 767 // Test constructing an image from a RGB565 buffer 768 void ConstructRGB565() { 769 T frame1, frame2; 770 size_t out_size = kWidth * kHeight * 2; 771 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]); 772 uint8_t* out = ALIGNP(outbuf.get(), kAlignment); 773 T frame; 774 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 775 EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_RGBP, 776 out, 777 out_size, kWidth * 2)); 778 EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_RGBP, 779 kWidth, kHeight, &frame2)); 780 EXPECT_TRUE(IsEqual(frame1, frame2, 20)); 781 } 782 783 // Test constructing an image from a ARGB1555 buffer 784 void ConstructARGB1555() { 785 T frame1, frame2; 786 size_t out_size = kWidth * kHeight * 2; 787 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]); 788 uint8_t* out = ALIGNP(outbuf.get(), kAlignment); 789 T frame; 790 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 791 EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_RGBO, 792 out, 793 out_size, kWidth * 2)); 794 EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_RGBO, 795 kWidth, kHeight, &frame2)); 796 EXPECT_TRUE(IsEqual(frame1, frame2, 20)); 797 } 798 799 // Test constructing an image from a ARGB4444 buffer 800 void ConstructARGB4444() { 801 T frame1, frame2; 802 size_t out_size = kWidth * kHeight * 2; 803 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]); 804 uint8_t* out = ALIGNP(outbuf.get(), kAlignment); 805 T frame; 806 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 807 EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_R444, 808 out, 809 out_size, kWidth * 2)); 810 EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_R444, 811 kWidth, kHeight, &frame2)); 812 EXPECT_TRUE(IsEqual(frame1, frame2, 20)); 813 } 814 815 // Macro to help test different rotations 816 #define TEST_MIRROR(FOURCC, BPP) \ 817 void Construct##FOURCC##Mirror() { \ 818 T frame1, frame2, frame3; \ 819 rtc::scoped_ptr<rtc::MemoryStream> ms( \ 820 CreateYuvSample(kWidth, kHeight, BPP)); \ 821 ASSERT_TRUE(ms.get() != NULL); \ 822 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth, \ 823 -kHeight, kWidth, kHeight, \ 824 webrtc::kVideoRotation_180, &frame1)); \ 825 size_t data_size; \ 826 bool ret = ms->GetSize(&data_size); \ 827 EXPECT_TRUE(ret); \ 828 EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \ 829 kHeight, \ 830 reinterpret_cast<uint8_t*>(ms->GetBuffer()), \ 831 data_size, 1, 1, 0, webrtc::kVideoRotation_0)); \ 832 int width_rotate = static_cast<int>(frame1.GetWidth()); \ 833 int height_rotate = static_cast<int>(frame1.GetHeight()); \ 834 EXPECT_TRUE(frame3.InitToBlack(width_rotate, height_rotate, 1, 1, 0)); \ 835 libyuv::I420Mirror( \ 836 frame2.GetYPlane(), frame2.GetYPitch(), frame2.GetUPlane(), \ 837 frame2.GetUPitch(), frame2.GetVPlane(), frame2.GetVPitch(), \ 838 frame3.GetYPlane(), frame3.GetYPitch(), frame3.GetUPlane(), \ 839 frame3.GetUPitch(), frame3.GetVPlane(), frame3.GetVPitch(), kWidth, \ 840 kHeight); \ 841 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); \ 842 } 843 844 TEST_MIRROR(I420, 420) 845 846 // Macro to help test different rotations 847 #define TEST_ROTATE(FOURCC, BPP, ROTATE) \ 848 void Construct##FOURCC##Rotate##ROTATE() { \ 849 T frame1, frame2, frame3; \ 850 rtc::scoped_ptr<rtc::MemoryStream> ms( \ 851 CreateYuvSample(kWidth, kHeight, BPP)); \ 852 ASSERT_TRUE(ms.get() != NULL); \ 853 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth, kHeight, \ 854 kWidth, kHeight, webrtc::kVideoRotation_##ROTATE, \ 855 &frame1)); \ 856 size_t data_size; \ 857 bool ret = ms->GetSize(&data_size); \ 858 EXPECT_TRUE(ret); \ 859 EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \ 860 kHeight, \ 861 reinterpret_cast<uint8_t*>(ms->GetBuffer()), \ 862 data_size, 1, 1, 0, webrtc::kVideoRotation_0)); \ 863 int width_rotate = static_cast<int>(frame1.GetWidth()); \ 864 int height_rotate = static_cast<int>(frame1.GetHeight()); \ 865 EXPECT_TRUE(frame3.InitToBlack(width_rotate, height_rotate, 1, 1, 0)); \ 866 libyuv::I420Rotate( \ 867 frame2.GetYPlane(), frame2.GetYPitch(), frame2.GetUPlane(), \ 868 frame2.GetUPitch(), frame2.GetVPlane(), frame2.GetVPitch(), \ 869 frame3.GetYPlane(), frame3.GetYPitch(), frame3.GetUPlane(), \ 870 frame3.GetUPitch(), frame3.GetVPlane(), frame3.GetVPitch(), kWidth, \ 871 kHeight, libyuv::kRotate##ROTATE); \ 872 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); \ 873 } 874 875 // Test constructing an image with rotation. 876 TEST_ROTATE(I420, 12, 0) 877 TEST_ROTATE(I420, 12, 90) 878 TEST_ROTATE(I420, 12, 180) 879 TEST_ROTATE(I420, 12, 270) 880 TEST_ROTATE(YV12, 12, 0) 881 TEST_ROTATE(YV12, 12, 90) 882 TEST_ROTATE(YV12, 12, 180) 883 TEST_ROTATE(YV12, 12, 270) 884 TEST_ROTATE(NV12, 12, 0) 885 TEST_ROTATE(NV12, 12, 90) 886 TEST_ROTATE(NV12, 12, 180) 887 TEST_ROTATE(NV12, 12, 270) 888 TEST_ROTATE(NV21, 12, 0) 889 TEST_ROTATE(NV21, 12, 90) 890 TEST_ROTATE(NV21, 12, 180) 891 TEST_ROTATE(NV21, 12, 270) 892 TEST_ROTATE(UYVY, 16, 0) 893 TEST_ROTATE(UYVY, 16, 90) 894 TEST_ROTATE(UYVY, 16, 180) 895 TEST_ROTATE(UYVY, 16, 270) 896 TEST_ROTATE(YUY2, 16, 0) 897 TEST_ROTATE(YUY2, 16, 90) 898 TEST_ROTATE(YUY2, 16, 180) 899 TEST_ROTATE(YUY2, 16, 270) 900 901 // Test constructing an image from a UYVY buffer rotated 90 degrees. 902 void ConstructUyvyRotate90() { 903 T frame2; 904 rtc::scoped_ptr<rtc::MemoryStream> ms( 905 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); 906 ASSERT_TRUE(ms.get() != NULL); 907 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, 908 kWidth, kHeight, webrtc::kVideoRotation_90, &frame2)); 909 } 910 911 // Test constructing an image from a UYVY buffer rotated 180 degrees. 912 void ConstructUyvyRotate180() { 913 T frame2; 914 rtc::scoped_ptr<rtc::MemoryStream> ms( 915 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); 916 ASSERT_TRUE(ms.get() != NULL); 917 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, 918 kWidth, kHeight, webrtc::kVideoRotation_180, 919 &frame2)); 920 } 921 922 // Test constructing an image from a UYVY buffer rotated 270 degrees. 923 void ConstructUyvyRotate270() { 924 T frame2; 925 rtc::scoped_ptr<rtc::MemoryStream> ms( 926 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); 927 ASSERT_TRUE(ms.get() != NULL); 928 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, 929 kWidth, kHeight, webrtc::kVideoRotation_270, 930 &frame2)); 931 } 932 933 // Test constructing an image from a YUY2 buffer rotated 90 degrees. 934 void ConstructYuy2Rotate90() { 935 T frame2; 936 rtc::scoped_ptr<rtc::MemoryStream> ms( 937 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); 938 ASSERT_TRUE(ms.get() != NULL); 939 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 940 kWidth, kHeight, webrtc::kVideoRotation_90, &frame2)); 941 } 942 943 // Test constructing an image from a YUY2 buffer rotated 180 degrees. 944 void ConstructYuy2Rotate180() { 945 T frame2; 946 rtc::scoped_ptr<rtc::MemoryStream> ms( 947 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); 948 ASSERT_TRUE(ms.get() != NULL); 949 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 950 kWidth, kHeight, webrtc::kVideoRotation_180, 951 &frame2)); 952 } 953 954 // Test constructing an image from a YUY2 buffer rotated 270 degrees. 955 void ConstructYuy2Rotate270() { 956 T frame2; 957 rtc::scoped_ptr<rtc::MemoryStream> ms( 958 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); 959 ASSERT_TRUE(ms.get() != NULL); 960 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 961 kWidth, kHeight, webrtc::kVideoRotation_270, 962 &frame2)); 963 } 964 965 // Test 1 pixel edge case image I420 buffer. 966 void ConstructI4201Pixel() { 967 T frame; 968 uint8_t pixel[3] = {1, 2, 3}; 969 for (int i = 0; i < repeat_; ++i) { 970 EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1, pixel, 971 sizeof(pixel), 1, 1, 0, webrtc::kVideoRotation_0)); 972 } 973 const uint8_t* y = pixel; 974 const uint8_t* u = y + 1; 975 const uint8_t* v = u + 1; 976 EXPECT_TRUE(IsEqual(frame, 1, 1, 1, 1, 0, y, 1, u, 1, v, 1, 0)); 977 } 978 979 // Test 5 pixel edge case image. 980 void ConstructI4205Pixel() { 981 T frame; 982 uint8_t pixels5x5[5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) * 2]; 983 memset(pixels5x5, 1, 5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) * 2); 984 for (int i = 0; i < repeat_; ++i) { 985 EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 5, 5, 5, 5, pixels5x5, 986 sizeof(pixels5x5), 1, 1, 0, 987 webrtc::kVideoRotation_0)); 988 } 989 EXPECT_EQ(5u, frame.GetWidth()); 990 EXPECT_EQ(5u, frame.GetHeight()); 991 EXPECT_EQ(5, frame.GetYPitch()); 992 EXPECT_EQ(3, frame.GetUPitch()); 993 EXPECT_EQ(3, frame.GetVPitch()); 994 } 995 996 // Test 1 pixel edge case image ARGB buffer. 997 void ConstructARGB1Pixel() { 998 T frame; 999 uint8_t pixel[4] = {64, 128, 192, 255}; 1000 for (int i = 0; i < repeat_; ++i) { 1001 EXPECT_TRUE(frame.Init(cricket::FOURCC_ARGB, 1, 1, 1, 1, pixel, 1002 sizeof(pixel), 1, 1, 0, 1003 webrtc::kVideoRotation_0)); 1004 } 1005 // Convert back to ARGB. 1006 size_t out_size = 4; 1007 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]); 1008 uint8_t* out = ALIGNP(outbuf.get(), kAlignment); 1009 1010 EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, 1011 out, 1012 out_size, // buffer size 1013 out_size)); // stride 1014 #ifdef USE_LMI_CONVERT 1015 // TODO(fbarchard): Expected to fail, but not crash. 1016 EXPECT_FALSE(IsPlaneEqual("argb", pixel, 4, out, 4, 3, 1, 2)); 1017 #else 1018 // TODO(fbarchard): Check for overwrite. 1019 EXPECT_TRUE(IsPlaneEqual("argb", pixel, 4, out, 4, 3, 1, 2)); 1020 #endif 1021 } 1022 1023 // Test Black, White and Grey pixels. 1024 void ConstructARGBBlackWhitePixel() { 1025 T frame; 1026 uint8_t pixel[10 * 4] = {0, 0, 0, 255, // Black. 1027 0, 0, 0, 255, // Black. 1028 64, 64, 64, 255, // Dark Grey. 1029 64, 64, 64, 255, // Dark Grey. 1030 128, 128, 128, 255, // Grey. 1031 128, 128, 128, 255, // Grey. 1032 196, 196, 196, 255, // Light Grey. 1033 196, 196, 196, 255, // Light Grey. 1034 255, 255, 255, 255, // White. 1035 255, 255, 255, 255}; // White. 1036 1037 for (int i = 0; i < repeat_; ++i) { 1038 EXPECT_TRUE(frame.Init(cricket::FOURCC_ARGB, 10, 1, 10, 1, pixel, 1039 sizeof(pixel), 1, 1, 0, 1040 webrtc::kVideoRotation_0)); 1041 } 1042 // Convert back to ARGB 1043 size_t out_size = 10 * 4; 1044 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]); 1045 uint8_t* out = ALIGNP(outbuf.get(), kAlignment); 1046 1047 EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, 1048 out, 1049 out_size, // buffer size. 1050 out_size)); // stride. 1051 EXPECT_TRUE(IsPlaneEqual("argb", pixel, out_size, 1052 out, out_size, 1053 out_size, 1, 2)); 1054 } 1055 1056 // Test constructing an image from an I420 buffer with horizontal cropping. 1057 void ConstructI420CropHorizontal() { 1058 T frame1, frame2; 1059 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1060 ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight, 1061 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, 1062 &frame2)); 1063 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0)); 1064 } 1065 1066 // Test constructing an image from a YUY2 buffer with horizontal cropping. 1067 void ConstructYuy2CropHorizontal() { 1068 T frame1, frame2; 1069 rtc::scoped_ptr<rtc::MemoryStream> ms( 1070 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); 1071 ASSERT_TRUE(ms.get() != NULL); 1072 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 1073 &frame1)); 1074 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 1075 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, 1076 &frame2)); 1077 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0)); 1078 } 1079 1080 // Test constructing an image from an ARGB buffer with horizontal cropping. 1081 void ConstructARGBCropHorizontal() { 1082 T frame1, frame2; 1083 rtc::scoped_ptr<rtc::MemoryStream> ms( 1084 CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight)); 1085 ASSERT_TRUE(ms.get() != NULL); 1086 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight, 1087 &frame1)); 1088 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight, 1089 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, 1090 &frame2)); 1091 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 2)); 1092 } 1093 1094 // Test constructing an image from an I420 buffer, cropping top and bottom. 1095 void ConstructI420CropVertical() { 1096 T frame1, frame2; 1097 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1098 ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight, 1099 kWidth, kHeight * 3 / 4, webrtc::kVideoRotation_0, 1100 &frame2)); 1101 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, 0, kHeight / 8, 0)); 1102 } 1103 1104 // Test constructing an image from I420 synonymous formats. 1105 void ConstructI420Aliases() { 1106 T frame1, frame2, frame3; 1107 ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight, 1108 &frame1)); 1109 ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_IYUV, kWidth, kHeight, 1110 &frame2)); 1111 ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_YU12, kWidth, kHeight, 1112 &frame3)); 1113 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1114 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); 1115 } 1116 1117 // Test constructing an image from an I420 MJPG buffer. 1118 void ConstructMjpgI420() { 1119 T frame1, frame2; 1120 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1121 ASSERT_TRUE(LoadFrame(kJpeg420Filename, 1122 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); 1123 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); 1124 } 1125 1126 // Test constructing an image from an I422 MJPG buffer. 1127 void ConstructMjpgI422() { 1128 T frame1, frame2; 1129 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1130 ASSERT_TRUE(LoadFrame(kJpeg422Filename, 1131 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); 1132 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); 1133 } 1134 1135 // Test constructing an image from an I444 MJPG buffer. 1136 void ConstructMjpgI444() { 1137 T frame1, frame2; 1138 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1139 ASSERT_TRUE(LoadFrame(kJpeg444Filename, 1140 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); 1141 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); 1142 } 1143 1144 // Test constructing an image from an I444 MJPG buffer. 1145 void ConstructMjpgI411() { 1146 T frame1, frame2; 1147 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1148 ASSERT_TRUE(LoadFrame(kJpeg411Filename, 1149 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); 1150 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); 1151 } 1152 1153 // Test constructing an image from an I400 MJPG buffer. 1154 // TODO(fbarchard): Stronger compare on chroma. Compare agaisnt a grey image. 1155 void ConstructMjpgI400() { 1156 T frame1, frame2; 1157 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1158 ASSERT_TRUE(LoadFrame(kJpeg400Filename, 1159 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); 1160 EXPECT_TRUE(IsPlaneEqual("y", frame1.GetYPlane(), frame1.GetYPitch(), 1161 frame2.GetYPlane(), frame2.GetYPitch(), 1162 kWidth, kHeight, 32)); 1163 EXPECT_TRUE(IsEqual(frame1, frame2, 128)); 1164 } 1165 1166 // Test constructing an image from an I420 MJPG buffer. 1167 void ValidateFrame(const char* name, 1168 uint32_t fourcc, 1169 int data_adjust, 1170 int size_adjust, 1171 bool expected_result) { 1172 T frame; 1173 rtc::scoped_ptr<rtc::MemoryStream> ms(LoadSample(name)); 1174 ASSERT_TRUE(ms.get() != NULL); 1175 const uint8_t* sample = 1176 reinterpret_cast<const uint8_t*>(ms.get()->GetBuffer()); 1177 size_t sample_size; 1178 ms->GetSize(&sample_size); 1179 // Optional adjust size to test invalid size. 1180 size_t data_size = sample_size + data_adjust; 1181 1182 // Allocate a buffer with end page aligned. 1183 const int kPadToHeapSized = 16 * 1024 * 1024; 1184 rtc::scoped_ptr<uint8_t[]> page_buffer( 1185 new uint8_t[((data_size + kPadToHeapSized + 4095) & ~4095)]); 1186 uint8_t* data_ptr = page_buffer.get(); 1187 if (!data_ptr) { 1188 LOG(LS_WARNING) << "Failed to allocate memory for ValidateFrame test."; 1189 EXPECT_FALSE(expected_result); // NULL is okay if failure was expected. 1190 return; 1191 } 1192 data_ptr += kPadToHeapSized + (-(static_cast<int>(data_size)) & 4095); 1193 memcpy(data_ptr, sample, std::min(data_size, sample_size)); 1194 for (int i = 0; i < repeat_; ++i) { 1195 EXPECT_EQ(expected_result, frame.Validate(fourcc, kWidth, kHeight, 1196 data_ptr, 1197 sample_size + size_adjust)); 1198 } 1199 } 1200 1201 // Test validate for I420 MJPG buffer. 1202 void ValidateMjpgI420() { 1203 ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, 0, 0, true); 1204 } 1205 1206 // Test validate for I422 MJPG buffer. 1207 void ValidateMjpgI422() { 1208 ValidateFrame(kJpeg422Filename, cricket::FOURCC_MJPG, 0, 0, true); 1209 } 1210 1211 // Test validate for I444 MJPG buffer. 1212 void ValidateMjpgI444() { 1213 ValidateFrame(kJpeg444Filename, cricket::FOURCC_MJPG, 0, 0, true); 1214 } 1215 1216 // Test validate for I411 MJPG buffer. 1217 void ValidateMjpgI411() { 1218 ValidateFrame(kJpeg411Filename, cricket::FOURCC_MJPG, 0, 0, true); 1219 } 1220 1221 // Test validate for I400 MJPG buffer. 1222 void ValidateMjpgI400() { 1223 ValidateFrame(kJpeg400Filename, cricket::FOURCC_MJPG, 0, 0, true); 1224 } 1225 1226 // Test validate for I420 buffer. 1227 void ValidateI420() { 1228 ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, 0, true); 1229 } 1230 1231 // Test validate for I420 buffer where size is too small 1232 void ValidateI420SmallSize() { 1233 ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, -16384, false); 1234 } 1235 1236 // Test validate for I420 buffer where size is too large (16 MB) 1237 // Will produce warning but pass. 1238 void ValidateI420LargeSize() { 1239 ValidateFrame(kImageFilename, cricket::FOURCC_I420, 16000000, 16000000, 1240 true); 1241 } 1242 1243 // Test validate for I420 buffer where size is 1 GB (not reasonable). 1244 void ValidateI420HugeSize() { 1245 #ifndef WIN32 // TODO(fbarchard): Reenable when fixing bug 9603762. 1246 ValidateFrame(kImageFilename, cricket::FOURCC_I420, 1000000000u, 1247 1000000000u, false); 1248 #endif 1249 } 1250 1251 // The following test that Validate crashes if the size is greater than the 1252 // actual buffer size. 1253 // TODO(fbarchard): Consider moving a filter into the capturer/plugin. 1254 #if defined(_MSC_VER) && !defined(NDEBUG) 1255 int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { 1256 if (code == EXCEPTION_ACCESS_VIOLATION) { 1257 LOG(LS_INFO) << "Caught EXCEPTION_ACCESS_VIOLATION as expected."; 1258 return EXCEPTION_EXECUTE_HANDLER; 1259 } else { 1260 LOG(LS_INFO) << "Did not catch EXCEPTION_ACCESS_VIOLATION. Unexpected."; 1261 return EXCEPTION_CONTINUE_SEARCH; 1262 } 1263 } 1264 1265 // Test validate fails for truncated MJPG data buffer. If ValidateFrame 1266 // crashes the exception handler will return and unittest passes with OK. 1267 void ValidateMjpgI420InvalidSize() { 1268 __try { 1269 ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, -16384, 0, false); 1270 FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION."; 1271 } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { 1272 return; // Successfully crashed in ValidateFrame. 1273 } 1274 } 1275 1276 // Test validate fails for truncated I420 buffer. 1277 void ValidateI420InvalidSize() { 1278 __try { 1279 ValidateFrame(kImageFilename, cricket::FOURCC_I420, -16384, 0, false); 1280 FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION."; 1281 } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { 1282 return; // Successfully crashed in ValidateFrame. 1283 } 1284 } 1285 #endif 1286 1287 // Test constructing an image from a YUY2 buffer (and synonymous formats). 1288 void ConstructYuy2Aliases() { 1289 T frame1, frame2, frame3, frame4; 1290 rtc::scoped_ptr<rtc::MemoryStream> ms( 1291 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); 1292 ASSERT_TRUE(ms.get() != NULL); 1293 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, 1294 &frame1)); 1295 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, 1296 kWidth, kHeight, &frame2)); 1297 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUVS, 1298 kWidth, kHeight, &frame3)); 1299 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUYV, 1300 kWidth, kHeight, &frame4)); 1301 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1302 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); 1303 EXPECT_TRUE(IsEqual(frame1, frame4, 0)); 1304 } 1305 1306 // Test constructing an image from a UYVY buffer (and synonymous formats). 1307 void ConstructUyvyAliases() { 1308 T frame1, frame2, frame3, frame4; 1309 rtc::scoped_ptr<rtc::MemoryStream> ms( 1310 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); 1311 ASSERT_TRUE(ms.get() != NULL); 1312 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, 1313 &frame1)); 1314 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, 1315 kWidth, kHeight, &frame2)); 1316 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_2VUY, 1317 kWidth, kHeight, &frame3)); 1318 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_HDYC, 1319 kWidth, kHeight, &frame4)); 1320 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1321 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); 1322 EXPECT_TRUE(IsEqual(frame1, frame4, 0)); 1323 } 1324 1325 // Test creating a copy. 1326 void ConstructCopy() { 1327 T frame1, frame2; 1328 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1329 for (int i = 0; i < repeat_; ++i) { 1330 EXPECT_TRUE(frame2.Init(frame1)); 1331 } 1332 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1333 } 1334 1335 // Test creating a copy and check that it just increments the refcount. 1336 void ConstructCopyIsRef() { 1337 T frame1, frame2; 1338 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1339 for (int i = 0; i < repeat_; ++i) { 1340 EXPECT_TRUE(frame2.Init(frame1)); 1341 } 1342 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1343 EXPECT_EQ(frame1.GetYPlane(), frame2.GetYPlane()); 1344 EXPECT_EQ(frame1.GetUPlane(), frame2.GetUPlane()); 1345 EXPECT_EQ(frame1.GetVPlane(), frame2.GetVPlane()); 1346 } 1347 1348 // Test creating an empty image and initing it to black. 1349 void ConstructBlack() { 1350 T frame; 1351 for (int i = 0; i < repeat_; ++i) { 1352 EXPECT_TRUE(frame.InitToBlack(kWidth, kHeight, 1, 1, 0)); 1353 } 1354 EXPECT_TRUE(IsSize(frame, kWidth, kHeight)); 1355 EXPECT_TRUE(IsBlack(frame)); 1356 } 1357 1358 // Test constructing an image from a YUY2 buffer with a range of sizes. 1359 // Only tests that conversion does not crash or corrupt heap. 1360 void ConstructYuy2AllSizes() { 1361 T frame1, frame2; 1362 for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) { 1363 for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) { 1364 rtc::scoped_ptr<rtc::MemoryStream> ms( 1365 CreateYuv422Sample(cricket::FOURCC_YUY2, width, height)); 1366 ASSERT_TRUE(ms.get() != NULL); 1367 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, width, height, 1368 &frame1)); 1369 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, 1370 width, height, &frame2)); 1371 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1372 } 1373 } 1374 } 1375 1376 // Test constructing an image from a ARGB buffer with a range of sizes. 1377 // Only tests that conversion does not crash or corrupt heap. 1378 void ConstructARGBAllSizes() { 1379 T frame1, frame2; 1380 for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) { 1381 for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) { 1382 rtc::scoped_ptr<rtc::MemoryStream> ms( 1383 CreateRgbSample(cricket::FOURCC_ARGB, width, height)); 1384 ASSERT_TRUE(ms.get() != NULL); 1385 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, width, height, 1386 &frame1)); 1387 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, 1388 width, height, &frame2)); 1389 EXPECT_TRUE(IsEqual(frame1, frame2, 64)); 1390 } 1391 } 1392 // Test a practical window size for screencasting usecase. 1393 const int kOddWidth = 1228; 1394 const int kOddHeight = 260; 1395 for (int j = 0; j < 2; ++j) { 1396 for (int i = 0; i < 2; ++i) { 1397 rtc::scoped_ptr<rtc::MemoryStream> ms( 1398 CreateRgbSample(cricket::FOURCC_ARGB, kOddWidth + i, kOddHeight + j)); 1399 ASSERT_TRUE(ms.get() != NULL); 1400 EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, 1401 kOddWidth + i, kOddHeight + j, 1402 &frame1)); 1403 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, 1404 kOddWidth + i, kOddHeight + j, &frame2)); 1405 EXPECT_TRUE(IsEqual(frame1, frame2, 64)); 1406 } 1407 } 1408 } 1409 1410 // Tests re-initing an existing image. 1411 void Reset(webrtc::VideoRotation rotation, bool apply_rotation) { 1412 T frame1, frame2; 1413 rtc::scoped_ptr<rtc::MemoryStream> ms( 1414 LoadSample(kImageFilename)); 1415 ASSERT_TRUE(ms.get() != NULL); 1416 size_t data_size; 1417 ms->GetSize(&data_size); 1418 EXPECT_TRUE(frame1.InitToBlack(kWidth, kHeight, 1, 1, 0)); 1419 EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0)); 1420 EXPECT_TRUE(IsBlack(frame1)); 1421 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); 1422 EXPECT_TRUE(frame1.Reset(cricket::FOURCC_I420, kWidth, kHeight, kWidth, 1423 kHeight, 1424 reinterpret_cast<uint8_t*>(ms->GetBuffer()), 1425 data_size, 1, 1, 0, rotation, apply_rotation)); 1426 if (apply_rotation) 1427 EXPECT_EQ(webrtc::kVideoRotation_0, frame1.GetVideoRotation()); 1428 else 1429 EXPECT_EQ(rotation, frame1.GetVideoRotation()); 1430 1431 // Swapp width and height if the frame is rotated 90 or 270 degrees. 1432 if (apply_rotation && (rotation == webrtc::kVideoRotation_90 1433 || rotation == webrtc::kVideoRotation_270)) { 1434 EXPECT_TRUE(kHeight == frame1.GetWidth()); 1435 EXPECT_TRUE(kWidth == frame1.GetHeight()); 1436 } else { 1437 EXPECT_TRUE(kWidth == frame1.GetWidth()); 1438 EXPECT_TRUE(kHeight == frame1.GetHeight()); 1439 } 1440 EXPECT_FALSE(IsBlack(frame1)); 1441 EXPECT_FALSE(IsEqual(frame1, frame2, 0)); 1442 } 1443 1444 void ResetAndApplyRotation() { 1445 Reset(webrtc::kVideoRotation_90, true); 1446 } 1447 1448 void ResetAndDontApplyRotation() { 1449 Reset(webrtc::kVideoRotation_90, false); 1450 } 1451 1452 ////////////////////// 1453 // Conversion tests // 1454 ////////////////////// 1455 1456 enum ToFrom { TO, FROM }; 1457 1458 // Helper function for test converting from I420 to packed formats. 1459 inline void ConvertToBuffer(int bpp, 1460 int rowpad, 1461 bool invert, 1462 ToFrom to_from, 1463 int error, 1464 uint32_t fourcc, 1465 int (*RGBToI420)(const uint8_t* src_frame, 1466 int src_stride_frame, 1467 uint8_t* dst_y, 1468 int dst_stride_y, 1469 uint8_t* dst_u, 1470 int dst_stride_u, 1471 uint8_t* dst_v, 1472 int dst_stride_v, 1473 int width, 1474 int height)) { 1475 T frame1, frame2; 1476 int repeat_to = (to_from == TO) ? repeat_ : 1; 1477 int repeat_from = (to_from == FROM) ? repeat_ : 1; 1478 1479 int astride = kWidth * bpp + rowpad; 1480 size_t out_size = astride * kHeight; 1481 rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment + 1]); 1482 memset(outbuf.get(), 0, out_size + kAlignment + 1); 1483 uint8_t* outtop = ALIGNP(outbuf.get(), kAlignment); 1484 uint8_t* out = outtop; 1485 int stride = astride; 1486 if (invert) { 1487 out += (kHeight - 1) * stride; // Point to last row. 1488 stride = -stride; 1489 } 1490 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1491 1492 for (int i = 0; i < repeat_to; ++i) { 1493 EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(fourcc, 1494 out, 1495 out_size, stride)); 1496 } 1497 EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0)); 1498 for (int i = 0; i < repeat_from; ++i) { 1499 EXPECT_EQ(0, RGBToI420(out, stride, 1500 frame2.GetYPlane(), frame2.GetYPitch(), 1501 frame2.GetUPlane(), frame2.GetUPitch(), 1502 frame2.GetVPlane(), frame2.GetVPitch(), 1503 kWidth, kHeight)); 1504 } 1505 if (rowpad) { 1506 EXPECT_EQ(0, outtop[kWidth * bpp]); // Ensure stride skipped end of row. 1507 EXPECT_NE(0, outtop[astride]); // Ensure pixel at start of 2nd row. 1508 } else { 1509 EXPECT_NE(0, outtop[kWidth * bpp]); // Expect something to be here. 1510 } 1511 EXPECT_EQ(0, outtop[out_size]); // Ensure no overrun. 1512 EXPECT_TRUE(IsEqual(frame1, frame2, error)); 1513 } 1514 1515 static const int kError = 20; 1516 static const int kErrorHigh = 40; 1517 static const int kOddStride = 23; 1518 1519 // Tests ConvertToRGBBuffer formats. 1520 void ConvertToARGBBuffer() { 1521 ConvertToBuffer(4, 0, false, TO, kError, 1522 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1523 } 1524 void ConvertToBGRABuffer() { 1525 ConvertToBuffer(4, 0, false, TO, kError, 1526 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1527 } 1528 void ConvertToABGRBuffer() { 1529 ConvertToBuffer(4, 0, false, TO, kError, 1530 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1531 } 1532 void ConvertToRGB24Buffer() { 1533 ConvertToBuffer(3, 0, false, TO, kError, 1534 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1535 } 1536 void ConvertToRAWBuffer() { 1537 ConvertToBuffer(3, 0, false, TO, kError, 1538 cricket::FOURCC_RAW, libyuv::RAWToI420); 1539 } 1540 void ConvertToRGB565Buffer() { 1541 ConvertToBuffer(2, 0, false, TO, kError, 1542 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1543 } 1544 void ConvertToARGB1555Buffer() { 1545 ConvertToBuffer(2, 0, false, TO, kError, 1546 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1547 } 1548 void ConvertToARGB4444Buffer() { 1549 ConvertToBuffer(2, 0, false, TO, kError, 1550 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1551 } 1552 void ConvertToI400Buffer() { 1553 ConvertToBuffer(1, 0, false, TO, 128, 1554 cricket::FOURCC_I400, libyuv::I400ToI420); 1555 } 1556 void ConvertToYUY2Buffer() { 1557 ConvertToBuffer(2, 0, false, TO, kError, 1558 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1559 } 1560 void ConvertToUYVYBuffer() { 1561 ConvertToBuffer(2, 0, false, TO, kError, 1562 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1563 } 1564 1565 // Tests ConvertToRGBBuffer formats with odd stride. 1566 void ConvertToARGBBufferStride() { 1567 ConvertToBuffer(4, kOddStride, false, TO, kError, 1568 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1569 } 1570 void ConvertToBGRABufferStride() { 1571 ConvertToBuffer(4, kOddStride, false, TO, kError, 1572 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1573 } 1574 void ConvertToABGRBufferStride() { 1575 ConvertToBuffer(4, kOddStride, false, TO, kError, 1576 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1577 } 1578 void ConvertToRGB24BufferStride() { 1579 ConvertToBuffer(3, kOddStride, false, TO, kError, 1580 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1581 } 1582 void ConvertToRAWBufferStride() { 1583 ConvertToBuffer(3, kOddStride, false, TO, kError, 1584 cricket::FOURCC_RAW, libyuv::RAWToI420); 1585 } 1586 void ConvertToRGB565BufferStride() { 1587 ConvertToBuffer(2, kOddStride, false, TO, kError, 1588 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1589 } 1590 void ConvertToARGB1555BufferStride() { 1591 ConvertToBuffer(2, kOddStride, false, TO, kError, 1592 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1593 } 1594 void ConvertToARGB4444BufferStride() { 1595 ConvertToBuffer(2, kOddStride, false, TO, kError, 1596 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1597 } 1598 void ConvertToI400BufferStride() { 1599 ConvertToBuffer(1, kOddStride, false, TO, 128, 1600 cricket::FOURCC_I400, libyuv::I400ToI420); 1601 } 1602 void ConvertToYUY2BufferStride() { 1603 ConvertToBuffer(2, kOddStride, false, TO, kError, 1604 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1605 } 1606 void ConvertToUYVYBufferStride() { 1607 ConvertToBuffer(2, kOddStride, false, TO, kError, 1608 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1609 } 1610 1611 // Tests ConvertToRGBBuffer formats with negative stride to invert image. 1612 void ConvertToARGBBufferInverted() { 1613 ConvertToBuffer(4, 0, true, TO, kError, 1614 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1615 } 1616 void ConvertToBGRABufferInverted() { 1617 ConvertToBuffer(4, 0, true, TO, kError, 1618 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1619 } 1620 void ConvertToABGRBufferInverted() { 1621 ConvertToBuffer(4, 0, true, TO, kError, 1622 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1623 } 1624 void ConvertToRGB24BufferInverted() { 1625 ConvertToBuffer(3, 0, true, TO, kError, 1626 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1627 } 1628 void ConvertToRAWBufferInverted() { 1629 ConvertToBuffer(3, 0, true, TO, kError, 1630 cricket::FOURCC_RAW, libyuv::RAWToI420); 1631 } 1632 void ConvertToRGB565BufferInverted() { 1633 ConvertToBuffer(2, 0, true, TO, kError, 1634 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1635 } 1636 void ConvertToARGB1555BufferInverted() { 1637 ConvertToBuffer(2, 0, true, TO, kError, 1638 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1639 } 1640 void ConvertToARGB4444BufferInverted() { 1641 ConvertToBuffer(2, 0, true, TO, kError, 1642 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1643 } 1644 void ConvertToI400BufferInverted() { 1645 ConvertToBuffer(1, 0, true, TO, 128, 1646 cricket::FOURCC_I400, libyuv::I400ToI420); 1647 } 1648 void ConvertToYUY2BufferInverted() { 1649 ConvertToBuffer(2, 0, true, TO, kError, 1650 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1651 } 1652 void ConvertToUYVYBufferInverted() { 1653 ConvertToBuffer(2, 0, true, TO, kError, 1654 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1655 } 1656 1657 // Tests ConvertFrom formats. 1658 void ConvertFromARGBBuffer() { 1659 ConvertToBuffer(4, 0, false, FROM, kError, 1660 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1661 } 1662 void ConvertFromBGRABuffer() { 1663 ConvertToBuffer(4, 0, false, FROM, kError, 1664 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1665 } 1666 void ConvertFromABGRBuffer() { 1667 ConvertToBuffer(4, 0, false, FROM, kError, 1668 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1669 } 1670 void ConvertFromRGB24Buffer() { 1671 ConvertToBuffer(3, 0, false, FROM, kError, 1672 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1673 } 1674 void ConvertFromRAWBuffer() { 1675 ConvertToBuffer(3, 0, false, FROM, kError, 1676 cricket::FOURCC_RAW, libyuv::RAWToI420); 1677 } 1678 void ConvertFromRGB565Buffer() { 1679 ConvertToBuffer(2, 0, false, FROM, kError, 1680 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1681 } 1682 void ConvertFromARGB1555Buffer() { 1683 ConvertToBuffer(2, 0, false, FROM, kError, 1684 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1685 } 1686 void ConvertFromARGB4444Buffer() { 1687 ConvertToBuffer(2, 0, false, FROM, kError, 1688 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1689 } 1690 void ConvertFromI400Buffer() { 1691 ConvertToBuffer(1, 0, false, FROM, 128, 1692 cricket::FOURCC_I400, libyuv::I400ToI420); 1693 } 1694 void ConvertFromYUY2Buffer() { 1695 ConvertToBuffer(2, 0, false, FROM, kError, 1696 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1697 } 1698 void ConvertFromUYVYBuffer() { 1699 ConvertToBuffer(2, 0, false, FROM, kError, 1700 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1701 } 1702 1703 // Tests ConvertFrom formats with odd stride. 1704 void ConvertFromARGBBufferStride() { 1705 ConvertToBuffer(4, kOddStride, false, FROM, kError, 1706 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1707 } 1708 void ConvertFromBGRABufferStride() { 1709 ConvertToBuffer(4, kOddStride, false, FROM, kError, 1710 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1711 } 1712 void ConvertFromABGRBufferStride() { 1713 ConvertToBuffer(4, kOddStride, false, FROM, kError, 1714 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1715 } 1716 void ConvertFromRGB24BufferStride() { 1717 ConvertToBuffer(3, kOddStride, false, FROM, kError, 1718 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1719 } 1720 void ConvertFromRAWBufferStride() { 1721 ConvertToBuffer(3, kOddStride, false, FROM, kError, 1722 cricket::FOURCC_RAW, libyuv::RAWToI420); 1723 } 1724 void ConvertFromRGB565BufferStride() { 1725 ConvertToBuffer(2, kOddStride, false, FROM, kError, 1726 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1727 } 1728 void ConvertFromARGB1555BufferStride() { 1729 ConvertToBuffer(2, kOddStride, false, FROM, kError, 1730 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1731 } 1732 void ConvertFromARGB4444BufferStride() { 1733 ConvertToBuffer(2, kOddStride, false, FROM, kError, 1734 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1735 } 1736 void ConvertFromI400BufferStride() { 1737 ConvertToBuffer(1, kOddStride, false, FROM, 128, 1738 cricket::FOURCC_I400, libyuv::I400ToI420); 1739 } 1740 void ConvertFromYUY2BufferStride() { 1741 ConvertToBuffer(2, kOddStride, false, FROM, kError, 1742 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1743 } 1744 void ConvertFromUYVYBufferStride() { 1745 ConvertToBuffer(2, kOddStride, false, FROM, kError, 1746 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1747 } 1748 1749 // Tests ConvertFrom formats with negative stride to invert image. 1750 void ConvertFromARGBBufferInverted() { 1751 ConvertToBuffer(4, 0, true, FROM, kError, 1752 cricket::FOURCC_ARGB, libyuv::ARGBToI420); 1753 } 1754 void ConvertFromBGRABufferInverted() { 1755 ConvertToBuffer(4, 0, true, FROM, kError, 1756 cricket::FOURCC_BGRA, libyuv::BGRAToI420); 1757 } 1758 void ConvertFromABGRBufferInverted() { 1759 ConvertToBuffer(4, 0, true, FROM, kError, 1760 cricket::FOURCC_ABGR, libyuv::ABGRToI420); 1761 } 1762 void ConvertFromRGB24BufferInverted() { 1763 ConvertToBuffer(3, 0, true, FROM, kError, 1764 cricket::FOURCC_24BG, libyuv::RGB24ToI420); 1765 } 1766 void ConvertFromRAWBufferInverted() { 1767 ConvertToBuffer(3, 0, true, FROM, kError, 1768 cricket::FOURCC_RAW, libyuv::RAWToI420); 1769 } 1770 void ConvertFromRGB565BufferInverted() { 1771 ConvertToBuffer(2, 0, true, FROM, kError, 1772 cricket::FOURCC_RGBP, libyuv::RGB565ToI420); 1773 } 1774 void ConvertFromARGB1555BufferInverted() { 1775 ConvertToBuffer(2, 0, true, FROM, kError, 1776 cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420); 1777 } 1778 void ConvertFromARGB4444BufferInverted() { 1779 ConvertToBuffer(2, 0, true, FROM, kError, 1780 cricket::FOURCC_R444, libyuv::ARGB4444ToI420); 1781 } 1782 void ConvertFromI400BufferInverted() { 1783 ConvertToBuffer(1, 0, true, FROM, 128, 1784 cricket::FOURCC_I400, libyuv::I400ToI420); 1785 } 1786 void ConvertFromYUY2BufferInverted() { 1787 ConvertToBuffer(2, 0, true, FROM, kError, 1788 cricket::FOURCC_YUY2, libyuv::YUY2ToI420); 1789 } 1790 void ConvertFromUYVYBufferInverted() { 1791 ConvertToBuffer(2, 0, true, FROM, kError, 1792 cricket::FOURCC_UYVY, libyuv::UYVYToI420); 1793 } 1794 1795 // Test converting from I420 to I422. 1796 void ConvertToI422Buffer() { 1797 T frame1, frame2; 1798 size_t out_size = kWidth * kHeight * 2; 1799 rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[out_size + kAlignment]); 1800 uint8_t* y = ALIGNP(buf.get(), kAlignment); 1801 uint8_t* u = y + kWidth * kHeight; 1802 uint8_t* v = u + (kWidth / 2) * kHeight; 1803 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); 1804 for (int i = 0; i < repeat_; ++i) { 1805 EXPECT_EQ(0, libyuv::I420ToI422(frame1.GetYPlane(), frame1.GetYPitch(), 1806 frame1.GetUPlane(), frame1.GetUPitch(), 1807 frame1.GetVPlane(), frame1.GetVPitch(), 1808 y, kWidth, 1809 u, kWidth / 2, 1810 v, kWidth / 2, 1811 kWidth, kHeight)); 1812 } 1813 EXPECT_TRUE(frame2.Init(cricket::FOURCC_I422, kWidth, kHeight, kWidth, 1814 kHeight, y, out_size, 1, 1, 0, 1815 webrtc::kVideoRotation_0)); 1816 EXPECT_TRUE(IsEqual(frame1, frame2, 1)); 1817 } 1818 1819 /////////////////// 1820 // General tests // 1821 /////////////////// 1822 1823 void Copy() { 1824 rtc::scoped_ptr<T> source(new T); 1825 rtc::scoped_ptr<cricket::VideoFrame> target; 1826 ASSERT_TRUE(LoadFrameNoRepeat(source.get())); 1827 target.reset(source->Copy()); 1828 EXPECT_TRUE(IsEqual(*source, *target, 0)); 1829 source.reset(); 1830 EXPECT_TRUE(target->GetYPlane() != NULL); 1831 } 1832 1833 void CopyIsRef() { 1834 rtc::scoped_ptr<T> source(new T); 1835 rtc::scoped_ptr<const cricket::VideoFrame> target; 1836 ASSERT_TRUE(LoadFrameNoRepeat(source.get())); 1837 target.reset(source->Copy()); 1838 EXPECT_TRUE(IsEqual(*source, *target, 0)); 1839 const T* const_source = source.get(); 1840 EXPECT_EQ(const_source->GetYPlane(), target->GetYPlane()); 1841 EXPECT_EQ(const_source->GetUPlane(), target->GetUPlane()); 1842 EXPECT_EQ(const_source->GetVPlane(), target->GetVPlane()); 1843 } 1844 1845 void MakeExclusive() { 1846 rtc::scoped_ptr<T> source(new T); 1847 rtc::scoped_ptr<cricket::VideoFrame> target; 1848 ASSERT_TRUE(LoadFrameNoRepeat(source.get())); 1849 target.reset(source->Copy()); 1850 EXPECT_TRUE(target->MakeExclusive()); 1851 EXPECT_TRUE(IsEqual(*source, *target, 0)); 1852 EXPECT_NE(target->GetYPlane(), source->GetYPlane()); 1853 EXPECT_NE(target->GetUPlane(), source->GetUPlane()); 1854 EXPECT_NE(target->GetVPlane(), source->GetVPlane()); 1855 } 1856 1857 void CopyToBuffer() { 1858 T frame; 1859 rtc::scoped_ptr<rtc::MemoryStream> ms( 1860 LoadSample(kImageFilename)); 1861 ASSERT_TRUE(ms.get() != NULL); 1862 ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, 1863 &frame)); 1864 size_t out_size = kWidth * kHeight * 3 / 2; 1865 rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size]); 1866 for (int i = 0; i < repeat_; ++i) { 1867 EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size)); 1868 } 1869 EXPECT_EQ(0, memcmp(out.get(), ms->GetBuffer(), out_size)); 1870 } 1871 1872 void CopyToFrame() { 1873 T source; 1874 rtc::scoped_ptr<rtc::MemoryStream> ms( 1875 LoadSample(kImageFilename)); 1876 ASSERT_TRUE(ms.get() != NULL); 1877 ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, 1878 &source)); 1879 1880 // Create the target frame by loading from a file. 1881 T target; 1882 ASSERT_TRUE(LoadFrameNoRepeat(&target)); 1883 EXPECT_FALSE(IsBlack(target)); 1884 1885 // Stretch and check if the stretched target is black. 1886 source.CopyToFrame(&target); 1887 1888 EXPECT_TRUE(IsEqual(source, target, 0)); 1889 } 1890 1891 void Write() { 1892 T frame; 1893 rtc::scoped_ptr<rtc::MemoryStream> ms( 1894 LoadSample(kImageFilename)); 1895 ASSERT_TRUE(ms.get() != NULL); 1896 rtc::MemoryStream ms2; 1897 size_t size; 1898 ASSERT_TRUE(ms->GetSize(&size)); 1899 ASSERT_TRUE(ms2.ReserveSize(size)); 1900 ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, 1901 &frame)); 1902 for (int i = 0; i < repeat_; ++i) { 1903 ms2.SetPosition(0u); // Useful when repeat_ > 1. 1904 int error; 1905 EXPECT_EQ(rtc::SR_SUCCESS, frame.Write(&ms2, &error)); 1906 } 1907 size_t out_size = cricket::VideoFrame::SizeOf(kWidth, kHeight); 1908 EXPECT_EQ(0, memcmp(ms2.GetBuffer(), ms->GetBuffer(), out_size)); 1909 } 1910 1911 void CopyToBuffer1Pixel() { 1912 size_t out_size = 3; 1913 rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size + 1]); 1914 memset(out.get(), 0xfb, out_size + 1); // Fill buffer 1915 uint8_t pixel[3] = {1, 2, 3}; 1916 T frame; 1917 EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1, pixel, 1918 sizeof(pixel), 1, 1, 0, 1919 webrtc::kVideoRotation_0)); 1920 for (int i = 0; i < repeat_; ++i) { 1921 EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size)); 1922 } 1923 EXPECT_EQ(1, out.get()[0]); // Check Y. Should be 1. 1924 EXPECT_EQ(2, out.get()[1]); // Check U. Should be 2. 1925 EXPECT_EQ(3, out.get()[2]); // Check V. Should be 3. 1926 EXPECT_EQ(0xfb, out.get()[3]); // Check sentinel is still intact. 1927 } 1928 1929 void StretchToFrame() { 1930 // Create the source frame as a black frame. 1931 T source; 1932 EXPECT_TRUE(source.InitToBlack(kWidth * 2, kHeight * 2, 1, 1, 0)); 1933 EXPECT_TRUE(IsSize(source, kWidth * 2, kHeight * 2)); 1934 1935 // Create the target frame by loading from a file. 1936 T target1; 1937 ASSERT_TRUE(LoadFrameNoRepeat(&target1)); 1938 EXPECT_FALSE(IsBlack(target1)); 1939 1940 // Stretch and check if the stretched target is black. 1941 source.StretchToFrame(&target1, true, false); 1942 EXPECT_TRUE(IsBlack(target1)); 1943 1944 // Crop and stretch and check if the stretched target is black. 1945 T target2; 1946 ASSERT_TRUE(LoadFrameNoRepeat(&target2)); 1947 source.StretchToFrame(&target2, true, true); 1948 EXPECT_TRUE(IsBlack(target2)); 1949 EXPECT_EQ(source.GetTimeStamp(), target2.GetTimeStamp()); 1950 } 1951 1952 int repeat_; 1953 }; 1954 1955 #endif // TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ 1956