Files
platform-external-webrtc/modules/desktop_capture/win/screen_capture_utils.cc
Austin Orion 2aad81259e Refactor and implement WgcCapturerWin, a source agnostic capturer.
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}
2021-01-27 21:17:19 +00:00

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