• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016 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 "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
12 
13 #include <initializer_list>
14 #include <memory>
15 #include <utility>
16 #include <vector>
17 
18 #include "modules/desktop_capture/desktop_geometry.h"
19 #include "modules/desktop_capture/desktop_region.h"
20 #include "modules/desktop_capture/differ_block.h"
21 #include "modules/desktop_capture/fake_desktop_capturer.h"
22 #include "modules/desktop_capture/mock_desktop_capturer_callback.h"
23 #include "rtc_base/random.h"
24 #include "rtc_base/time_utils.h"
25 #include "system_wrappers/include/cpu_features_wrapper.h"
26 #include "test/gtest.h"
27 
28 namespace webrtc {
29 
30 namespace {
31 
32 // Compares and asserts `frame`.updated_region() equals to `rects`. This
33 // function does not care about the order of the `rects` and it does not expect
34 // DesktopRegion to return an exact area for each rectangle in `rects`.
35 template <template <typename, typename...> class T = std::initializer_list,
36           typename... Rect>
AssertUpdatedRegionIs(const DesktopFrame & frame,const T<DesktopRect,Rect...> & rects)37 void AssertUpdatedRegionIs(const DesktopFrame& frame,
38                            const T<DesktopRect, Rect...>& rects) {
39   DesktopRegion region;
40   for (const auto& rect : rects) {
41     region.AddRect(rect);
42   }
43   ASSERT_TRUE(frame.updated_region().Equals(region));
44 }
45 
46 // Compares and asserts `frame`.updated_region() covers all rectangles in
47 // `rects`, but does not cover areas other than a kBlockSize expansion. This
48 // function does not care about the order of the `rects`, and it does not expect
49 // DesktopRegion to return an exact area of each rectangle in `rects`.
50 template <template <typename, typename...> class T = std::initializer_list,
51           typename... Rect>
AssertUpdatedRegionCovers(const DesktopFrame & frame,const T<DesktopRect,Rect...> & rects)52 void AssertUpdatedRegionCovers(const DesktopFrame& frame,
53                                const T<DesktopRect, Rect...>& rects) {
54   DesktopRegion region;
55   for (const auto& rect : rects) {
56     region.AddRect(rect);
57   }
58 
59   // Intersect of `rects` and `frame`.updated_region() should be `rects`. i.e.
60   // `frame`.updated_region() should be a superset of `rects`.
61   DesktopRegion intersect(region);
62   intersect.IntersectWith(frame.updated_region());
63   ASSERT_TRUE(region.Equals(intersect));
64 
65   // Difference between `rects` and `frame`.updated_region() should not cover
66   // areas which have larger than twice of kBlockSize width and height.
67   //
68   // Explanation of the 'twice' of kBlockSize (indeed kBlockSize * 2 - 2) is
69   // following,
70   // (Each block in the following grid is a 8 x 8 pixels area. X means the real
71   // updated area, m means the updated area marked by
72   // DesktopCapturerDifferWrapper.)
73   // +---+---+---+---+---+---+---+---+
74   // | X | m | m | m | m | m | m | m |
75   // +---+---+---+---+---+---+---+---+
76   // | m | m | m | m | m | m | m | m |
77   // +---+---+---+---+---+---+---+---+
78   // | m | m | m | m | m | m | m | m |
79   // +---+---+---+---+---+---+---+---+
80   // | m | m | m | m | m | m | m | X |
81   // +---+---+---+---+---+---+---+---+
82   // The top left [0, 0] - [8, 8] and right bottom [56, 24] - [64, 32] blocks of
83   // this area are updated. But since DesktopCapturerDifferWrapper compares
84   // 32 x 32 blocks by default, this entire area is marked as updated. So the
85   // [8, 8] - [56, 32] is expected to be covered in the difference.
86   //
87   // But if [0, 0] - [8, 8] and [64, 24] - [72, 32] blocks are updated,
88   // +---+---+---+---+---+---+---+---+---+---+---+---+
89   // | X | m | m | m |   |   |   |   | m | m | m | m |
90   // +---+---+---+---+---+---+---+---+---+---+---+---+
91   // | m | m | m | m |   |   |   |   | m | m | m | m |
92   // +---+---+---+---+---+---+---+---+---+---+---+---+
93   // | m | m | m | m |   |   |   |   | m | m | m | m |
94   // +---+---+---+---+---+---+---+---+---+---+---+---+
95   // | m | m | m | m |   |   |   |   | X | m | m | m |
96   // +---+---+---+---+---+---+---+---+---+---+---+---+
97   // the [8, 8] - [64, 32] is not expected to be covered in the difference. As
98   // DesktopCapturerDifferWrapper should only mark [0, 0] - [32, 32] and
99   // [64, 0] - [96, 32] as updated.
100   DesktopRegion differ(frame.updated_region());
101   differ.Subtract(region);
102   for (DesktopRegion::Iterator it(differ); !it.IsAtEnd(); it.Advance()) {
103     ASSERT_TRUE(it.rect().width() <= kBlockSize * 2 - 2 ||
104                 it.rect().height() <= kBlockSize * 2 - 2);
105   }
106 }
107 
108 // Executes a DesktopCapturerDifferWrapper::Capture() and compares its output
109 // DesktopFrame::updated_region() with `updated_region` if `check_result` is
110 // true. If `exactly_match` is true, AssertUpdatedRegionIs() will be used,
111 // otherwise AssertUpdatedRegionCovers() will be used.
112 template <template <typename, typename...> class T = std::initializer_list,
113           typename... Rect>
ExecuteDifferWrapperCase(BlackWhiteDesktopFramePainter * frame_painter,DesktopCapturerDifferWrapper * capturer,MockDesktopCapturerCallback * callback,const T<DesktopRect,Rect...> & updated_region,bool check_result,bool exactly_match)114 void ExecuteDifferWrapperCase(BlackWhiteDesktopFramePainter* frame_painter,
115                               DesktopCapturerDifferWrapper* capturer,
116                               MockDesktopCapturerCallback* callback,
117                               const T<DesktopRect, Rect...>& updated_region,
118                               bool check_result,
119                               bool exactly_match) {
120   EXPECT_CALL(*callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
121                                             ::testing::_))
122       .Times(1)
123       .WillOnce(
124           ::testing::Invoke([&updated_region, check_result, exactly_match](
125                                 DesktopCapturer::Result result,
126                                 std::unique_ptr<DesktopFrame>* frame) {
127             ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
128             if (check_result) {
129               if (exactly_match) {
130                 AssertUpdatedRegionIs(**frame, updated_region);
131               } else {
132                 AssertUpdatedRegionCovers(**frame, updated_region);
133               }
134             }
135           }));
136   for (const auto& rect : updated_region) {
137     frame_painter->updated_region()->AddRect(rect);
138   }
139   capturer->CaptureFrame();
140 }
141 
142 // Executes a DesktopCapturerDifferWrapper::Capture(), if updated_region() is
143 // not set, this function will reset DesktopCapturerDifferWrapper internal
144 // DesktopFrame into black.
ExecuteCapturer(DesktopCapturerDifferWrapper * capturer,MockDesktopCapturerCallback * callback)145 void ExecuteCapturer(DesktopCapturerDifferWrapper* capturer,
146                      MockDesktopCapturerCallback* callback) {
147   EXPECT_CALL(*callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
148                                             ::testing::_))
149       .Times(1);
150   capturer->CaptureFrame();
151 }
152 
ExecuteDifferWrapperTest(bool with_hints,bool enlarge_updated_region,bool random_updated_region,bool check_result)153 void ExecuteDifferWrapperTest(bool with_hints,
154                               bool enlarge_updated_region,
155                               bool random_updated_region,
156                               bool check_result) {
157   const bool updated_region_should_exactly_match =
158       with_hints && !enlarge_updated_region && !random_updated_region;
159   BlackWhiteDesktopFramePainter frame_painter;
160   PainterDesktopFrameGenerator frame_generator;
161   frame_generator.set_desktop_frame_painter(&frame_painter);
162   std::unique_ptr<FakeDesktopCapturer> fake(new FakeDesktopCapturer());
163   fake->set_frame_generator(&frame_generator);
164   DesktopCapturerDifferWrapper capturer(std::move(fake));
165   MockDesktopCapturerCallback callback;
166   frame_generator.set_provide_updated_region_hints(with_hints);
167   frame_generator.set_enlarge_updated_region(enlarge_updated_region);
168   frame_generator.set_add_random_updated_region(random_updated_region);
169 
170   capturer.Start(&callback);
171 
172   EXPECT_CALL(callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
173                                            ::testing::_))
174       .Times(1)
175       .WillOnce(::testing::Invoke([](DesktopCapturer::Result result,
176                                      std::unique_ptr<DesktopFrame>* frame) {
177         ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
178         AssertUpdatedRegionIs(**frame,
179                               {DesktopRect::MakeSize((*frame)->size())});
180       }));
181   capturer.CaptureFrame();
182 
183   ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
184                            {DesktopRect::MakeLTRB(100, 100, 200, 200),
185                             DesktopRect::MakeLTRB(300, 300, 400, 400)},
186                            check_result, updated_region_should_exactly_match);
187   ExecuteCapturer(&capturer, &callback);
188 
189   ExecuteDifferWrapperCase(
190       &frame_painter, &capturer, &callback,
191       {DesktopRect::MakeLTRB(0, 0, 40, 40),
192        DesktopRect::MakeLTRB(0, frame_generator.size()->height() - 40, 40,
193                              frame_generator.size()->height()),
194        DesktopRect::MakeLTRB(frame_generator.size()->width() - 40, 0,
195                              frame_generator.size()->width(), 40),
196        DesktopRect::MakeLTRB(frame_generator.size()->width() - 40,
197                              frame_generator.size()->height() - 40,
198                              frame_generator.size()->width(),
199                              frame_generator.size()->height())},
200       check_result, updated_region_should_exactly_match);
201 
202   Random random(rtc::TimeMillis());
203   // Fuzzing tests.
204   for (int i = 0; i < 1000; i++) {
205     if (enlarge_updated_region) {
206       frame_generator.set_enlarge_range(random.Rand(1, 50));
207     }
208     frame_generator.size()->set(random.Rand(500, 2000), random.Rand(500, 2000));
209     ExecuteCapturer(&capturer, &callback);
210     std::vector<DesktopRect> updated_region;
211     for (int j = random.Rand(50); j >= 0; j--) {
212       // At least a 1 x 1 updated region.
213       const int left = random.Rand(0, frame_generator.size()->width() - 2);
214       const int top = random.Rand(0, frame_generator.size()->height() - 2);
215       const int right = random.Rand(left + 1, frame_generator.size()->width());
216       const int bottom = random.Rand(top + 1, frame_generator.size()->height());
217       updated_region.push_back(DesktopRect::MakeLTRB(left, top, right, bottom));
218     }
219     ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
220                              updated_region, check_result,
221                              updated_region_should_exactly_match);
222   }
223 }
224 
225 }  // namespace
226 
TEST(DesktopCapturerDifferWrapperTest,CaptureWithoutHints)227 TEST(DesktopCapturerDifferWrapperTest, CaptureWithoutHints) {
228   ExecuteDifferWrapperTest(false, false, false, true);
229 }
230 
TEST(DesktopCapturerDifferWrapperTest,CaptureWithHints)231 TEST(DesktopCapturerDifferWrapperTest, CaptureWithHints) {
232   ExecuteDifferWrapperTest(true, false, false, true);
233 }
234 
TEST(DesktopCapturerDifferWrapperTest,CaptureWithEnlargedHints)235 TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedHints) {
236   ExecuteDifferWrapperTest(true, true, false, true);
237 }
238 
TEST(DesktopCapturerDifferWrapperTest,CaptureWithRandomHints)239 TEST(DesktopCapturerDifferWrapperTest, CaptureWithRandomHints) {
240   ExecuteDifferWrapperTest(true, false, true, true);
241 }
242 
TEST(DesktopCapturerDifferWrapperTest,CaptureWithEnlargedAndRandomHints)243 TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedAndRandomHints) {
244   ExecuteDifferWrapperTest(true, true, true, true);
245 }
246 
247 // When hints are provided, DesktopCapturerDifferWrapper has a slightly better
248 // performance in current configuration, but not so significant. Following is
249 // one run result.
250 // [ RUN      ] DISABLED_CaptureWithoutHintsPerf
251 // [       OK ] DISABLED_CaptureWithoutHintsPerf (7118 ms)
252 // [ RUN      ] DISABLED_CaptureWithHintsPerf
253 // [       OK ] DISABLED_CaptureWithHintsPerf (5580 ms)
254 // [ RUN      ] DISABLED_CaptureWithEnlargedHintsPerf
255 // [       OK ] DISABLED_CaptureWithEnlargedHintsPerf (5974 ms)
256 // [ RUN      ] DISABLED_CaptureWithRandomHintsPerf
257 // [       OK ] DISABLED_CaptureWithRandomHintsPerf (6184 ms)
258 // [ RUN      ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf
259 // [       OK ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf (6347 ms)
TEST(DesktopCapturerDifferWrapperTest,DISABLED_CaptureWithoutHintsPerf)260 TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithoutHintsPerf) {
261   int64_t started = rtc::TimeMillis();
262   ExecuteDifferWrapperTest(false, false, false, false);
263   ASSERT_LE(rtc::TimeMillis() - started, 15000);
264 }
265 
TEST(DesktopCapturerDifferWrapperTest,DISABLED_CaptureWithHintsPerf)266 TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithHintsPerf) {
267   int64_t started = rtc::TimeMillis();
268   ExecuteDifferWrapperTest(true, false, false, false);
269   ASSERT_LE(rtc::TimeMillis() - started, 15000);
270 }
271 
TEST(DesktopCapturerDifferWrapperTest,DISABLED_CaptureWithEnlargedHintsPerf)272 TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithEnlargedHintsPerf) {
273   int64_t started = rtc::TimeMillis();
274   ExecuteDifferWrapperTest(true, true, false, false);
275   ASSERT_LE(rtc::TimeMillis() - started, 15000);
276 }
277 
TEST(DesktopCapturerDifferWrapperTest,DISABLED_CaptureWithRandomHintsPerf)278 TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithRandomHintsPerf) {
279   int64_t started = rtc::TimeMillis();
280   ExecuteDifferWrapperTest(true, false, true, false);
281   ASSERT_LE(rtc::TimeMillis() - started, 15000);
282 }
283 
TEST(DesktopCapturerDifferWrapperTest,DISABLED_CaptureWithEnlargedAndRandomHintsPerf)284 TEST(DesktopCapturerDifferWrapperTest,
285      DISABLED_CaptureWithEnlargedAndRandomHintsPerf) {
286   int64_t started = rtc::TimeMillis();
287   ExecuteDifferWrapperTest(true, true, true, false);
288   ASSERT_LE(rtc::TimeMillis() - started, 15000);
289 }
290 
291 }  // namespace webrtc
292