video_frame_unittest.cc (24339B)
1 /* 2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "api/video/video_frame.h" 12 13 #include <algorithm> 14 #include <cstdint> 15 #include <cstring> 16 #include <utility> 17 #include <vector> 18 19 #include "api/scoped_refptr.h" 20 #include "api/video/i010_buffer.h" 21 #include "api/video/i210_buffer.h" 22 #include "api/video/i410_buffer.h" 23 #include "api/video/i420_buffer.h" 24 #include "api/video/i422_buffer.h" 25 #include "api/video/i444_buffer.h" 26 #include "api/video/nv12_buffer.h" 27 #include "api/video/video_frame_buffer.h" 28 #include "api/video/video_rotation.h" 29 #include "test/fake_texture_frame.h" 30 #include "test/frame_utils.h" 31 #include "test/gtest.h" 32 33 namespace webrtc { 34 35 namespace { 36 37 struct SubSampling { 38 int x; 39 int y; 40 }; 41 42 SubSampling SubSamplingForType(VideoFrameBuffer::Type type) { 43 switch (type) { 44 case VideoFrameBuffer::Type::kI420: 45 return {.x = 2, .y = 2}; 46 case VideoFrameBuffer::Type::kI420A: 47 return {.x = 2, .y = 2}; 48 case VideoFrameBuffer::Type::kI422: 49 return {.x = 2, .y = 1}; 50 case VideoFrameBuffer::Type::kI444: 51 return {.x = 1, .y = 1}; 52 case VideoFrameBuffer::Type::kI010: 53 return {.x = 2, .y = 2}; 54 case VideoFrameBuffer::Type::kI210: 55 return {.x = 2, .y = 1}; 56 case VideoFrameBuffer::Type::kI410: 57 return {.x = 1, .y = 1}; 58 default: 59 return {}; 60 } 61 } 62 63 // Helper function to create a buffer and fill it with a gradient for 64 // PlanarYuvBuffer based buffers. 65 template <class T> 66 scoped_refptr<T> CreateGradient(int width, int height) { 67 scoped_refptr<T> buffer(T::Create(width, height)); 68 // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h 69 for (int x = 0; x < width; x++) { 70 for (int y = 0; y < height; y++) { 71 buffer->MutableDataY()[x + y * width] = 72 128 * (x * height + y * width) / (width * height); 73 } 74 } 75 int chroma_width = buffer->ChromaWidth(); 76 int chroma_height = buffer->ChromaHeight(); 77 for (int x = 0; x < chroma_width; x++) { 78 for (int y = 0; y < chroma_height; y++) { 79 buffer->MutableDataU()[x + y * chroma_width] = 80 255 * x / (chroma_width - 1); 81 buffer->MutableDataV()[x + y * chroma_width] = 82 255 * y / (chroma_height - 1); 83 } 84 } 85 return buffer; 86 } 87 88 // Helper function to create a buffer and fill it with a gradient. 89 scoped_refptr<NV12BufferInterface> CreateNV12Gradient(int width, int height) { 90 scoped_refptr<NV12Buffer> buffer(NV12Buffer::Create(width, height)); 91 // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h 92 for (int x = 0; x < width; x++) { 93 for (int y = 0; y < height; y++) { 94 buffer->MutableDataY()[x + y * width] = 95 128 * (x * height + y * width) / (width * height); 96 } 97 } 98 int chroma_width = buffer->ChromaWidth(); 99 int chroma_height = buffer->ChromaHeight(); 100 for (int x = 0; x < chroma_width; x++) { 101 for (int y = 0; y < chroma_height; y++) { 102 buffer->MutableDataUV()[x * 2 + y * buffer->StrideUV()] = 103 255 * x / (chroma_width - 1); 104 buffer->MutableDataUV()[x * 2 + 1 + y * buffer->StrideUV()] = 105 255 * y / (chroma_height - 1); 106 } 107 } 108 return buffer; 109 } 110 111 // The offsets and sizes describe the rectangle extracted from the 112 // original (gradient) frame, in relative coordinates where the 113 // original frame correspond to the unit square, 0.0 <= x, y < 1.0. 114 template <class T> 115 void CheckCrop(const T& frame, 116 double offset_x, 117 double offset_y, 118 double rel_width, 119 double rel_height) { 120 int width = frame.width(); 121 int height = frame.height(); 122 123 SubSampling plane_divider = SubSamplingForType(frame.type()); 124 125 // Check that pixel values in the corners match the gradient used 126 // for initialization. 127 for (int i = 0; i < 2; i++) { 128 for (int j = 0; j < 2; j++) { 129 // Pixel coordinates of the corner. 130 int x = i * (width - 1); 131 int y = j * (height - 1); 132 // Relative coordinates, range 0.0 - 1.0 correspond to the 133 // size of the uncropped input frame. 134 double orig_x = offset_x + i * rel_width; 135 double orig_y = offset_y + j * rel_height; 136 137 EXPECT_NEAR(frame.DataY()[x + y * frame.StrideY()] / 256.0, 138 (orig_x + orig_y) / 2, 0.02); 139 EXPECT_NEAR(frame.DataU()[x / plane_divider.x + 140 (y / plane_divider.y) * frame.StrideU()] / 141 256.0, 142 orig_x, 0.02); 143 EXPECT_NEAR(frame.DataV()[x / plane_divider.x + 144 (y / plane_divider.y) * frame.StrideV()] / 145 256.0, 146 orig_y, 0.02); 147 } 148 } 149 } 150 151 template <class T> 152 void CheckRotate(int width, 153 int height, 154 VideoRotation rotation, 155 const T& rotated) { 156 int rotated_width = width; 157 int rotated_height = height; 158 159 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { 160 std::swap(rotated_width, rotated_height); 161 } 162 EXPECT_EQ(rotated_width, rotated.width()); 163 EXPECT_EQ(rotated_height, rotated.height()); 164 165 // Clock-wise order (with 0,0 at top-left) 166 const struct { 167 int x; 168 int y; 169 } corners[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; 170 // Corresponding corner colors of the frame produced by CreateGradient. 171 const struct { 172 int y; 173 int u; 174 int v; 175 } colors[] = {{0, 0, 0}, {127, 255, 0}, {255, 255, 255}, {127, 0, 255}}; 176 int corner_offset = static_cast<int>(rotation) / 90; 177 178 SubSampling plane_divider = SubSamplingForType(rotated.type()); 179 180 for (int i = 0; i < 4; i++) { 181 int j = (i + corner_offset) % 4; 182 int x = corners[j].x * (rotated_width - 1); 183 int y = corners[j].y * (rotated_height - 1); 184 EXPECT_EQ(colors[i].y, rotated.DataY()[x + y * rotated.StrideY()]); 185 if (rotated.type() == VideoFrameBuffer::Type::kI422 || 186 rotated.type() == VideoFrameBuffer::Type::kI210) { 187 EXPECT_NEAR(colors[i].u, 188 rotated.DataU()[(x / plane_divider.x) + 189 (y / plane_divider.y) * rotated.StrideU()], 190 1); 191 EXPECT_NEAR(colors[i].v, 192 rotated.DataV()[(x / plane_divider.x) + 193 (y / plane_divider.y) * rotated.StrideV()], 194 1); 195 } else { 196 EXPECT_EQ(colors[i].u, 197 rotated.DataU()[(x / plane_divider.x) + 198 (y / plane_divider.y) * rotated.StrideU()]); 199 EXPECT_EQ(colors[i].v, 200 rotated.DataV()[(x / plane_divider.x) + 201 (y / plane_divider.y) * rotated.StrideV()]); 202 } 203 } 204 } 205 206 } // namespace 207 208 TEST(TestVideoFrame, WidthHeightValues) { 209 VideoFrame frame = 210 VideoFrame::Builder() 211 .set_video_frame_buffer(I420Buffer::Create(10, 10, 10, 14, 90)) 212 .set_rotation(kVideoRotation_0) 213 .set_timestamp_ms(789) 214 .build(); 215 const int valid_value = 10; 216 EXPECT_EQ(valid_value, frame.width()); 217 EXPECT_EQ(valid_value, frame.height()); 218 frame.set_rtp_timestamp(123u); 219 EXPECT_EQ(123u, frame.rtp_timestamp()); 220 frame.set_ntp_time_ms(456); 221 EXPECT_EQ(456, frame.ntp_time_ms()); 222 EXPECT_EQ(789, frame.render_time_ms()); 223 } 224 225 TEST(TestVideoFrame, ShallowCopy) { 226 uint32_t timestamp = 1; 227 int64_t ntp_time_ms = 2; 228 int64_t timestamp_us = 3; 229 int stride_y = 15; 230 int stride_u = 10; 231 int stride_v = 10; 232 int width = 15; 233 int height = 15; 234 235 const int kSizeY = 400; 236 const int kSizeU = 100; 237 const int kSizeV = 100; 238 const VideoRotation kRotation = kVideoRotation_270; 239 uint8_t buffer_y[kSizeY]; 240 uint8_t buffer_u[kSizeU]; 241 uint8_t buffer_v[kSizeV]; 242 memset(buffer_y, 16, kSizeY); 243 memset(buffer_u, 8, kSizeU); 244 memset(buffer_v, 4, kSizeV); 245 246 VideoFrame frame1 = VideoFrame::Builder() 247 .set_video_frame_buffer(I420Buffer::Copy( 248 width, height, buffer_y, stride_y, buffer_u, 249 stride_u, buffer_v, stride_v)) 250 .set_rotation(kRotation) 251 .set_timestamp_us(0) 252 .build(); 253 frame1.set_rtp_timestamp(timestamp); 254 frame1.set_ntp_time_ms(ntp_time_ms); 255 frame1.set_timestamp_us(timestamp_us); 256 VideoFrame frame2(frame1); 257 258 EXPECT_EQ(frame1.video_frame_buffer(), frame2.video_frame_buffer()); 259 const I420BufferInterface* yuv1 = frame1.video_frame_buffer()->GetI420(); 260 const I420BufferInterface* yuv2 = frame2.video_frame_buffer()->GetI420(); 261 EXPECT_EQ(yuv1->DataY(), yuv2->DataY()); 262 EXPECT_EQ(yuv1->DataU(), yuv2->DataU()); 263 EXPECT_EQ(yuv1->DataV(), yuv2->DataV()); 264 265 EXPECT_EQ(frame2.rtp_timestamp(), frame1.rtp_timestamp()); 266 EXPECT_EQ(frame2.ntp_time_ms(), frame1.ntp_time_ms()); 267 EXPECT_EQ(frame2.timestamp_us(), frame1.timestamp_us()); 268 EXPECT_EQ(frame2.rotation(), frame1.rotation()); 269 270 frame2.set_rtp_timestamp(timestamp + 1); 271 frame2.set_ntp_time_ms(ntp_time_ms + 1); 272 frame2.set_timestamp_us(timestamp_us + 1); 273 frame2.set_rotation(kVideoRotation_90); 274 275 EXPECT_NE(frame2.rtp_timestamp(), frame1.rtp_timestamp()); 276 EXPECT_NE(frame2.ntp_time_ms(), frame1.ntp_time_ms()); 277 EXPECT_NE(frame2.timestamp_us(), frame1.timestamp_us()); 278 EXPECT_NE(frame2.rotation(), frame1.rotation()); 279 } 280 281 TEST(TestVideoFrame, TextureInitialValues) { 282 VideoFrame frame = 283 test::FakeNativeBuffer::CreateFrame(640, 480, 100, 10, kVideoRotation_0); 284 EXPECT_EQ(640, frame.width()); 285 EXPECT_EQ(480, frame.height()); 286 EXPECT_EQ(100u, frame.rtp_timestamp()); 287 EXPECT_EQ(10, frame.render_time_ms()); 288 ASSERT_TRUE(frame.video_frame_buffer() != nullptr); 289 EXPECT_TRUE(frame.video_frame_buffer()->type() == 290 VideoFrameBuffer::Type::kNative); 291 292 frame.set_rtp_timestamp(200); 293 EXPECT_EQ(200u, frame.rtp_timestamp()); 294 frame.set_timestamp_us(20); 295 EXPECT_EQ(20, frame.timestamp_us()); 296 } 297 298 template <typename T> 299 class TestPlanarYuvBuffer : public ::testing::Test {}; 300 TYPED_TEST_SUITE_P(TestPlanarYuvBuffer); 301 302 template <class T> 303 scoped_refptr<T> CreateAndFillBuffer() { 304 auto buf = T::Create(20, 10); 305 memset(buf->MutableDataY(), 1, 200); 306 307 if (buf->type() == VideoFrameBuffer::Type::kI444 || 308 buf->type() == VideoFrameBuffer::Type::kI410) { 309 memset(buf->MutableDataU(), 2, 200); 310 memset(buf->MutableDataV(), 3, 200); 311 } else if (buf->type() == VideoFrameBuffer::Type::kI422 || 312 buf->type() == VideoFrameBuffer::Type::kI210) { 313 memset(buf->MutableDataU(), 2, 100); 314 memset(buf->MutableDataV(), 3, 100); 315 } else { 316 memset(buf->MutableDataU(), 2, 50); 317 memset(buf->MutableDataV(), 3, 50); 318 } 319 320 return buf; 321 } 322 323 TYPED_TEST_P(TestPlanarYuvBuffer, Copy) { 324 scoped_refptr<TypeParam> buf1 = CreateAndFillBuffer<TypeParam>(); 325 scoped_refptr<TypeParam> buf2 = TypeParam::Copy(*buf1); 326 EXPECT_TRUE(test::FrameBufsEqual(buf1, buf2)); 327 } 328 329 TYPED_TEST_P(TestPlanarYuvBuffer, CropXCenter) { 330 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100); 331 332 // Pure center cropping, no scaling. 333 scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100); 334 scaled_buffer->CropAndScaleFrom(*buf, 50, 0, 100, 100); 335 CheckCrop<TypeParam>(*scaled_buffer, 0.25, 0.0, 0.5, 1.0); 336 } 337 338 TYPED_TEST_P(TestPlanarYuvBuffer, CropXNotCenter) { 339 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100); 340 341 // Non-center cropping, no scaling. 342 scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100); 343 scaled_buffer->CropAndScaleFrom(*buf, 25, 0, 100, 100); 344 CheckCrop<TypeParam>(*scaled_buffer, 0.125, 0.0, 0.5, 1.0); 345 } 346 347 TYPED_TEST_P(TestPlanarYuvBuffer, CropYCenter) { 348 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(100, 200); 349 350 // Pure center cropping, no scaling. 351 scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100); 352 scaled_buffer->CropAndScaleFrom(*buf, 0, 50, 100, 100); 353 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.25, 1.0, 0.5); 354 } 355 356 TYPED_TEST_P(TestPlanarYuvBuffer, CropYNotCenter) { 357 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(100, 200); 358 359 // Pure center cropping, no scaling. 360 scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100); 361 scaled_buffer->CropAndScaleFrom(*buf, 0, 25, 100, 100); 362 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.125, 1.0, 0.5); 363 } 364 365 TYPED_TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) { 366 const int buffer_width = 640; 367 const int buffer_height = 480; 368 const int crop_width = 320; 369 const int crop_height = 180; 370 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(640, 480); 371 372 // Pure center cropping, no scaling. 373 const int out_width = 374 std::min(buffer_width, crop_width * buffer_height / crop_height); 375 const int out_height = 376 std::min(buffer_height, crop_height * buffer_width / crop_width); 377 scoped_refptr<TypeParam> scaled_buffer = 378 TypeParam::Create(out_width, out_height); 379 scaled_buffer->CropAndScaleFrom(*buf, (buffer_width - out_width) / 2, 380 (buffer_height - out_height) / 2, out_width, 381 out_height); 382 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.125, 1.0, 0.75); 383 } 384 385 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBuffer, 386 Copy, 387 CropXCenter, 388 CropXNotCenter, 389 CropYCenter, 390 CropYNotCenter, 391 CropAndScale16x9); 392 393 using TestTypesAll = ::testing::Types<I420Buffer, 394 I010Buffer, 395 I444Buffer, 396 I422Buffer, 397 I210Buffer, 398 I410Buffer>; 399 INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBuffer, TestTypesAll); 400 401 template <class T> 402 class TestPlanarYuvBufferScale : public ::testing::Test {}; 403 TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale); 404 405 TYPED_TEST_P(TestPlanarYuvBufferScale, Scale) { 406 scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100); 407 408 // Pure scaling, no cropping. 409 scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(150, 75); 410 scaled_buffer->ScaleFrom(*buf); 411 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.0, 1.0, 1.0); 412 } 413 414 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale, Scale); 415 416 using TestTypesScale = 417 ::testing::Types<I420Buffer, I010Buffer, I210Buffer, I410Buffer>; 418 INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBufferScale, TestTypesScale); 419 420 template <class T> 421 class TestPlanarYuvBufferRotate : public ::testing::Test { 422 public: 423 std::vector<webrtc::VideoRotation> RotationParams = { 424 kVideoRotation_0, kVideoRotation_90, kVideoRotation_180, 425 kVideoRotation_270}; 426 }; 427 428 TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate); 429 430 TYPED_TEST_P(TestPlanarYuvBufferRotate, Rotates) { 431 for (const VideoRotation& rotation : this->RotationParams) { 432 scoped_refptr<TypeParam> buffer = CreateGradient<TypeParam>(640, 480); 433 scoped_refptr<TypeParam> rotated_buffer = 434 TypeParam::Rotate(*buffer, rotation); 435 CheckRotate(640, 480, rotation, *rotated_buffer); 436 } 437 } 438 439 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate, Rotates); 440 441 using TestTypesRotate = ::testing:: 442 Types<I420Buffer, I010Buffer, I444Buffer, I422Buffer, I210Buffer>; 443 INSTANTIATE_TYPED_TEST_SUITE_P(Rotate, 444 TestPlanarYuvBufferRotate, 445 TestTypesRotate); 446 447 TEST(TestNV12Buffer, CropAndScale) { 448 const int kSourceWidth = 640; 449 const int kSourceHeight = 480; 450 const int kScaledWidth = 320; 451 const int kScaledHeight = 240; 452 const int kCropLeft = 40; 453 const int kCropTop = 30; 454 const int kCropRight = 0; 455 const int kCropBottom = 30; 456 457 scoped_refptr<VideoFrameBuffer> buf = 458 CreateNV12Gradient(kSourceWidth, kSourceHeight); 459 460 scoped_refptr<VideoFrameBuffer> scaled_buffer = buf->CropAndScale( 461 kCropLeft, kCropTop, kSourceWidth - kCropLeft - kCropRight, 462 kSourceHeight - kCropTop - kCropBottom, kScaledWidth, kScaledHeight); 463 464 // Parameters to CheckCrop indicate what part of the source frame is in the 465 // scaled frame. 466 const float kOffsetX = (kCropLeft + 0.0) / kSourceWidth; 467 const float kOffsetY = (kCropTop + 0.0) / kSourceHeight; 468 const float kRelativeWidth = 469 (kSourceWidth - kCropLeft - kCropRight + 0.0) / kSourceWidth; 470 const float kRelativeHeight = 471 (kSourceHeight - kCropTop - kCropBottom + 0.0) / kSourceHeight; 472 CheckCrop(*scaled_buffer->ToI420(), kOffsetX, kOffsetY, kRelativeWidth, 473 kRelativeHeight); 474 } 475 476 TEST(TestUpdateRect, CanCompare) { 477 VideoFrame::UpdateRect a = { 478 .offset_x = 0, .offset_y = 0, .width = 100, .height = 200}; 479 VideoFrame::UpdateRect b = { 480 .offset_x = 0, .offset_y = 0, .width = 100, .height = 200}; 481 VideoFrame::UpdateRect c = { 482 .offset_x = 1, .offset_y = 0, .width = 100, .height = 200}; 483 VideoFrame::UpdateRect d = { 484 .offset_x = 0, .offset_y = 1, .width = 100, .height = 200}; 485 EXPECT_TRUE(a == b); 486 EXPECT_FALSE(a == c); 487 EXPECT_FALSE(a == d); 488 } 489 490 TEST(TestUpdateRect, ComputesIsEmpty) { 491 VideoFrame::UpdateRect a = { 492 .offset_x = 0, .offset_y = 0, .width = 0, .height = 0}; 493 VideoFrame::UpdateRect b = { 494 .offset_x = 0, .offset_y = 0, .width = 100, .height = 200}; 495 VideoFrame::UpdateRect c = { 496 .offset_x = 1, .offset_y = 100, .width = 0, .height = 0}; 497 VideoFrame::UpdateRect d = { 498 .offset_x = 1, .offset_y = 100, .width = 100, .height = 200}; 499 EXPECT_TRUE(a.IsEmpty()); 500 EXPECT_FALSE(b.IsEmpty()); 501 EXPECT_TRUE(c.IsEmpty()); 502 EXPECT_FALSE(d.IsEmpty()); 503 } 504 505 TEST(TestUpdateRectUnion, NonIntersecting) { 506 VideoFrame::UpdateRect a = { 507 .offset_x = 0, .offset_y = 0, .width = 10, .height = 20}; 508 VideoFrame::UpdateRect b = { 509 .offset_x = 100, .offset_y = 200, .width = 10, .height = 20}; 510 a.Union(b); 511 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 110, 220})); 512 } 513 514 TEST(TestUpdateRectUnion, Intersecting) { 515 VideoFrame::UpdateRect a = { 516 .offset_x = 0, .offset_y = 0, .width = 10, .height = 10}; 517 VideoFrame::UpdateRect b = { 518 .offset_x = 5, .offset_y = 5, .width = 30, .height = 20}; 519 a.Union(b); 520 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 35, 25})); 521 } 522 523 TEST(TestUpdateRectUnion, OneInsideAnother) { 524 VideoFrame::UpdateRect a = { 525 .offset_x = 0, .offset_y = 0, .width = 100, .height = 100}; 526 VideoFrame::UpdateRect b = { 527 .offset_x = 5, .offset_y = 5, .width = 30, .height = 20}; 528 a.Union(b); 529 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 100, 100})); 530 } 531 532 TEST(TestUpdateRectIntersect, NonIntersecting) { 533 VideoFrame::UpdateRect a = { 534 .offset_x = 0, .offset_y = 0, .width = 10, .height = 20}; 535 VideoFrame::UpdateRect b = { 536 .offset_x = 100, .offset_y = 200, .width = 10, .height = 20}; 537 a.Intersect(b); 538 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 0, 0})); 539 } 540 541 TEST(TestUpdateRectIntersect, Intersecting) { 542 VideoFrame::UpdateRect a = { 543 .offset_x = 0, .offset_y = 0, .width = 10, .height = 10}; 544 VideoFrame::UpdateRect b = { 545 .offset_x = 5, .offset_y = 5, .width = 30, .height = 20}; 546 a.Intersect(b); 547 EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 5, 5})); 548 } 549 550 TEST(TestUpdateRectIntersect, OneInsideAnother) { 551 VideoFrame::UpdateRect a = { 552 .offset_x = 0, .offset_y = 0, .width = 100, .height = 100}; 553 VideoFrame::UpdateRect b = { 554 .offset_x = 5, .offset_y = 5, .width = 30, .height = 20}; 555 a.Intersect(b); 556 EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 30, 20})); 557 } 558 559 TEST(TestUpdateRectScale, NoScale) { 560 const int width = 640; 561 const int height = 480; 562 VideoFrame::UpdateRect a = { 563 .offset_x = 100, .offset_y = 50, .width = 100, .height = 200}; 564 VideoFrame::UpdateRect scaled = 565 a.ScaleWithFrame(width, height, 0, 0, width, height, width, height); 566 EXPECT_EQ(scaled, VideoFrame::UpdateRect({100, 50, 100, 200})); 567 } 568 569 TEST(TestUpdateRectScale, CropOnly) { 570 const int width = 640; 571 const int height = 480; 572 VideoFrame::UpdateRect a = { 573 .offset_x = 100, .offset_y = 50, .width = 100, .height = 200}; 574 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 575 width, height, 10, 10, width - 20, height - 20, width - 20, height - 20); 576 EXPECT_EQ(scaled, VideoFrame::UpdateRect({90, 40, 100, 200})); 577 } 578 579 TEST(TestUpdateRectScale, CropOnlyToOddOffset) { 580 const int width = 640; 581 const int height = 480; 582 VideoFrame::UpdateRect a = { 583 .offset_x = 100, .offset_y = 50, .width = 100, .height = 200}; 584 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 585 width, height, 5, 5, width - 10, height - 10, width - 10, height - 10); 586 EXPECT_EQ(scaled, VideoFrame::UpdateRect({94, 44, 102, 202})); 587 } 588 589 TEST(TestUpdateRectScale, ScaleByHalf) { 590 const int width = 640; 591 const int height = 480; 592 VideoFrame::UpdateRect a = { 593 .offset_x = 100, .offset_y = 60, .width = 100, .height = 200}; 594 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 595 width, height, 0, 0, width, height, width / 2, height / 2); 596 // Scaled by half and +2 pixels in all directions. 597 EXPECT_EQ(scaled, VideoFrame::UpdateRect({48, 28, 54, 104})); 598 } 599 600 TEST(TestUpdateRectScale, CropToUnchangedRegionBelowUpdateRect) { 601 const int width = 640; 602 const int height = 480; 603 VideoFrame::UpdateRect a = { 604 .offset_x = 100, .offset_y = 60, .width = 100, .height = 200}; 605 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 606 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10); 607 // Update is out of the cropped frame. 608 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0})); 609 } 610 611 TEST(TestUpdateRectScale, CropToUnchangedRegionAboveUpdateRect) { 612 const int width = 640; 613 const int height = 480; 614 VideoFrame::UpdateRect a = { 615 .offset_x = 600, .offset_y = 400, .width = 10, .height = 10}; 616 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 617 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10); 618 // Update is out of the cropped frame. 619 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0})); 620 } 621 622 TEST(TestUpdateRectScale, CropInsideUpdate) { 623 const int width = 640; 624 const int height = 480; 625 VideoFrame::UpdateRect a = { 626 .offset_x = 300, .offset_y = 200, .width = 100, .height = 100}; 627 VideoFrame::UpdateRect scaled = a.ScaleWithFrame( 628 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10); 629 // Cropped frame is inside the update rect. 630 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 10, 10})); 631 } 632 633 TEST(TestUpdateRectScale, CropAndScaleByHalf) { 634 const int width = 640; 635 const int height = 480; 636 VideoFrame::UpdateRect a = { 637 .offset_x = 100, .offset_y = 60, .width = 100, .height = 200}; 638 VideoFrame::UpdateRect scaled = 639 a.ScaleWithFrame(width, height, 10, 10, width - 20, height - 20, 640 (width - 20) / 2, (height - 20) / 2); 641 // Scaled by half and +3 pixels in all directions, because of odd offset after 642 // crop and scale. 643 EXPECT_EQ(scaled, VideoFrame::UpdateRect({42, 22, 56, 106})); 644 } 645 646 TEST(TestVideoFrame, IsRepeatFrame) { 647 // Defaults to false. 648 VideoFrame frame1 = VideoFrame::Builder() 649 .set_video_frame_buffer(I420Buffer::Create(10, 10)) 650 .set_rotation(kVideoRotation_0) 651 .set_timestamp_ms(123) 652 .build(); 653 EXPECT_FALSE(frame1.is_repeat_frame()); 654 655 // Set to true. 656 VideoFrame frame2 = VideoFrame::Builder() 657 .set_video_frame_buffer(I420Buffer::Create(10, 10)) 658 .set_rotation(kVideoRotation_0) 659 .set_timestamp_ms(456) 660 .set_is_repeat_frame(true) 661 .build(); 662 EXPECT_TRUE(frame2.is_repeat_frame()); 663 664 // Set to false. 665 VideoFrame frame3 = VideoFrame::Builder() 666 .set_video_frame_buffer(I420Buffer::Create(10, 10)) 667 .set_rotation(kVideoRotation_0) 668 .set_timestamp_ms(789) 669 .set_is_repeat_frame(false) 670 .build(); 671 EXPECT_FALSE(frame3.is_repeat_frame()); 672 } 673 674 } // namespace webrtc