Query the system for WGC support instead of relying on version checks.

The Windows 2019 Enterprise SKU is based on RS5 but is missing support
for WGC. This changes adds a function IsWgcSupported which will check
that the API is actually present, which should be more robust than just
performing a version check.

Bug: webrtc:13932
Change-Id: Ib6a2278f316b9b86671df77fd37468c79564d655
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/258163
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Austin Orion <auorion@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#36506}
This commit is contained in:
Austin Orion 2022-04-08 09:09:26 -07:00 committed by WebRTC LUCI CQ
parent 385b6c5460
commit 2e1ac58631
6 changed files with 100 additions and 19 deletions

View File

@ -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.

View File

@ -54,8 +54,7 @@ bool DesktopCapturer::IsOccluded(const DesktopVector& pos) {
std::unique_ptr<DesktopCapturer> 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> DesktopCapturer::CreateWindowCapturer(
std::unique_ptr<DesktopCapturer> 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)

View File

@ -144,7 +144,7 @@ HRESULT WgcCaptureSession::StartCapture() {
ComPtr<WGC::IDirect3D11CaptureFramePoolStatics> frame_pool_statics;
hr = GetActivationFactory<
ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics,
WGC::IDirect3D11CaptureFramePoolStatics,
RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>(
&frame_pool_statics);
if (FAILED(hr)) {

View File

@ -10,6 +10,9 @@
#include "modules/desktop_capture/win/wgc_capturer_win.h"
#include <windows.foundation.metadata.h>
#include <windows.graphics.capture.h>
#include <utility>
#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<ABI::Windows::Foundation::Metadata::IApiInformationStatics>
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<WGC::IGraphicsCaptureSessionStatics> 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<WgcCaptureSourceFactory> source_factory,
std::unique_ptr<SourceEnumerator> source_enumerator,

View File

@ -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.

View File

@ -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<CaptureType>,
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>(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<CaptureType>,
};
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));