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