Update IsMonitorValid to return false when no displays are found.
Versions of Windows before Win11 will crash when `CreateForMonitor` is called, but the system has no attached displays. This can be avoided by adding a check to ensure at least one display is found before we return true in `IsMonitorValid`. Previously we would early return `true` if the "monitor" we were checking was the `kFullDesktopScreenId`. Bug: chromium:1316478 Change-Id: I2562fe3834db574cf3706ee1d604472ac03f9ff3 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258920 Reviewed-by: Alexander Cooper <alcooper@chromium.org> Commit-Queue: Austin Orion <auorion@microsoft.com> Cr-Commit-Position: refs/heads/main@{#36555}
This commit is contained in:
parent
d229326578
commit
740d704079
@ -24,6 +24,14 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool HasActiveDisplay() {
|
||||
DesktopCapturer::SourceList screens;
|
||||
if (!GetScreenList(&screens))
|
||||
return false;
|
||||
|
||||
return screens.size() >= 1;
|
||||
}
|
||||
|
||||
bool GetScreenList(DesktopCapturer::SourceList* screens,
|
||||
std::vector<std::string>* device_names /* = nullptr */) {
|
||||
RTC_DCHECK_EQ(screens->size(), 0U);
|
||||
@ -91,6 +99,12 @@ bool IsMonitorValid(const HMONITOR monitor) {
|
||||
// An HMONITOR of 0 refers to a virtual monitor that spans all physical
|
||||
// monitors.
|
||||
if (monitor == 0) {
|
||||
// There is a bug in a Windows OS API that causes a crash when capturing if
|
||||
// there are no active displays. We must ensure there is an active display
|
||||
// before returning true.
|
||||
if (!HasActiveDisplay())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,9 @@ WEBRTC_DECLARE_HANDLE(HMONITOR);
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Returns true if the system has at least one active display.
|
||||
bool HasActiveDisplay();
|
||||
|
||||
// Output the list of active screens into `screens`. Returns true if succeeded,
|
||||
// or false if it fails to enumerate the display devices. If the `device_names`
|
||||
// is provided, it will be filled with the DISPLAY_DEVICE.DeviceName in UTF-8
|
||||
|
||||
@ -35,7 +35,8 @@ TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) {
|
||||
DesktopCapturer::SourceList screens;
|
||||
ASSERT_TRUE(GetScreenList(&screens));
|
||||
if (screens.size() == 0) {
|
||||
RTC_LOG(LS_INFO) << "Skip screen capture test on systems with no monitors.";
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Skip ScreenCaptureUtilsTest on systems with no monitors.";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
@ -45,12 +46,33 @@ TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) {
|
||||
}
|
||||
|
||||
TEST(ScreenCaptureUtilsTest, FullScreenDeviceIndexToHmonitor) {
|
||||
if (!HasActiveDisplay()) {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Skip ScreenCaptureUtilsTest on systems with no monitors.";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
HMONITOR hmonitor;
|
||||
ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
|
||||
ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
|
||||
ASSERT_TRUE(IsMonitorValid(hmonitor));
|
||||
}
|
||||
|
||||
TEST(ScreenCaptureUtilsTest, NoMonitors) {
|
||||
if (HasActiveDisplay()) {
|
||||
RTC_LOG(LS_INFO) << "Skip ScreenCaptureUtilsTest designed specifically for "
|
||||
"systems with no monitors";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
HMONITOR hmonitor;
|
||||
ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
|
||||
ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
|
||||
|
||||
// The monitor should be invalid since the system has no attached displays.
|
||||
ASSERT_FALSE(IsMonitorValid(hmonitor));
|
||||
}
|
||||
|
||||
TEST(ScreenCaptureUtilsTest, InvalidDeviceIndexToHmonitor) {
|
||||
HMONITOR hmonitor;
|
||||
ASSERT_FALSE(GetHmonitorFromDeviceIndex(kInvalidScreenId, &hmonitor));
|
||||
|
||||
@ -163,6 +163,12 @@ HRESULT WgcScreenSource::CreateCaptureItem(
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// Ensure the monitor is still valid (hasn't disconnected) before trying to
|
||||
// create the item. On versions of Windows before Win11, `CreateForMonitor`
|
||||
// will crash if no displays are connected.
|
||||
if (!IsMonitorValid(*hmonitor_))
|
||||
return E_ABORT;
|
||||
|
||||
ComPtr<WGC::IGraphicsCaptureItem> item;
|
||||
hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item));
|
||||
if (FAILED(hr))
|
||||
|
||||
@ -57,10 +57,15 @@ void RecordWgcCapturerResult(WgcCapturerResult error) {
|
||||
} // namespace
|
||||
|
||||
bool IsWgcSupported(CaptureType capture_type) {
|
||||
// A bug in the WGC API `CreateForMonitor` was fixed in 20H1.
|
||||
if (capture_type == CaptureType::kScreen &&
|
||||
rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1) {
|
||||
return false;
|
||||
if (capture_type == CaptureType::kScreen) {
|
||||
// A bug in the WGC API `CreateForMonitor` was fixed in 20H1.
|
||||
if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1)
|
||||
return false;
|
||||
|
||||
// There is another bug in `CreateForMonitor` that causes a crash if there
|
||||
// are no active displays.
|
||||
if (!HasActiveDisplay())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ResolveCoreWinRTDelayload())
|
||||
|
||||
@ -361,6 +361,18 @@ TEST_F(WgcCapturerWinTest, CaptureAllMonitors) {
|
||||
EXPECT_GT(frame_->size().height(), 0);
|
||||
}
|
||||
|
||||
TEST_F(WgcCapturerWinTest, NoMonitors) {
|
||||
if (HasActiveDisplay()) {
|
||||
RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
|
||||
"systems with no monitors";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// A bug in `CreateForMonitor` prevents screen capture when no displays are
|
||||
// attached.
|
||||
EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
|
||||
}
|
||||
|
||||
// Window specific tests.
|
||||
TEST_F(WgcCapturerWinTest, FocusOnWindow) {
|
||||
capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user