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