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.