diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h index bc26db7cc4..f2aad8fd6f 100644 --- a/modules/desktop_capture/desktop_capture_types.h +++ b/modules/desktop_capture/desktop_capture_types.h @@ -15,6 +15,8 @@ namespace webrtc { +enum class CaptureType { kWindow, kScreen }; + // Type used to identify windows on the desktop. Values are platform-specific: // - On Windows: HWND cast to intptr_t. // - On Linux (with X11): X11 Window (unsigned long) type cast to intptr_t. diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc index 9e6b99ac58..f4676b7fe2 100644 --- a/modules/desktop_capture/desktop_capturer.cc +++ b/modules/desktop_capture/desktop_capturer.cc @@ -54,8 +54,7 @@ bool DesktopCapturer::IsOccluded(const DesktopVector& pos) { std::unique_ptr DesktopCapturer::CreateWindowCapturer( const DesktopCaptureOptions& options) { #if defined(RTC_ENABLE_WIN_WGC) - if (options.allow_wgc_capturer() && - rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_RS5) { + if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kWindow)) { return WgcCapturerWin::CreateRawWindowCapturer(options); } #endif // defined(RTC_ENABLE_WIN_WGC) @@ -78,8 +77,7 @@ std::unique_ptr DesktopCapturer::CreateWindowCapturer( std::unique_ptr DesktopCapturer::CreateScreenCapturer( const DesktopCaptureOptions& options) { #if defined(RTC_ENABLE_WIN_WGC) - if (options.allow_wgc_capturer() && - rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_20H1) { + if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kScreen)) { return WgcCapturerWin::CreateRawScreenCapturer(options); } #endif // defined(RTC_ENABLE_WIN_WGC) diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc index b6b566b3ee..d4ab0b03d2 100644 --- a/modules/desktop_capture/win/wgc_capture_session.cc +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -144,7 +144,7 @@ HRESULT WgcCaptureSession::StartCapture() { ComPtr frame_pool_statics; hr = GetActivationFactory< - ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics, + WGC::IDirect3D11CaptureFramePoolStatics, RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>( &frame_pool_statics); if (FAILED(hr)) { diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc index 35d7bd1cec..dfe3577d53 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.cc +++ b/modules/desktop_capture/win/wgc_capturer_win.cc @@ -10,6 +10,9 @@ #include "modules/desktop_capture/win/wgc_capturer_win.h" +#include +#include + #include #include "modules/desktop_capture/desktop_capture_metrics_helper.h" @@ -17,6 +20,9 @@ #include "modules/desktop_capture/win/wgc_desktop_frame.h" #include "rtc_base/logging.h" #include "rtc_base/time_utils.h" +#include "rtc_base/win/get_activation_factory.h" +#include "rtc_base/win/hstring.h" +#include "rtc_base/win/windows_version.h" #include "system_wrappers/include/metrics.h" namespace WGC = ABI::Windows::Graphics::Capture; @@ -26,6 +32,11 @@ namespace webrtc { namespace { +const wchar_t kWgcSessionType[] = + L"Windows.Graphics.Capture.GraphicsCaptureSession"; +const wchar_t kApiContract[] = L"Windows.Foundation.UniversalApiContract"; +const UINT16 kRequiredApiContractVersion = 8; + enum class WgcCapturerResult { kSuccess = 0, kNoDirect3dDevice = 1, @@ -45,6 +56,65 @@ 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 (!ResolveCoreWinRTDelayload()) + return false; + + ComPtr + api_info_statics; + HRESULT hr = GetActivationFactory< + ABI::Windows::Foundation::Metadata::IApiInformationStatics, + RuntimeClass_Windows_Foundation_Metadata_ApiInformation>( + &api_info_statics); + if (FAILED(hr)) + return false; + + HSTRING api_contract; + hr = webrtc::CreateHstring(kApiContract, wcslen(kApiContract), &api_contract); + if (FAILED(hr)) + return false; + + boolean is_api_present; + hr = api_info_statics->IsApiContractPresentByMajor( + api_contract, kRequiredApiContractVersion, &is_api_present); + webrtc::DeleteHstring(api_contract); + if (FAILED(hr) || !is_api_present) + return false; + + HSTRING wgc_session_type; + hr = webrtc::CreateHstring(kWgcSessionType, wcslen(kWgcSessionType), + &wgc_session_type); + if (FAILED(hr)) + return false; + + boolean is_type_present; + hr = api_info_statics->IsTypePresent(wgc_session_type, &is_type_present); + webrtc::DeleteHstring(wgc_session_type); + if (FAILED(hr) || !is_type_present) + return false; + + ComPtr capture_session_statics; + hr = GetActivationFactory< + WGC::IGraphicsCaptureSessionStatics, + RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession>( + &capture_session_statics); + if (FAILED(hr)) + return false; + + boolean is_supported; + hr = capture_session_statics->IsSupported(&is_supported); + if (FAILED(hr) || !is_supported) + return false; + + return true; +} + WgcCapturerWin::WgcCapturerWin( std::unique_ptr source_factory, std::unique_ptr source_enumerator, diff --git a/modules/desktop_capture/win/wgc_capturer_win.h b/modules/desktop_capture/win/wgc_capturer_win.h index 0eef5283c7..ba212b6e69 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.h +++ b/modules/desktop_capture/win/wgc_capturer_win.h @@ -26,6 +26,9 @@ namespace webrtc { +// Checks if the WGC API is present and supported on the system. +bool IsWgcSupported(CaptureType capture_type); + // WgcCapturerWin is initialized with an implementation of this base class, // which it uses to find capturable sources of a particular type. This way, // WgcCapturerWin can remain source-agnostic. diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index fe18577ae9..18edf451c5 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -67,23 +67,24 @@ const UINT kNoOp = WM_APP; const UINT kDestroyWindow = WM_APP + 1; const UINT kQuitRunning = WM_APP + 2; -enum CaptureType { kWindowCapture = 0, kScreenCapture = 1 }; - } // namespace class WgcCapturerWinTest : public ::testing::TestWithParam, public DesktopCapturer::Callback { public: void SetUp() override { - if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) { - RTC_LOG(LS_INFO) - << "Skipping WgcCapturerWinTests on Windows versions < RS5."; - GTEST_SKIP(); - } - com_initializer_ = std::make_unique(ScopedCOMInitializer::kMTA); EXPECT_TRUE(com_initializer_->Succeeded()); + + // Most tests (except `CaptureAllMonitors`) avoid the bug in screen capture, + // so we check support for window capture so these tests can run on more + // systems. + if (!IsWgcSupported(CaptureType::kWindow)) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } } void SetUpForWindowCapture(int window_width = kMediumWindowWidth, @@ -260,7 +261,7 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, }; TEST_P(WgcCapturerWinTest, SelectValidSource) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -270,7 +271,7 @@ TEST_P(WgcCapturerWinTest, SelectValidSource) { } TEST_P(WgcCapturerWinTest, SelectInvalidSource) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { capturer_ = WgcCapturerWin::CreateRawWindowCapturer( DesktopCaptureOptions::CreateDefault()); source_id_ = kNullWindowId; @@ -284,7 +285,7 @@ TEST_P(WgcCapturerWinTest, SelectInvalidSource) { } TEST_P(WgcCapturerWinTest, Capture) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -303,7 +304,7 @@ TEST_P(WgcCapturerWinTest, Capture) { } TEST_P(WgcCapturerWinTest, CaptureTime) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -331,8 +332,8 @@ TEST_P(WgcCapturerWinTest, CaptureTime) { INSTANTIATE_TEST_SUITE_P(SourceAgnostic, WgcCapturerWinTest, - ::testing::Values(CaptureType::kWindowCapture, - CaptureType::kScreenCapture)); + ::testing::Values(CaptureType::kWindow, + CaptureType::kScreen)); // Monitor specific tests. TEST_F(WgcCapturerWinTest, FocusOnMonitor) { @@ -344,6 +345,13 @@ TEST_F(WgcCapturerWinTest, FocusOnMonitor) { } TEST_F(WgcCapturerWinTest, CaptureAllMonitors) { + // Trying to capture all monitors causes a crash on Windows versions <20H1. + if (!IsWgcSupported(CaptureType::kScreen)) { + RTC_LOG(LS_INFO) + << "Skipping CaptureAllMonitors test on unsupported platforms."; + GTEST_SKIP(); + } + SetUpForScreenCapture(); EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));