
This change refactors WgcWindowCapturer into WgcCapturerWin, a source agnostic capturer, and finishes the implementation to enable both window and screen capture. This CL depends on another which must complete first: 196622: Add ability to load CreateDirect3DDeviceFromDXGIDevice from d3d11.dll | https://webrtc-review.googlesource.com/c/src/+/196622 This feature remains off by default behind a build flag, due to it adding a depency on the Win10 SDK vv10.0.19041 which not all consumers of WebRTC have upgraded to. A follow up change later will enable the rtc_enable_win_wgc build flag, but for now it should remain off. The basic operation of this class is as follows: Consumers call either WgcCapturerWin::CreateRawWindowCapturer or CreateRawScreenCapturer to receive a correctly initialized WgcCapturerWin object suitable for the desired source type. Callers then indicate via SelectSource and a SourceId the desired capture target, and the capturer creates an appropriate WgcCaptureSource for the correct type (window or screen) using the WgcCaptureSourceFactory supplied at construction. Next, callers request frames for the currently selected source and the capturer then creates a WgcCaptureSession and stores it in a map for more efficient capture of multiple sources. The WgcCaptureSession is supplied with a GraphicsCaptureItem created by the WgcCaptureSource. It uses this item to create a Direct3D11CaptureFramePool and create and start a GraphicsCaptureSession. Once started, captured frames will begin to be deposited into the FramePool. Typically, one would listen for the FrameArrived event and process the frame then, but due to the synchronous nature of the DesktopCapturer interface, and to avoid a more complicated multi- threaded architecture we ignore the FrameArrived event. Instead, we wait for a request for a frame from the caller, then we check the FramePool for a frame, and process it on demand. Processing a frame involves moving the image data from an ID3D11Texture2D stored in the GPU into a texture that is accessible from the CPU, and then copying the data into the new WgcDesktopFrame class. This copy is necessary as otherwise we would need to manage the lifetimes of the CaptureFrame and ID3D11Texture2D objects, lest the buffer be invalidated. Once we've copied the data and returned it to the caller, we can unmap the texture and exit the scope of the GetFrame method, which will destruct the CaptureFrame object. At this point, the CaptureSession will begin capturing a new frame, and will soon deposit it into the FramePool and we can repeat. Bug: webrtc:9273 Change-Id: I02263c4fd587df652b04d5267fad8965330d0f5b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/200161 Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Reviewed-by: Guido Urdaneta <guidou@webrtc.org> Commit-Queue: Austin Orion <auorion@microsoft.com> Cr-Commit-Position: refs/heads/master@{#33083}
150 lines
4.9 KiB
C++
150 lines
4.9 KiB
C++
/*
|
|
* Copyright (c) 2014 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/screen_capture_utils.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "modules/desktop_capture/desktop_capturer.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/string_utils.h"
|
|
#include "rtc_base/win32.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
BOOL CALLBACK GetMonitorListHandler(HMONITOR monitor,
|
|
HDC hdc,
|
|
LPRECT rect,
|
|
LPARAM data) {
|
|
auto monitor_list = reinterpret_cast<DesktopCapturer::SourceList*>(data);
|
|
|
|
// Get the name of the monitor.
|
|
MONITORINFOEXA monitor_info;
|
|
monitor_info.cbSize = sizeof(MONITORINFOEXA);
|
|
if (!GetMonitorInfoA(monitor, &monitor_info))
|
|
return FALSE;
|
|
|
|
DesktopCapturer::Source monitor_source;
|
|
monitor_source.id = reinterpret_cast<intptr_t>(monitor);
|
|
monitor_source.title = monitor_info.szDevice;
|
|
monitor_list->push_back(monitor_source);
|
|
return TRUE;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// |monitors| is populated with HMONITOR values for each display monitor found.
|
|
// This is in contrast to |GetScreenList| which returns the display indices.
|
|
bool GetMonitorList(DesktopCapturer::SourceList* monitors) {
|
|
RTC_DCHECK_EQ(monitors->size(), 0U);
|
|
// |EnumDisplayMonitors| accepts a display context and a rectangle, which
|
|
// allows us to specify a certain region and return only the monitors that
|
|
// intersect that region. We, however, want all the monitors, so we pass in
|
|
// NULL parameters.
|
|
return EnumDisplayMonitors(/*hdc=*/NULL, /*clip_rect=*/NULL,
|
|
GetMonitorListHandler,
|
|
reinterpret_cast<LPARAM>(monitors));
|
|
}
|
|
|
|
bool GetScreenList(DesktopCapturer::SourceList* screens,
|
|
std::vector<std::string>* device_names /* = nullptr */) {
|
|
RTC_DCHECK_EQ(screens->size(), 0U);
|
|
if (device_names) {
|
|
RTC_DCHECK_EQ(device_names->size(), 0U);
|
|
}
|
|
|
|
BOOL enum_result = TRUE;
|
|
for (int device_index = 0;; ++device_index) {
|
|
DISPLAY_DEVICEW device;
|
|
device.cb = sizeof(device);
|
|
enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);
|
|
|
|
// |enum_result| is 0 if we have enumerated all devices.
|
|
if (!enum_result)
|
|
break;
|
|
|
|
// We only care about active displays.
|
|
if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE))
|
|
continue;
|
|
|
|
screens->push_back({device_index, std::string()});
|
|
if (device_names) {
|
|
device_names->push_back(rtc::ToUtf8(device.DeviceName));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IsMonitorValid(DesktopCapturer::SourceId monitor) {
|
|
MONITORINFO monitor_info;
|
|
monitor_info.cbSize = sizeof(MONITORINFO);
|
|
return GetMonitorInfoA(reinterpret_cast<HMONITOR>(monitor), &monitor_info);
|
|
}
|
|
|
|
bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key) {
|
|
if (screen == kFullDesktopScreenId) {
|
|
*device_key = L"";
|
|
return true;
|
|
}
|
|
|
|
DISPLAY_DEVICEW device;
|
|
device.cb = sizeof(device);
|
|
BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0);
|
|
if (enum_result)
|
|
*device_key = device.DeviceKey;
|
|
|
|
return !!enum_result;
|
|
}
|
|
|
|
DesktopRect GetFullscreenRect() {
|
|
return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
|
|
GetSystemMetrics(SM_YVIRTUALSCREEN),
|
|
GetSystemMetrics(SM_CXVIRTUALSCREEN),
|
|
GetSystemMetrics(SM_CYVIRTUALSCREEN));
|
|
}
|
|
|
|
DesktopRect GetScreenRect(DesktopCapturer::SourceId screen,
|
|
const std::wstring& device_key) {
|
|
if (screen == kFullDesktopScreenId) {
|
|
return GetFullscreenRect();
|
|
}
|
|
|
|
DISPLAY_DEVICEW device;
|
|
device.cb = sizeof(device);
|
|
BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0);
|
|
if (!result)
|
|
return DesktopRect();
|
|
|
|
// Verifies the device index still maps to the same display device, to make
|
|
// sure we are capturing the same device when devices are added or removed.
|
|
// DeviceKey is documented as reserved, but it actually contains the registry
|
|
// key for the device and is unique for each monitor, while DeviceID is not.
|
|
if (device_key != device.DeviceKey)
|
|
return DesktopRect();
|
|
|
|
DEVMODEW device_mode;
|
|
device_mode.dmSize = sizeof(device_mode);
|
|
device_mode.dmDriverExtra = 0;
|
|
result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS,
|
|
&device_mode, 0);
|
|
if (!result)
|
|
return DesktopRect();
|
|
|
|
return DesktopRect::MakeXYWH(
|
|
device_mode.dmPosition.x, device_mode.dmPosition.y,
|
|
device_mode.dmPelsWidth, device_mode.dmPelsHeight);
|
|
}
|
|
|
|
} // namespace webrtc
|