1 /*
2 * Copyright (c) 2020 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/win/wgc_capturer_win.h"
12
13 #include <string>
14 #include <utility>
15 #include <vector>
16
17 #include "modules/desktop_capture/desktop_capture_options.h"
18 #include "modules/desktop_capture/desktop_capture_types.h"
19 #include "modules/desktop_capture/desktop_capturer.h"
20 #include "modules/desktop_capture/win/test_support/test_window.h"
21 #include "modules/desktop_capture/win/wgc_capture_session.h"
22 #include "modules/desktop_capture/win/window_capture_utils.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/logging.h"
25 #include "rtc_base/task_queue_for_test.h"
26 #include "rtc_base/thread.h"
27 #include "rtc_base/time_utils.h"
28 #include "rtc_base/win/scoped_com_initializer.h"
29 #include "rtc_base/win/windows_version.h"
30 #include "system_wrappers/include/metrics.h"
31 #include "system_wrappers/include/sleep.h"
32 #include "test/gtest.h"
33
34 namespace webrtc {
35 namespace {
36
37 constexpr char kWindowThreadName[] = "wgc_capturer_test_window_thread";
38 constexpr WCHAR kWindowTitle[] = L"WGC Capturer Test Window";
39
40 constexpr char kCapturerImplHistogram[] =
41 "WebRTC.DesktopCapture.Win.DesktopCapturerImpl";
42
43 constexpr char kCapturerResultHistogram[] =
44 "WebRTC.DesktopCapture.Win.WgcCapturerResult";
45 constexpr int kSuccess = 0;
46 constexpr int kSessionStartFailure = 4;
47
48 constexpr char kCaptureSessionResultHistogram[] =
49 "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult";
50 constexpr int kSourceClosed = 1;
51
52 constexpr char kCaptureTimeHistogram[] =
53 "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime";
54
55 // The capturer keeps `kNumBuffers` in its frame pool, so we need to request
56 // that many frames to clear those out. The next frame will have the new size
57 // (if the size has changed) so we will resize the frame pool at this point.
58 // Then, we need to clear any frames that may have delivered to the frame pool
59 // before the resize. Finally, the next frame will be guaranteed to be the new
60 // size.
61 constexpr int kNumCapturesToFlushBuffers =
62 WgcCaptureSession::kNumBuffers * 2 + 1;
63
64 constexpr int kSmallWindowWidth = 200;
65 constexpr int kSmallWindowHeight = 100;
66 constexpr int kMediumWindowWidth = 300;
67 constexpr int kMediumWindowHeight = 200;
68 constexpr int kLargeWindowWidth = 400;
69 constexpr int kLargeWindowHeight = 500;
70
71 // The size of the image we capture is slightly smaller than the actual size of
72 // the window.
73 constexpr int kWindowWidthSubtrahend = 14;
74 constexpr int kWindowHeightSubtrahend = 7;
75
76 // Custom message constants so we can direct our thread to close windows and
77 // quit running.
78 constexpr UINT kDestroyWindow = WM_APP;
79 constexpr UINT kQuitRunning = WM_APP + 1;
80
81 // When testing changes to real windows, sometimes the effects (close or resize)
82 // don't happen immediately, we want to keep trying until we see the effect but
83 // only for a reasonable amount of time.
84 constexpr int kMaxTries = 50;
85
86 } // namespace
87
88 class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
89 public DesktopCapturer::Callback {
90 public:
SetUp()91 void SetUp() override {
92 com_initializer_ =
93 std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
94 EXPECT_TRUE(com_initializer_->Succeeded());
95
96 if (!IsWgcSupported(GetParam())) {
97 RTC_LOG(LS_INFO)
98 << "Skipping WgcCapturerWinTests on unsupported platforms.";
99 GTEST_SKIP();
100 }
101 }
102
SetUpForWindowCapture(int window_width=kMediumWindowWidth,int window_height=kMediumWindowHeight)103 void SetUpForWindowCapture(int window_width = kMediumWindowWidth,
104 int window_height = kMediumWindowHeight) {
105 capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
106 DesktopCaptureOptions::CreateDefault());
107 CreateWindowOnSeparateThread(window_width, window_height);
108 StartWindowThreadMessageLoop();
109 source_id_ = GetTestWindowIdFromSourceList();
110 }
111
SetUpForScreenCapture()112 void SetUpForScreenCapture() {
113 capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
114 DesktopCaptureOptions::CreateDefault());
115 source_id_ = GetScreenIdFromSourceList();
116 }
117
TearDown()118 void TearDown() override {
119 if (window_open_) {
120 CloseTestWindow();
121 }
122 }
123
124 // The window must live on a separate thread so that we can run a message pump
125 // without blocking the test thread. This is necessary if we are interested in
126 // having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more
127 // closely resembles how capture works in the wild.
CreateWindowOnSeparateThread(int window_width,int window_height)128 void CreateWindowOnSeparateThread(int window_width, int window_height) {
129 window_thread_ = rtc::Thread::Create();
130 window_thread_->SetName(kWindowThreadName, nullptr);
131 window_thread_->Start();
132 SendTask(window_thread_.get(), [this, window_width, window_height]() {
133 window_thread_id_ = GetCurrentThreadId();
134 window_info_ =
135 CreateTestWindow(kWindowTitle, window_height, window_width);
136 window_open_ = true;
137
138 while (!IsWindowResponding(window_info_.hwnd)) {
139 RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in "
140 "WgcWindowCaptureTest.";
141 }
142
143 while (!IsWindowValidAndVisible(window_info_.hwnd)) {
144 RTC_LOG(LS_INFO) << "Waiting for test window to be visible in "
145 "WgcWindowCaptureTest.";
146 }
147 });
148
149 ASSERT_TRUE(window_thread_->RunningForTest());
150 ASSERT_FALSE(window_thread_->IsCurrent());
151 }
152
StartWindowThreadMessageLoop()153 void StartWindowThreadMessageLoop() {
154 window_thread_->PostTask([this]() {
155 MSG msg;
156 BOOL gm;
157 while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
158 ::DispatchMessage(&msg);
159 if (msg.message == kDestroyWindow) {
160 DestroyTestWindow(window_info_);
161 }
162 if (msg.message == kQuitRunning) {
163 PostQuitMessage(0);
164 }
165 }
166 });
167 }
168
CloseTestWindow()169 void CloseTestWindow() {
170 ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0);
171 ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0);
172 window_thread_->Stop();
173 window_open_ = false;
174 }
175
GetTestWindowIdFromSourceList()176 DesktopCapturer::SourceId GetTestWindowIdFromSourceList() {
177 // Frequently, the test window will not show up in GetSourceList because it
178 // was created too recently. Since we are confident the window will be found
179 // eventually we loop here until we find it.
180 intptr_t src_id = 0;
181 do {
182 DesktopCapturer::SourceList sources;
183 EXPECT_TRUE(capturer_->GetSourceList(&sources));
184 auto it = std::find_if(
185 sources.begin(), sources.end(),
186 [&](const DesktopCapturer::Source& src) {
187 return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
188 });
189
190 if (it != sources.end())
191 src_id = it->id;
192 } while (src_id != reinterpret_cast<intptr_t>(window_info_.hwnd));
193
194 return src_id;
195 }
196
GetScreenIdFromSourceList()197 DesktopCapturer::SourceId GetScreenIdFromSourceList() {
198 DesktopCapturer::SourceList sources;
199 EXPECT_TRUE(capturer_->GetSourceList(&sources));
200 EXPECT_GT(sources.size(), 0ULL);
201 return sources[0].id;
202 }
203
DoCapture(int num_captures=1)204 void DoCapture(int num_captures = 1) {
205 // Capture the requested number of frames. We expect the first capture to
206 // always succeed. If we're asked for multiple frames, we do expect to see a
207 // a couple dropped frames due to resizing the window.
208 const int max_tries = num_captures == 1 ? 1 : kMaxTries;
209 int success_count = 0;
210 for (int i = 0; success_count < num_captures && i < max_tries; i++) {
211 capturer_->CaptureFrame();
212 if (result_ == DesktopCapturer::Result::ERROR_PERMANENT)
213 break;
214 if (result_ == DesktopCapturer::Result::SUCCESS)
215 success_count++;
216 }
217
218 total_successful_captures_ += success_count;
219 EXPECT_EQ(success_count, num_captures);
220 EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
221 EXPECT_TRUE(frame_);
222 EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSuccess),
223 total_successful_captures_);
224 }
225
ValidateFrame(int expected_width,int expected_height)226 void ValidateFrame(int expected_width, int expected_height) {
227 EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend);
228 EXPECT_EQ(frame_->size().height(),
229 expected_height - kWindowHeightSubtrahend);
230
231 // Verify the buffer contains as much data as it should.
232 int data_length = frame_->stride() * frame_->size().height();
233
234 // The first and last pixel should have the same color because they will be
235 // from the border of the window.
236 // Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit.
237 uint32_t first_pixel = static_cast<uint32_t>(*frame_->data());
238 uint32_t last_pixel = static_cast<uint32_t>(
239 *(frame_->data() + data_length - DesktopFrame::kBytesPerPixel));
240 EXPECT_EQ(first_pixel, last_pixel);
241
242 // Let's also check a pixel from the middle of the content area, which the
243 // test window will paint a consistent color for us to verify.
244 uint8_t* middle_pixel = frame_->data() + (data_length / 2);
245
246 int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4;
247 EXPECT_EQ(*middle_pixel, kTestWindowBValue);
248 middle_pixel += sub_pixel_offset;
249 EXPECT_EQ(*middle_pixel, kTestWindowGValue);
250 middle_pixel += sub_pixel_offset;
251 EXPECT_EQ(*middle_pixel, kTestWindowRValue);
252 middle_pixel += sub_pixel_offset;
253
254 // The window is opaque so we expect 0xFF for the Alpha channel.
255 EXPECT_EQ(*middle_pixel, 0xFF);
256 }
257
258 // DesktopCapturer::Callback interface
259 // The capturer synchronously invokes this method before `CaptureFrame()`
260 // returns.
OnCaptureResult(DesktopCapturer::Result result,std::unique_ptr<DesktopFrame> frame)261 void OnCaptureResult(DesktopCapturer::Result result,
262 std::unique_ptr<DesktopFrame> frame) override {
263 result_ = result;
264 frame_ = std::move(frame);
265 }
266
267 protected:
268 std::unique_ptr<ScopedCOMInitializer> com_initializer_;
269 DWORD window_thread_id_;
270 std::unique_ptr<rtc::Thread> window_thread_;
271 WindowInfo window_info_;
272 intptr_t source_id_;
273 bool window_open_ = false;
274 DesktopCapturer::Result result_;
275 int total_successful_captures_ = 0;
276 std::unique_ptr<DesktopFrame> frame_;
277 std::unique_ptr<DesktopCapturer> capturer_;
278 };
279
TEST_P(WgcCapturerWinTest,SelectValidSource)280 TEST_P(WgcCapturerWinTest, SelectValidSource) {
281 if (GetParam() == CaptureType::kWindow) {
282 SetUpForWindowCapture();
283 } else {
284 SetUpForScreenCapture();
285 }
286
287 EXPECT_TRUE(capturer_->SelectSource(source_id_));
288 }
289
TEST_P(WgcCapturerWinTest,SelectInvalidSource)290 TEST_P(WgcCapturerWinTest, SelectInvalidSource) {
291 if (GetParam() == CaptureType::kWindow) {
292 capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
293 DesktopCaptureOptions::CreateDefault());
294 source_id_ = kNullWindowId;
295 } else {
296 capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
297 DesktopCaptureOptions::CreateDefault());
298 source_id_ = kInvalidScreenId;
299 }
300
301 EXPECT_FALSE(capturer_->SelectSource(source_id_));
302 }
303
TEST_P(WgcCapturerWinTest,Capture)304 TEST_P(WgcCapturerWinTest, Capture) {
305 if (GetParam() == CaptureType::kWindow) {
306 SetUpForWindowCapture();
307 } else {
308 SetUpForScreenCapture();
309 }
310
311 EXPECT_TRUE(capturer_->SelectSource(source_id_));
312
313 capturer_->Start(this);
314 EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram,
315 DesktopCapturerId::kWgcCapturerWin),
316 1);
317
318 DoCapture();
319 EXPECT_GT(frame_->size().width(), 0);
320 EXPECT_GT(frame_->size().height(), 0);
321 }
322
TEST_P(WgcCapturerWinTest,CaptureTime)323 TEST_P(WgcCapturerWinTest, CaptureTime) {
324 if (GetParam() == CaptureType::kWindow) {
325 SetUpForWindowCapture();
326 } else {
327 SetUpForScreenCapture();
328 }
329
330 EXPECT_TRUE(capturer_->SelectSource(source_id_));
331 capturer_->Start(this);
332
333 int64_t start_time;
334 start_time = rtc::TimeNanos();
335 capturer_->CaptureFrame();
336
337 int capture_time_ms =
338 (rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec;
339 EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
340 EXPECT_TRUE(frame_);
341
342 // The test may measure the time slightly differently than the capturer. So we
343 // just check if it's within 5 ms.
344 EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5);
345 EXPECT_GE(
346 metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1);
347 }
348
349 INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
350 WgcCapturerWinTest,
351 ::testing::Values(CaptureType::kWindow,
352 CaptureType::kScreen));
353
TEST(WgcCapturerNoMonitorTest,NoMonitors)354 TEST(WgcCapturerNoMonitorTest, NoMonitors) {
355 ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
356 EXPECT_TRUE(com_initializer.Succeeded());
357 if (HasActiveDisplay()) {
358 RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
359 "systems with no monitors";
360 GTEST_SKIP();
361 }
362
363 // A bug in `CreateForMonitor` prevents screen capture when no displays are
364 // attached.
365 EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
366
367 // A bug in the DWM (Desktop Window Manager) prevents it from providing image
368 // data if there are no displays attached. This was fixed in Windows 11.
369 if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN11)
370 EXPECT_FALSE(IsWgcSupported(CaptureType::kWindow));
371 else
372 EXPECT_TRUE(IsWgcSupported(CaptureType::kWindow));
373 }
374
375 class WgcCapturerMonitorTest : public WgcCapturerWinTest {
376 public:
SetUp()377 void SetUp() {
378 com_initializer_ =
379 std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
380 EXPECT_TRUE(com_initializer_->Succeeded());
381
382 if (!IsWgcSupported(CaptureType::kScreen)) {
383 RTC_LOG(LS_INFO)
384 << "Skipping WgcCapturerWinTests on unsupported platforms.";
385 GTEST_SKIP();
386 }
387 }
388 };
389
TEST_F(WgcCapturerMonitorTest,FocusOnMonitor)390 TEST_F(WgcCapturerMonitorTest, FocusOnMonitor) {
391 SetUpForScreenCapture();
392 EXPECT_TRUE(capturer_->SelectSource(0));
393
394 // You can't set focus on a monitor.
395 EXPECT_FALSE(capturer_->FocusOnSelectedSource());
396 }
397
TEST_F(WgcCapturerMonitorTest,CaptureAllMonitors)398 TEST_F(WgcCapturerMonitorTest, CaptureAllMonitors) {
399 SetUpForScreenCapture();
400 EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
401
402 capturer_->Start(this);
403 DoCapture();
404 EXPECT_GT(frame_->size().width(), 0);
405 EXPECT_GT(frame_->size().height(), 0);
406 }
407
408 class WgcCapturerWindowTest : public WgcCapturerWinTest {
409 public:
SetUp()410 void SetUp() {
411 com_initializer_ =
412 std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
413 EXPECT_TRUE(com_initializer_->Succeeded());
414
415 if (!IsWgcSupported(CaptureType::kWindow)) {
416 RTC_LOG(LS_INFO)
417 << "Skipping WgcCapturerWinTests on unsupported platforms.";
418 GTEST_SKIP();
419 }
420 }
421 };
422
TEST_F(WgcCapturerWindowTest,FocusOnWindow)423 TEST_F(WgcCapturerWindowTest, FocusOnWindow) {
424 capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
425 DesktopCaptureOptions::CreateDefault());
426 window_info_ = CreateTestWindow(kWindowTitle);
427 source_id_ = GetScreenIdFromSourceList();
428
429 EXPECT_TRUE(capturer_->SelectSource(source_id_));
430 EXPECT_TRUE(capturer_->FocusOnSelectedSource());
431
432 HWND hwnd = reinterpret_cast<HWND>(source_id_);
433 EXPECT_EQ(hwnd, ::GetActiveWindow());
434 EXPECT_EQ(hwnd, ::GetForegroundWindow());
435 EXPECT_EQ(hwnd, ::GetFocus());
436 DestroyTestWindow(window_info_);
437 }
438
TEST_F(WgcCapturerWindowTest,SelectMinimizedWindow)439 TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) {
440 SetUpForWindowCapture();
441 MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));
442 EXPECT_FALSE(capturer_->SelectSource(source_id_));
443
444 UnminimizeTestWindow(reinterpret_cast<HWND>(source_id_));
445 EXPECT_TRUE(capturer_->SelectSource(source_id_));
446 }
447
TEST_F(WgcCapturerWindowTest,SelectClosedWindow)448 TEST_F(WgcCapturerWindowTest, SelectClosedWindow) {
449 SetUpForWindowCapture();
450 EXPECT_TRUE(capturer_->SelectSource(source_id_));
451
452 CloseTestWindow();
453 EXPECT_FALSE(capturer_->SelectSource(source_id_));
454 }
455
TEST_F(WgcCapturerWindowTest,UnsupportedWindowStyle)456 TEST_F(WgcCapturerWindowTest, UnsupportedWindowStyle) {
457 // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not
458 // support.
459 window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth,
460 kMediumWindowHeight, WS_EX_TOOLWINDOW);
461 capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
462 DesktopCaptureOptions::CreateDefault());
463 DesktopCapturer::SourceList sources;
464 EXPECT_TRUE(capturer_->GetSourceList(&sources));
465 auto it = std::find_if(
466 sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) {
467 return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
468 });
469
470 // We should not find the window, since we filter for unsupported styles.
471 EXPECT_EQ(it, sources.end());
472 DestroyTestWindow(window_info_);
473 }
474
TEST_F(WgcCapturerWindowTest,IncreaseWindowSizeMidCapture)475 TEST_F(WgcCapturerWindowTest, IncreaseWindowSizeMidCapture) {
476 SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight);
477 EXPECT_TRUE(capturer_->SelectSource(source_id_));
478
479 capturer_->Start(this);
480 DoCapture();
481 ValidateFrame(kSmallWindowWidth, kSmallWindowHeight);
482
483 ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
484 DoCapture(kNumCapturesToFlushBuffers);
485 ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
486
487 ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
488 DoCapture(kNumCapturesToFlushBuffers);
489 ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
490 }
491
TEST_F(WgcCapturerWindowTest,ReduceWindowSizeMidCapture)492 TEST_F(WgcCapturerWindowTest, ReduceWindowSizeMidCapture) {
493 SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight);
494 EXPECT_TRUE(capturer_->SelectSource(source_id_));
495
496 capturer_->Start(this);
497 DoCapture();
498 ValidateFrame(kLargeWindowWidth, kLargeWindowHeight);
499
500 ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
501 DoCapture(kNumCapturesToFlushBuffers);
502 ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
503
504 ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
505 DoCapture(kNumCapturesToFlushBuffers);
506 ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
507 }
508
TEST_F(WgcCapturerWindowTest,MinimizeWindowMidCapture)509 TEST_F(WgcCapturerWindowTest, MinimizeWindowMidCapture) {
510 SetUpForWindowCapture();
511 EXPECT_TRUE(capturer_->SelectSource(source_id_));
512
513 capturer_->Start(this);
514
515 // Minmize the window and capture should continue but return temporary errors.
516 MinimizeTestWindow(window_info_.hwnd);
517 for (int i = 0; i < 5; ++i) {
518 capturer_->CaptureFrame();
519 EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY);
520 }
521
522 // Reopen the window and the capture should continue normally.
523 UnminimizeTestWindow(window_info_.hwnd);
524 DoCapture();
525 // We can't verify the window size here because the test window does not
526 // repaint itself after it is unminimized, but capturing successfully is still
527 // a good test.
528 }
529
TEST_F(WgcCapturerWindowTest,CloseWindowMidCapture)530 TEST_F(WgcCapturerWindowTest, CloseWindowMidCapture) {
531 SetUpForWindowCapture();
532 EXPECT_TRUE(capturer_->SelectSource(source_id_));
533
534 capturer_->Start(this);
535 DoCapture();
536 ValidateFrame(kMediumWindowWidth, kMediumWindowHeight);
537
538 CloseTestWindow();
539
540 // We need to pump our message queue so the Closed event will be delivered to
541 // the capturer's event handler. If we are too early and the Closed event
542 // hasn't arrived yet we should keep trying until the capturer receives it and
543 // stops.
544 auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get());
545 MSG msg;
546 for (int i = 0;
547 wgc_capturer->IsSourceBeingCaptured(source_id_) && i < kMaxTries; ++i) {
548 // Unlike GetMessage, PeekMessage will not hang if there are no messages in
549 // the queue.
550 PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
551 SleepMs(1);
552 }
553
554 EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured(source_id_));
555
556 // The frame pool can buffer `kNumBuffers` frames. We must consume these
557 // and then make one more call to CaptureFrame before we expect to see the
558 // failure.
559 int num_tries = 0;
560 do {
561 capturer_->CaptureFrame();
562 } while (result_ == DesktopCapturer::Result::SUCCESS &&
563 ++num_tries <= WgcCaptureSession::kNumBuffers);
564
565 EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure),
566 1);
567 EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed),
568 1);
569 EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT);
570 }
571
572 } // namespace webrtc
573