blob: f9745b9d6b88ec76fc2d2cea885d95a59cc73bd4 [file] [log] [blame]
zijiehe6be0a652016-10-27 16:50:35 -07001/*
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 <string.h>
12
13#include <algorithm>
14#include <initializer_list>
zijiehe21841552016-11-18 20:31:02 -080015#include <iostream> // TODO(zijiehe): Remove once flaky has been resolved.
zijiehe6be0a652016-10-27 16:50:35 -070016#include <memory>
17#include <utility>
18
zijiehe21841552016-11-18 20:31:02 -080019// TODO(zijiehe): Remove once flaky has been resolved.
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/desktop_capture/desktop_capture_options.h"
21#include "modules/desktop_capture/desktop_capturer.h"
22#include "modules/desktop_capture/desktop_frame.h"
23#include "modules/desktop_capture/desktop_region.h"
24#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
25#include "modules/desktop_capture/rgba_color.h"
26#include "modules/desktop_capture/screen_drawer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "rtc_base/constructor_magic.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "rtc_base/logging.h"
Artem Titova76af0c2018-07-23 17:38:12 +020030#include "rtc_base/third_party/base64/base64.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020031#include "test/gmock.h"
32#include "test/gtest.h"
zijiehe6be0a652016-10-27 16:50:35 -070033
34#if defined(WEBRTC_WIN)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020035#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
36#include "rtc_base/win32.h"
zijiehe6be0a652016-10-27 16:50:35 -070037#endif // defined(WEBRTC_WIN)
38
39using ::testing::_;
40
41namespace webrtc {
42
43namespace {
44
zijiehee83f4b32016-12-08 11:47:01 -080045ACTION_P2(SaveCaptureResult, result, dest) {
46 *result = arg0;
zijiehe6be0a652016-10-27 16:50:35 -070047 *dest = std::move(*arg1);
48}
49
50// Returns true if color in |rect| of |frame| is |color|.
51bool ArePixelsColoredBy(const DesktopFrame& frame,
52 DesktopRect rect,
53 RgbaColor color,
54 bool may_partially_draw) {
55 if (!may_partially_draw) {
56 // updated_region() should cover the painted area.
57 DesktopRegion updated_region(frame.updated_region());
58 updated_region.IntersectWith(rect);
59 if (!updated_region.Equals(DesktopRegion(rect))) {
60 return false;
61 }
62 }
63
64 // Color in the |rect| should be |color|.
65 uint8_t* row = frame.GetFrameDataAtPos(rect.top_left());
66 for (int i = 0; i < rect.height(); i++) {
67 uint8_t* column = row;
68 for (int j = 0; j < rect.width(); j++) {
69 if (color != RgbaColor(column)) {
70 return false;
71 }
72 column += DesktopFrame::kBytesPerPixel;
73 }
74 row += frame.stride();
75 }
76 return true;
77}
78
79} // namespace
80
Mirko Bonadei6a489f22019-04-09 15:11:12 +020081class ScreenCapturerIntegrationTest : public ::testing::Test {
zijiehe6be0a652016-10-27 16:50:35 -070082 public:
83 void SetUp() override {
zijiehe54fd5792016-11-02 14:49:35 -070084 capturer_ = DesktopCapturer::CreateScreenCapturer(
85 DesktopCaptureOptions::CreateDefault());
zijiehe6be0a652016-10-27 16:50:35 -070086 }
87
88 protected:
89 void TestCaptureUpdatedRegion(
zijiehe54fd5792016-11-02 14:49:35 -070090 std::initializer_list<DesktopCapturer*> capturers) {
zijiehe6be0a652016-10-27 16:50:35 -070091 RTC_DCHECK(capturers.size() > 0);
Yves Gerey665174f2018-06-19 15:03:05 +020092// A large enough area for the tests, which should be able to be fulfilled
93// by most systems.
zijiehe166e59a2016-11-29 14:46:46 -080094#if defined(WEBRTC_WIN)
95 // On Windows, an interesting warning window may pop up randomly. The root
96 // cause is still under investigation, so reduce the test area to work
97 // around. Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6666.
98 const int kTestArea = 416;
99#else
zijiehe6be0a652016-10-27 16:50:35 -0700100 const int kTestArea = 512;
zijiehe166e59a2016-11-29 14:46:46 -0800101#endif
zijiehe6be0a652016-10-27 16:50:35 -0700102 const int kRectSize = 32;
103 std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
104 if (!drawer || drawer->DrawableRegion().is_empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100105 RTC_LOG(LS_WARNING)
106 << "No ScreenDrawer implementation for current platform.";
zijiehe6be0a652016-10-27 16:50:35 -0700107 return;
108 }
109 if (drawer->DrawableRegion().width() < kTestArea ||
110 drawer->DrawableRegion().height() < kTestArea) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100111 RTC_LOG(LS_WARNING)
112 << "ScreenDrawer::DrawableRegion() is too small for the "
113 "CaptureUpdatedRegion tests.";
zijiehe6be0a652016-10-27 16:50:35 -0700114 return;
115 }
116
zijiehe54fd5792016-11-02 14:49:35 -0700117 for (DesktopCapturer* capturer : capturers) {
zijiehe6be0a652016-10-27 16:50:35 -0700118 capturer->Start(&callback_);
119 }
120
121 // Draw a set of |kRectSize| by |kRectSize| rectangles at (|i|, |i|), or
122 // |i| by |i| rectangles at (|kRectSize|, |kRectSize|). One of (controlled
123 // by |c|) its primary colors is |i|, and the other two are 0x7f. So we
124 // won't draw a black or white rectangle.
125 for (int c = 0; c < 3; c++) {
126 // A fixed size rectangle.
127 for (int i = 0; i < kTestArea - kRectSize; i += 16) {
128 DesktopRect rect = DesktopRect::MakeXYWH(i, i, kRectSize, kRectSize);
129 rect.Translate(drawer->DrawableRegion().top_left());
130 RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
131 (c == 1 ? (i & 0xff) : 0x7f),
132 (c == 2 ? (i & 0xff) : 0x7f));
zijiehe21841552016-11-18 20:31:02 -0800133 // Fail fast.
134 ASSERT_NO_FATAL_FAILURE(
135 TestCaptureOneFrame(capturers, drawer.get(), rect, color));
zijiehe6be0a652016-10-27 16:50:35 -0700136 }
137
138 // A variable-size rectangle.
139 for (int i = 0; i < kTestArea - kRectSize; i += 16) {
140 DesktopRect rect = DesktopRect::MakeXYWH(kRectSize, kRectSize, i, i);
141 rect.Translate(drawer->DrawableRegion().top_left());
142 RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
143 (c == 1 ? (i & 0xff) : 0x7f),
144 (c == 2 ? (i & 0xff) : 0x7f));
zijiehe21841552016-11-18 20:31:02 -0800145 // Fail fast.
146 ASSERT_NO_FATAL_FAILURE(
147 TestCaptureOneFrame(capturers, drawer.get(), rect, color));
zijiehe6be0a652016-10-27 16:50:35 -0700148 }
149 }
150 }
151
152 void TestCaptureUpdatedRegion() {
153 TestCaptureUpdatedRegion({capturer_.get()});
154 }
155
156#if defined(WEBRTC_WIN)
157 // Enable allow_directx_capturer in DesktopCaptureOptions, but let
zijiehe54fd5792016-11-02 14:49:35 -0700158 // DesktopCapturer::CreateScreenCapturer() to decide whether a DirectX
159 // capturer should be used.
zijiehe6be0a652016-10-27 16:50:35 -0700160 void MaybeCreateDirectxCapturer() {
161 DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
162 options.set_allow_directx_capturer(true);
zijiehe54fd5792016-11-02 14:49:35 -0700163 capturer_ = DesktopCapturer::CreateScreenCapturer(options);
zijiehe6be0a652016-10-27 16:50:35 -0700164 }
165
166 bool CreateDirectxCapturer() {
167 if (!ScreenCapturerWinDirectx::IsSupported()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100168 RTC_LOG(LS_WARNING) << "Directx capturer is not supported";
zijiehe6be0a652016-10-27 16:50:35 -0700169 return false;
170 }
171
172 MaybeCreateDirectxCapturer();
173 return true;
174 }
175
176 void CreateMagnifierCapturer() {
177 DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
178 options.set_allow_use_magnification_api(true);
zijiehe54fd5792016-11-02 14:49:35 -0700179 capturer_ = DesktopCapturer::CreateScreenCapturer(options);
zijiehe6be0a652016-10-27 16:50:35 -0700180 }
181#endif // defined(WEBRTC_WIN)
182
zijiehe54fd5792016-11-02 14:49:35 -0700183 std::unique_ptr<DesktopCapturer> capturer_;
184 MockDesktopCapturerCallback callback_;
zijiehe6be0a652016-10-27 16:50:35 -0700185
186 private:
187 // Repeats capturing the frame by using |capturers| one-by-one for 600 times,
188 // typically 30 seconds, until they succeeded captured a |color| rectangle at
189 // |rect|. This function uses |drawer|->WaitForPendingDraws() between two
190 // attempts to wait for the screen to update.
zijiehe54fd5792016-11-02 14:49:35 -0700191 void TestCaptureOneFrame(std::vector<DesktopCapturer*> capturers,
zijiehe6be0a652016-10-27 16:50:35 -0700192 ScreenDrawer* drawer,
193 DesktopRect rect,
194 RgbaColor color) {
195 const int wait_capture_round = 600;
196 drawer->Clear();
197 size_t succeeded_capturers = 0;
198 for (int i = 0; i < wait_capture_round; i++) {
199 drawer->DrawRectangle(rect, color);
200 drawer->WaitForPendingDraws();
201 for (size_t j = 0; j < capturers.size(); j++) {
202 if (capturers[j] == nullptr) {
zijiehe54fd5792016-11-02 14:49:35 -0700203 // DesktopCapturer should return an empty updated_region() if no
zijiehe21841552016-11-18 20:31:02 -0800204 // update detected. So we won't test it again if it has captured the
205 // rectangle we drew.
zijiehe6be0a652016-10-27 16:50:35 -0700206 continue;
207 }
208 std::unique_ptr<DesktopFrame> frame = CaptureFrame(capturers[j]);
209 if (!frame) {
zijiehe21841552016-11-18 20:31:02 -0800210 // CaptureFrame() has triggered an assertion failure already, we only
211 // need to return here.
zijiehe6be0a652016-10-27 16:50:35 -0700212 return;
213 }
214
Yves Gerey665174f2018-06-19 15:03:05 +0200215 if (ArePixelsColoredBy(*frame, rect, color,
216 drawer->MayDrawIncompleteShapes())) {
zijiehe6be0a652016-10-27 16:50:35 -0700217 capturers[j] = nullptr;
218 succeeded_capturers++;
219 }
zijiehe21841552016-11-18 20:31:02 -0800220 // The following else if statement is for debugging purpose only, which
221 // should be removed after flaky of ScreenCapturerIntegrationTest has
222 // been resolved.
223 else if (i == wait_capture_round - 1) {
224 std::string result;
Yves Gerey665174f2018-06-19 15:03:05 +0200225 rtc::Base64::EncodeFromArray(
226 frame->data(), frame->size().height() * frame->stride(), &result);
zijiehe21841552016-11-18 20:31:02 -0800227 std::cout << frame->size().width() << " x " << frame->size().height()
228 << std::endl;
229 // Split the entire string (can be over 4M) into several lines to
zijiehe166e59a2016-11-29 14:46:46 -0800230 // avoid browser from sticking.
zijiehe21841552016-11-18 20:31:02 -0800231 static const size_t kLineLength = 32768;
232 const char* result_end = result.c_str() + result.length();
Yves Gerey665174f2018-06-19 15:03:05 +0200233 for (const char* it = result.c_str(); it < result_end;
zijiehe21841552016-11-18 20:31:02 -0800234 it += kLineLength) {
235 const size_t max_length = result_end - it;
236 std::cout << std::string(it, std::min(kLineLength, max_length))
237 << std::endl;
238 }
zijiehe166e59a2016-11-29 14:46:46 -0800239 std::cout << "Failed to capture rectangle " << rect.left() << " x "
240 << rect.top() << " - " << rect.right() << " x "
241 << rect.bottom() << " with color ("
242 << static_cast<int>(color.red) << ", "
243 << static_cast<int>(color.green) << ", "
244 << static_cast<int>(color.blue) << ", "
245 << static_cast<int>(color.alpha) << ")" << std::endl;
zijiehe21841552016-11-18 20:31:02 -0800246 ASSERT_TRUE(false) << "ScreenCapturerIntegrationTest may be flaky. "
zijiehee83f4b32016-12-08 11:47:01 -0800247 "Please kindly FYI the broken link to "
248 "zijiehe@chromium.org for investigation. If "
249 "the failure continually happens, but I have "
250 "not responded as quick as expected, disable "
251 "*all* tests in "
zijiehe21841552016-11-18 20:31:02 -0800252 "screen_capturer_integration_test.cc to "
253 "unblock other developers.";
254 }
zijiehe6be0a652016-10-27 16:50:35 -0700255 }
256
257 if (succeeded_capturers == capturers.size()) {
258 break;
259 }
260 }
261
262 ASSERT_EQ(succeeded_capturers, capturers.size());
263 }
264
265 // Expects |capturer| to successfully capture a frame, and returns it.
zijiehe54fd5792016-11-02 14:49:35 -0700266 std::unique_ptr<DesktopFrame> CaptureFrame(DesktopCapturer* capturer) {
zijiehee83f4b32016-12-08 11:47:01 -0800267 for (int i = 0; i < 10; i++) {
268 std::unique_ptr<DesktopFrame> frame;
269 DesktopCapturer::Result result;
270 EXPECT_CALL(callback_, OnCaptureResultPtr(_, _))
271 .WillOnce(SaveCaptureResult(&result, &frame));
272 capturer->CaptureFrame();
Mirko Bonadei6a489f22019-04-09 15:11:12 +0200273 ::testing::Mock::VerifyAndClearExpectations(&callback_);
zijiehee83f4b32016-12-08 11:47:01 -0800274 if (result == DesktopCapturer::Result::SUCCESS) {
275 EXPECT_TRUE(frame);
276 return frame;
277 } else {
278 EXPECT_FALSE(frame);
279 }
280 }
281
282 EXPECT_TRUE(false);
283 return nullptr;
zijiehe6be0a652016-10-27 16:50:35 -0700284 }
285};
286
zijiehe8d1649d2016-12-09 16:00:00 -0800287#if defined(WEBRTC_WIN)
288// ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
289// unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
Henrik Kjellander99f7bfd2016-12-12 08:29:55 +0100290#define MAYBE_CaptureUpdatedRegion DISABLED_CaptureUpdatedRegion
zijiehe8d1649d2016-12-09 16:00:00 -0800291#else
292#define MAYBE_CaptureUpdatedRegion CaptureUpdatedRegion
zijiehee83f4b32016-12-08 11:47:01 -0800293#endif
zijiehe8d1649d2016-12-09 16:00:00 -0800294TEST_F(ScreenCapturerIntegrationTest, MAYBE_CaptureUpdatedRegion) {
295 TestCaptureUpdatedRegion();
zijiehe6be0a652016-10-27 16:50:35 -0700296}
297
zijiehe8d1649d2016-12-09 16:00:00 -0800298#if defined(WEBRTC_WIN)
299// ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
300// unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
Henrik Kjellander99f7bfd2016-12-12 08:29:55 +0100301#define MAYBE_TwoCapturers DISABLED_TwoCapturers
zijiehe8d1649d2016-12-09 16:00:00 -0800302#else
303#define MAYBE_TwoCapturers TwoCapturers
304#endif
305TEST_F(ScreenCapturerIntegrationTest, MAYBE_TwoCapturers) {
zijiehe54fd5792016-11-02 14:49:35 -0700306 std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
zijiehe6be0a652016-10-27 16:50:35 -0700307 SetUp();
308 TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
309}
310
311#if defined(WEBRTC_WIN)
312
Zijie He0cab0852017-08-24 11:30:02 -0700313// Windows cannot capture contents on VMs hosted in GCE. See bug
314// https://bugs.chromium.org/p/webrtc/issues/detail?id=8153.
315TEST_F(ScreenCapturerIntegrationTest,
316 DISABLED_CaptureUpdatedRegionWithDirectxCapturer) {
zijiehe6be0a652016-10-27 16:50:35 -0700317 if (!CreateDirectxCapturer()) {
318 return;
319 }
320
321 TestCaptureUpdatedRegion();
322}
323
Zijie He0cab0852017-08-24 11:30:02 -0700324TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoDirectxCapturers) {
zijiehe6be0a652016-10-27 16:50:35 -0700325 if (!CreateDirectxCapturer()) {
326 return;
327 }
328
zijiehe54fd5792016-11-02 14:49:35 -0700329 std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
zijiehe6be0a652016-10-27 16:50:35 -0700330 RTC_CHECK(CreateDirectxCapturer());
331 TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
332}
333
334TEST_F(ScreenCapturerIntegrationTest,
Zijie He0cab0852017-08-24 11:30:02 -0700335 DISABLED_CaptureUpdatedRegionWithMagnifierCapturer) {
zijiehe166e59a2016-11-29 14:46:46 -0800336 // On Windows 8 or later, magnifier APIs return a frame with a border on test
337 // environment, so disable these tests.
zijiehee83f4b32016-12-08 11:47:01 -0800338 // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844
zijiehe166e59a2016-11-29 14:46:46 -0800339 // TODO(zijiehe): Find the root cause of the border and failure, which cannot
340 // reproduce on my dev machine.
341 if (rtc::IsWindows8OrLater()) {
342 return;
343 }
zijiehe6be0a652016-10-27 16:50:35 -0700344 CreateMagnifierCapturer();
345 TestCaptureUpdatedRegion();
346}
347
Zijie He0cab0852017-08-24 11:30:02 -0700348TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoMagnifierCapturers) {
zijiehe166e59a2016-11-29 14:46:46 -0800349 // On Windows 8 or later, magnifier APIs return a frame with a border on test
350 // environment, so disable these tests.
zijiehee83f4b32016-12-08 11:47:01 -0800351 // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844
zijiehe166e59a2016-11-29 14:46:46 -0800352 // TODO(zijiehe): Find the root cause of the border and failure, which cannot
353 // reproduce on my dev machine.
354 if (rtc::IsWindows8OrLater()) {
355 return;
356 }
zijiehe6be0a652016-10-27 16:50:35 -0700357 CreateMagnifierCapturer();
zijiehe54fd5792016-11-02 14:49:35 -0700358 std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
zijiehe6be0a652016-10-27 16:50:35 -0700359 CreateMagnifierCapturer();
360 TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
361}
362
363TEST_F(ScreenCapturerIntegrationTest,
Zijie He0cab0852017-08-24 11:30:02 -0700364 DISABLED_MaybeCaptureUpdatedRegionWithDirectxCapturer) {
zijiehee83f4b32016-12-08 11:47:01 -0800365 if (!rtc::IsWindows8OrLater()) {
366 // ScreenCapturerWinGdi randomly returns blank screen, the root cause is
367 // still unknown. Bug,
368 // https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
369 // On Windows 7 or early version, MaybeCreateDirectxCapturer() always
370 // creates GDI capturer.
371 return;
372 }
zijiehe6be0a652016-10-27 16:50:35 -0700373 // Even DirectX capturer is not supported in current system, we should be able
374 // to select a usable capturer.
375 MaybeCreateDirectxCapturer();
376 TestCaptureUpdatedRegion();
377}
378
379#endif // defined(WEBRTC_WIN)
380
381} // namespace webrtc