From e820cef5340610b9beebbcb63868743b95b97fcd Mon Sep 17 00:00:00 2001 From: Austin Orion Date: Tue, 15 Sep 2020 11:22:59 -0700 Subject: [PATCH] Begin implementing WGC CaptureFrame This change introduces the design that will allow us to deliver frames synchronously to callers despite the Windows.Graphics.Capture APIs being inherently asynchronous. We achieve this by having WindowCapturerWinWgc create and maintain a WgcCaptureSession object for each window that it is asked to capture a frame for. The capture session object will be the class that actually uses the WGC APIs, and it will store the frames it receives in a frame pool and deliver them via GetMostRecentFrame. The next CL will add the necessary functionality to the WgcCaptureSession class. Bug: webrtc:9273 Change-Id: I44e164f4874503d8ccc8e6a210e74f9c8458f6c4 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184220 Commit-Queue: Austin Orion Reviewed-by: Tommi Cr-Commit-Position: refs/heads/master@{#32240} --- modules/desktop_capture/BUILD.gn | 2 + modules/desktop_capture/desktop_capturer.cc | 15 ++++ modules/desktop_capture/desktop_frame.h | 2 +- .../win/wgc_capture_session.cc | 40 +++++++++ .../desktop_capture/win/wgc_capture_session.h | 48 ++++++++++ .../win/window_capturer_win_wgc.cc | 89 ++++++++++++++++++- .../win/window_capturer_win_wgc.h | 25 +++++- .../desktop_capture/window_capturer_win.cc | 10 +-- rtc_base/win/windows_version.cc | 6 +- rtc_base/win/windows_version.h | 2 + 10 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 modules/desktop_capture/win/wgc_capture_session.cc create mode 100644 modules/desktop_capture/win/wgc_capture_session.h diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 735616b633..6bf8c12e54 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -478,6 +478,8 @@ rtc_library("desktop_capture_generic") { "win/screen_capturer_win_magnifier.h", "win/selected_window_context.cc", "win/selected_window_context.h", + "win/wgc_capture_session.cc", + "win/wgc_capture_session.h", "win/window_capture_utils.cc", "win/window_capture_utils.h", "win/window_capturer_win_gdi.cc", diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc index 61926a6023..42ba4e26e8 100644 --- a/modules/desktop_capture/desktop_capturer.cc +++ b/modules/desktop_capture/desktop_capturer.cc @@ -20,6 +20,13 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer_differ_wrapper.h" +#if defined(WEBRTC_WIN) +#include "modules/desktop_capture/win/window_capturer_win_wgc.h" +#include "rtc_base/win/windows_version.h" + +const bool kUseWinWgcCapturer = false; +#endif // defined(WEBRTC_WIN) + namespace webrtc { DesktopCapturer::~DesktopCapturer() = default; @@ -49,6 +56,14 @@ bool DesktopCapturer::IsOccluded(const DesktopVector& pos) { std::unique_ptr DesktopCapturer::CreateWindowCapturer( const DesktopCaptureOptions& options) { #if defined(WEBRTC_WIN) + // TODO(bugs.webrtc.org/11760): Add a WebRTC field trial (or similar + // mechanism) check here that leads to use of the WGC capturer once it is + // fully implemented. + if (kUseWinWgcCapturer && + rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_RS5) { + return WindowCapturerWinWgc::CreateRawWindowCapturer(options); + } + if (options.allow_cropping_window_capturer()) { return CroppingWindowCapturer::CreateCapturer(options); } diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h index 3a18b7852d..4ee3680670 100644 --- a/modules/desktop_capture/desktop_frame.h +++ b/modules/desktop_capture/desktop_frame.h @@ -29,7 +29,7 @@ const float kStandardDPI = 96.0f; // DesktopFrame represents a video frame captured from the screen. class RTC_EXPORT DesktopFrame { public: - // DesktopFrame objects always hold RGBA data. + // DesktopFrame objects always hold BGRA data. static const int kBytesPerPixel = 4; virtual ~DesktopFrame(); diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc new file mode 100644 index 0000000000..ee55cf6164 --- /dev/null +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/win/wgc_capture_session.h" + +#include + +#include "rtc_base/checks.h" + +using Microsoft::WRL::ComPtr; +namespace webrtc { + +WgcCaptureSession::WgcCaptureSession(ComPtr d3d11_device, + HWND window) + : d3d11_device_(std::move(d3d11_device)), window_(window) {} +WgcCaptureSession::~WgcCaptureSession() = default; + +HRESULT WgcCaptureSession::StartCapture() { + RTC_DCHECK(!is_capture_started_); + RTC_DCHECK(d3d11_device_); + RTC_DCHECK(window_); + + return E_NOTIMPL; +} + +HRESULT WgcCaptureSession::GetMostRecentFrame( + std::unique_ptr* output_frame) { + RTC_DCHECK(is_capture_started_); + + return E_NOTIMPL; +} + +} // namespace webrtc diff --git a/modules/desktop_capture/win/wgc_capture_session.h b/modules/desktop_capture/win/wgc_capture_session.h new file mode 100644 index 0000000000..9f41331c92 --- /dev/null +++ b/modules/desktop_capture/win/wgc_capture_session.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ +#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ + +#include +#include +#include +#include + +#include "modules/desktop_capture/desktop_frame.h" + +namespace webrtc { + +class WgcCaptureSession final { + public: + WgcCaptureSession(Microsoft::WRL::ComPtr d3d11_device, + HWND window); + + // Disallow copy and assign + WgcCaptureSession(const WgcCaptureSession&) = delete; + WgcCaptureSession& operator=(const WgcCaptureSession&) = delete; + + ~WgcCaptureSession(); + + HRESULT StartCapture(); + HRESULT GetMostRecentFrame(std::unique_ptr* output_frame); + bool IsCaptureStarted() const { return is_capture_started_; } + + private: + // A Direct3D11 Device provided by the caller. We use this to create an + // IDirect3DDevice, and also to create textures that will hold the image data. + Microsoft::WRL::ComPtr d3d11_device_; + HWND window_; + bool is_capture_started_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ diff --git a/modules/desktop_capture/win/window_capturer_win_wgc.cc b/modules/desktop_capture/win/window_capturer_win_wgc.cc index 3f64983e0d..30a672d9ef 100644 --- a/modules/desktop_capture/win/window_capturer_win_wgc.cc +++ b/modules/desktop_capture/win/window_capturer_win_wgc.cc @@ -10,9 +10,9 @@ #include "modules/desktop_capture/win/window_capturer_win_wgc.h" -#include +#include -#include "modules/desktop_capture/desktop_capturer.h" +#include "rtc_base/logging.h" namespace webrtc { @@ -37,12 +37,95 @@ void WindowCapturerWinWgc::Start(Callback* callback) { RTC_DCHECK(callback); callback_ = callback; + + // Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many + // parameters are nullptr as the implemention uses defaults that work well for + // us. + HRESULT hr = D3D11CreateDevice( + /*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE, + /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + /*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION, + &d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr); + if (hr == DXGI_ERROR_UNSUPPORTED) { + // If a hardware device could not be created, use WARP which is a high speed + // software device. + hr = D3D11CreateDevice( + /*adapter=*/nullptr, D3D_DRIVER_TYPE_WARP, + /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + /*feature_levels=*/nullptr, /*feature_levels_size=*/0, + D3D11_SDK_VERSION, &d3d11_device_, /*feature_level=*/nullptr, + /*device_context=*/nullptr); + } + + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "Failed to create D3D11Device: " << hr; + } } void WindowCapturerWinWgc::CaptureFrame() { RTC_DCHECK(callback_); - callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + if (!window_) { + RTC_LOG(LS_ERROR) << "Window hasn't been selected"; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + + if (!d3d11_device_) { + RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture."; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + + WgcCaptureSession* capture_session = nullptr; + auto iter = ongoing_captures_.find(window_); + if (iter == ongoing_captures_.end()) { + auto iter_success_pair = ongoing_captures_.emplace( + std::piecewise_construct, std::forward_as_tuple(window_), + std::forward_as_tuple(d3d11_device_, window_)); + if (iter_success_pair.second) { + capture_session = &iter_success_pair.first->second; + } else { + RTC_LOG(LS_ERROR) << "Failed to create new WgcCaptureSession."; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + } else { + capture_session = &iter->second; + } + + HRESULT hr; + if (!capture_session->IsCaptureStarted()) { + hr = capture_session->StartCapture(); + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "Failed to start capture: " << hr; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + } + + std::unique_ptr frame; + hr = capture_session->GetMostRecentFrame(&frame); + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "GetMostRecentFrame failed: " << hr; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + + if (!frame) { + RTC_LOG(LS_WARNING) << "GetMostRecentFrame returned an empty frame."; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, + /*frame=*/nullptr); + return; + } + + callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, + std::move(frame)); } // static diff --git a/modules/desktop_capture/win/window_capturer_win_wgc.h b/modules/desktop_capture/win/window_capturer_win_wgc.h index 6617a2d4d9..7e05b0e541 100644 --- a/modules/desktop_capture/win/window_capturer_win_wgc.h +++ b/modules/desktop_capture/win/window_capturer_win_wgc.h @@ -11,19 +11,22 @@ #ifndef MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_WGC_H_ #define MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_WGC_H_ +#include +#include +#include #include #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/win/wgc_capture_session.h" #include "modules/desktop_capture/win/window_capture_utils.h" namespace webrtc { -class WindowCapturerWinWgc : public DesktopCapturer { +class WindowCapturerWinWgc final : public DesktopCapturer { public: WindowCapturerWinWgc(); - // Disallow copy and assign WindowCapturerWinWgc(const WindowCapturerWinWgc&) = delete; WindowCapturerWinWgc& operator=(const WindowCapturerWinWgc&) = delete; @@ -39,12 +42,26 @@ class WindowCapturerWinWgc : public DesktopCapturer { bool SelectSource(SourceId id) override; private: + // The callback that we deliver frames to, synchronously, before CaptureFrame + // returns. Callback* callback_ = nullptr; - // HWND for the currently selected window or nullptr if window is not - // selected. + // HWND for the currently selected window or nullptr if a window is not + // selected. We may be capturing many other windows, but this is the window + // that we will return a frame for when CaptureFrame is called. HWND window_ = nullptr; + + // This helps us enumerate the list of windows that we can capture. WindowCaptureHelperWin window_capture_helper_; + + // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who + // require one to perform the capture. + Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; + + // A map of all the windows we are capturing and the associated + // WgcCaptureSession. This is where we will get the frames for the window + // from, when requested. + std::map ongoing_captures_; }; } // namespace webrtc diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc index a63a24df58..4bfa09f4d6 100644 --- a/modules/desktop_capture/window_capturer_win.cc +++ b/modules/desktop_capture/window_capturer_win.cc @@ -11,21 +11,13 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/win/window_capturer_win_gdi.h" -#include "modules/desktop_capture/win/window_capturer_win_wgc.h" namespace webrtc { // static std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { - // TODO(bugs.webrtc.org/11760): Add a WebRTC field trial (or similar - // mechanism) and Windows version check here that leads to use of the WGC - // capturer once it is fully implemented. - if (true) { - return WindowCapturerWinGdi::CreateRawWindowCapturer(options); - } else { - return WindowCapturerWinWgc::CreateRawWindowCapturer(options); - } + return WindowCapturerWinGdi::CreateRawWindowCapturer(options); } } // namespace webrtc diff --git a/rtc_base/win/windows_version.cc b/rtc_base/win/windows_version.cc index 2e6c1577ce..42148adeea 100644 --- a/rtc_base/win/windows_version.cc +++ b/rtc_base/win/windows_version.cc @@ -203,8 +203,12 @@ Version MajorMinorBuildToVersion(int major, int minor, int build) { return VERSION_WIN10_RS2; } else if (build < 17134) { return VERSION_WIN10_RS3; - } else { + } else if (build < 17763) { return VERSION_WIN10_RS4; + } else if (build < 18362) { + return VERSION_WIN10_RS5; + } else { + return VERSION_WIN10_19H1; } } else if (major > 6) { RTC_NOTREACHED(); diff --git a/rtc_base/win/windows_version.h b/rtc_base/win/windows_version.h index 1ad319e4cc..33449e2b37 100644 --- a/rtc_base/win/windows_version.h +++ b/rtc_base/win/windows_version.h @@ -43,6 +43,8 @@ enum Version { VERSION_WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063. VERSION_WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299. VERSION_WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134. + VERSION_WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763. + VERSION_WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362. // On edit, update tools\metrics\histograms\enums.xml "WindowsVersion" and // "GpuBlacklistFeatureTestResultsWindows2". VERSION_WIN_LAST, // Indicates error condition.