Re-initialize the DXGI capturer when the DPI of the monitor changes
I am updating Chrome Remote Desktop to apply a scale factor when using curtain mode (i.e. a loopback RDP session) and I've found that while the changes are applied and the desktop is scaled, DXGI stops producing frames. This is essentially the same issue as crbug.com/1307357 except this issue is occurring when the DPI is changed rather than the desktop size. The fix is to look at the effective DPI for the source being captured (or the primary monitor when capturing the full desktop) and then signaling an environment change when the DPI differs. Bug: webrtc:14894,b:154733991 Change-Id: Id768d4a384434ba59e7396bc919d0ba30d0f6acc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/292791 Reviewed-by: Alexander Cooper <alcooper@chromium.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Joe Downing <joedow@google.com> Cr-Commit-Position: refs/heads/main@{#39305}
This commit is contained in:
parent
04e9354557
commit
60795e8c7a
@ -516,9 +516,11 @@ rtc_library("desktop_capture") {
|
|||||||
libs = [
|
libs = [
|
||||||
"d3d11.lib",
|
"d3d11.lib",
|
||||||
"dxgi.lib",
|
"dxgi.lib",
|
||||||
|
"shcore.lib",
|
||||||
]
|
]
|
||||||
deps += [
|
deps += [
|
||||||
"../../rtc_base:win32",
|
"../../rtc_base:win32",
|
||||||
|
"../../rtc_base/containers:flat_map",
|
||||||
"../../rtc_base/win:create_direct3d_device",
|
"../../rtc_base/win:create_direct3d_device",
|
||||||
"../../rtc_base/win:get_activation_factory",
|
"../../rtc_base/win:get_activation_factory",
|
||||||
"../../rtc_base/win:windows_version",
|
"../../rtc_base/win:windows_version",
|
||||||
|
|||||||
@ -10,28 +10,57 @@
|
|||||||
|
|
||||||
#include "modules/desktop_capture/win/display_configuration_monitor.h"
|
#include "modules/desktop_capture/win/display_configuration_monitor.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
bool DisplayConfigurationMonitor::IsChanged() {
|
bool DisplayConfigurationMonitor::IsChanged(
|
||||||
|
DesktopCapturer::SourceId source_id) {
|
||||||
DesktopRect rect = GetFullscreenRect();
|
DesktopRect rect = GetFullscreenRect();
|
||||||
|
DesktopVector dpi = GetDpiForSourceId(source_id);
|
||||||
|
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
rect_ = rect;
|
rect_ = rect;
|
||||||
|
source_dpis_.emplace(source_id, std::move(dpi));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.equals(rect_)) {
|
if (!source_dpis_.contains(source_id)) {
|
||||||
return false;
|
// If this is the first time we've seen this source_id, use the current DPI
|
||||||
|
// so the monitor does not indicate a change and possibly get reset.
|
||||||
|
source_dpis_.emplace(source_id, dpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
rect_ = rect;
|
bool has_changed = false;
|
||||||
return true;
|
if (!rect.equals(rect_) || !source_dpis_.at(source_id).equals(dpi)) {
|
||||||
|
has_changed = true;
|
||||||
|
rect_ = rect;
|
||||||
|
source_dpis_.emplace(source_id, std::move(dpi));
|
||||||
|
}
|
||||||
|
|
||||||
|
return has_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayConfigurationMonitor::Reset() {
|
void DisplayConfigurationMonitor::Reset() {
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
|
source_dpis_.clear();
|
||||||
|
rect_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopVector DisplayConfigurationMonitor::GetDpiForSourceId(
|
||||||
|
DesktopCapturer::SourceId source_id) {
|
||||||
|
HMONITOR monitor = 0;
|
||||||
|
if (source_id == kFullDesktopScreenId) {
|
||||||
|
// Get a handle to the primary monitor when capturing the full desktop.
|
||||||
|
monitor = MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
|
||||||
|
} else if (!GetHmonitorFromDeviceIndex(source_id, &monitor)) {
|
||||||
|
RTC_LOG(LS_WARNING) << "GetHmonitorFromDeviceIndex failed.";
|
||||||
|
}
|
||||||
|
return GetDpiForMonitor(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -11,7 +11,9 @@
|
|||||||
#ifndef MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
|
#ifndef MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
|
||||||
#define MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
|
#define MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
|
||||||
|
|
||||||
|
#include "modules/desktop_capture/desktop_capturer.h"
|
||||||
#include "modules/desktop_capture/desktop_geometry.h"
|
#include "modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "rtc_base/containers/flat_map.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -20,16 +22,29 @@ namespace webrtc {
|
|||||||
// TODO(zijiehe): Also check for pixel format changes.
|
// TODO(zijiehe): Also check for pixel format changes.
|
||||||
class DisplayConfigurationMonitor {
|
class DisplayConfigurationMonitor {
|
||||||
public:
|
public:
|
||||||
// Checks whether the change of display configuration has happened after last
|
// Checks whether the display configuration has changed since the last time
|
||||||
// IsChanged() call. This function won't return true for the first time after
|
// IsChanged() was called. |source_id| is used to observe changes for a
|
||||||
// constructor or Reset() call.
|
// specific display or all displays if kFullDesktopScreenId is passed in.
|
||||||
bool IsChanged();
|
// Returns false if object was Reset() or if IsChanged() has not been called.
|
||||||
|
bool IsChanged(DesktopCapturer::SourceId source_id);
|
||||||
|
|
||||||
// Resets to the initial state.
|
// Resets to the initial state.
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DesktopVector GetDpiForSourceId(DesktopCapturer::SourceId source_id);
|
||||||
|
|
||||||
|
// Represents the size of the desktop which includes all displays.
|
||||||
DesktopRect rect_;
|
DesktopRect rect_;
|
||||||
|
|
||||||
|
// Tracks the DPI for each display being captured. We need to track for each
|
||||||
|
// display as each one can be configured to use a different DPI which will not
|
||||||
|
// be reflected in calls to get the system DPI.
|
||||||
|
flat_map<DesktopCapturer::SourceId, DesktopVector> source_dpis_;
|
||||||
|
|
||||||
|
// Indicates whether |rect_| and |source_dpis_| have been initialized. This is
|
||||||
|
// used to prevent the monitor instance from signaling 'IsChanged()' before
|
||||||
|
// the initial values have been set.
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -129,10 +129,10 @@ DxgiDuplicatorController::Result DxgiDuplicatorController::DuplicateMonitor(
|
|||||||
return DoDuplicate(frame, monitor_id);
|
return DoDuplicate(frame, monitor_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopVector DxgiDuplicatorController::dpi() {
|
DesktopVector DxgiDuplicatorController::system_dpi() {
|
||||||
MutexLock lock(&mutex_);
|
MutexLock lock(&mutex_);
|
||||||
if (Initialize()) {
|
if (Initialize()) {
|
||||||
return dpi_;
|
return system_dpi_;
|
||||||
}
|
}
|
||||||
return DesktopVector();
|
return DesktopVector();
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ DxgiDuplicatorController::Result DxgiDuplicatorController::DoDuplicate(
|
|||||||
// TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and
|
// TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and
|
||||||
// IDXGIOutputDuplication::GetDesc() can detect the resolution change without
|
// IDXGIOutputDuplication::GetDesc() can detect the resolution change without
|
||||||
// reinitialization.
|
// reinitialization.
|
||||||
if (display_configuration_monitor_.IsChanged()) {
|
if (display_configuration_monitor_.IsChanged(frame->source_id_)) {
|
||||||
Deinitialize();
|
Deinitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +286,8 @@ bool DxgiDuplicatorController::DoInitialize() {
|
|||||||
HDC hdc = GetDC(nullptr);
|
HDC hdc = GetDC(nullptr);
|
||||||
// Use old DPI value if failed.
|
// Use old DPI value if failed.
|
||||||
if (hdc) {
|
if (hdc) {
|
||||||
dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
|
system_dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX),
|
||||||
|
GetDeviceCaps(hdc, LOGPIXELSY));
|
||||||
ReleaseDC(nullptr, hdc);
|
ReleaseDC(nullptr, hdc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +344,7 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
target->set_dpi(dpi_);
|
target->set_dpi(system_dpi_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace webrtc {
|
|||||||
// but a later Duplicate() returns false, this usually means the display mode is
|
// but a later Duplicate() returns false, this usually means the display mode is
|
||||||
// changing. Consumers should retry after a while. (Typically 50 milliseconds,
|
// changing. Consumers should retry after a while. (Typically 50 milliseconds,
|
||||||
// but according to hardware performance, this time may vary.)
|
// but according to hardware performance, this time may vary.)
|
||||||
// The underyling DxgiOutputDuplicators may take an additional reference on the
|
// The underlying DxgiOutputDuplicators may take an additional reference on the
|
||||||
// frame passed in to the Duplicate methods so that they can guarantee delivery
|
// frame passed in to the Duplicate methods so that they can guarantee delivery
|
||||||
// of new frames when requested; since if there have been no updates to the
|
// of new frames when requested; since if there have been no updates to the
|
||||||
// surface, they may be unable to capture a frame.
|
// surface, they may be unable to capture a frame.
|
||||||
@ -49,7 +49,7 @@ class RTC_EXPORT DxgiDuplicatorController {
|
|||||||
public:
|
public:
|
||||||
using Context = DxgiFrameContext;
|
using Context = DxgiFrameContext;
|
||||||
|
|
||||||
// A collection of D3d information we are interested on, which may impact
|
// A collection of D3d information we are interested in, which may impact
|
||||||
// capturer performance or reliability.
|
// capturer performance or reliability.
|
||||||
struct D3dInfo {
|
struct D3dInfo {
|
||||||
// Each video adapter has its own D3D_FEATURE_LEVEL, so this structure
|
// Each video adapter has its own D3D_FEATURE_LEVEL, so this structure
|
||||||
@ -105,7 +105,7 @@ class RTC_EXPORT DxgiDuplicatorController {
|
|||||||
// synchronize them manually. We should find a way to do it.
|
// synchronize them manually. We should find a way to do it.
|
||||||
Result Duplicate(DxgiFrame* frame);
|
Result Duplicate(DxgiFrame* frame);
|
||||||
|
|
||||||
// Captures one monitor and writes into target. `monitor_id` should >= 0. If
|
// Captures one monitor and writes into target. `monitor_id` must be >= 0. If
|
||||||
// `monitor_id` is greater than the total screen count of all the Duplicators,
|
// `monitor_id` is greater than the total screen count of all the Duplicators,
|
||||||
// this function returns false. May retain a reference to `frame`'s underlying
|
// this function returns false. May retain a reference to `frame`'s underlying
|
||||||
// |SharedDesktopFrame|.
|
// |SharedDesktopFrame|.
|
||||||
@ -113,7 +113,7 @@ class RTC_EXPORT DxgiDuplicatorController {
|
|||||||
|
|
||||||
// Returns dpi of current system. Returns an empty DesktopVector if system
|
// Returns dpi of current system. Returns an empty DesktopVector if system
|
||||||
// does not support DXGI based capturer.
|
// does not support DXGI based capturer.
|
||||||
DesktopVector dpi();
|
DesktopVector system_dpi();
|
||||||
|
|
||||||
// Returns the count of screens on the system. These screens can be retrieved
|
// Returns the count of screens on the system. These screens can be retrieved
|
||||||
// by an integer in the range of [0, ScreenCount()). If system does not
|
// by an integer in the range of [0, ScreenCount()). If system does not
|
||||||
@ -172,7 +172,7 @@ class RTC_EXPORT DxgiDuplicatorController {
|
|||||||
// Initialize().
|
// Initialize().
|
||||||
bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||||
|
|
||||||
// Clears all COM components referred by this instance. So next Duplicate()
|
// Clears all COM components referred to by this instance. So next Duplicate()
|
||||||
// call will eventually initialize this instance again.
|
// call will eventually initialize this instance again.
|
||||||
void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||||
|
|
||||||
@ -243,12 +243,12 @@ class RTC_EXPORT DxgiDuplicatorController {
|
|||||||
// a Context instance is always initialized after DxgiDuplicatorController.
|
// a Context instance is always initialized after DxgiDuplicatorController.
|
||||||
int identity_ RTC_GUARDED_BY(mutex_) = 0;
|
int identity_ RTC_GUARDED_BY(mutex_) = 0;
|
||||||
DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_);
|
DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_);
|
||||||
DesktopVector dpi_ RTC_GUARDED_BY(mutex_);
|
DesktopVector system_dpi_ RTC_GUARDED_BY(mutex_);
|
||||||
std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_);
|
std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_);
|
||||||
D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_);
|
D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_);
|
||||||
DisplayConfigurationMonitor display_configuration_monitor_
|
DisplayConfigurationMonitor display_configuration_monitor_
|
||||||
RTC_GUARDED_BY(mutex_);
|
RTC_GUARDED_BY(mutex_);
|
||||||
// A number to indicate how many succeeded duplications have been performed.
|
// A number to indicate how many successful duplications have been performed.
|
||||||
uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0;
|
uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
||||||
|
|
||||||
|
#include <shellscalingapi.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -145,6 +146,28 @@ DesktopRect GetFullscreenRect() {
|
|||||||
GetSystemMetrics(SM_CYVIRTUALSCREEN));
|
GetSystemMetrics(SM_CYVIRTUALSCREEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DesktopVector GetDpiForMonitor(HMONITOR monitor) {
|
||||||
|
UINT dpi_x, dpi_y;
|
||||||
|
// MDT_EFFECTIVE_DPI includes the scale factor as well as the system DPI.
|
||||||
|
HRESULT hr = ::GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
return {static_cast<INT>(dpi_x), static_cast<INT>(dpi_y)};
|
||||||
|
}
|
||||||
|
RTC_LOG_GLE_EX(LS_WARNING, hr) << "GetDpiForMonitor() failed";
|
||||||
|
|
||||||
|
// If we can't get the per-monitor DPI, then return the system DPI.
|
||||||
|
HDC hdc = GetDC(nullptr);
|
||||||
|
if (hdc) {
|
||||||
|
DesktopVector dpi{GetDeviceCaps(hdc, LOGPIXELSX),
|
||||||
|
GetDeviceCaps(hdc, LOGPIXELSY)};
|
||||||
|
ReleaseDC(nullptr, hdc);
|
||||||
|
return dpi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything fails, then return the default DPI for Windows.
|
||||||
|
return {96, 96};
|
||||||
|
}
|
||||||
|
|
||||||
DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
|
DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
|
||||||
const std::wstring& device_key) {
|
const std::wstring& device_key) {
|
||||||
if (screen == kFullDesktopScreenId) {
|
if (screen == kFullDesktopScreenId) {
|
||||||
|
|||||||
@ -54,6 +54,10 @@ bool IsMonitorValid(HMONITOR monitor);
|
|||||||
// primary display's top-left. On failure, returns an empty rect.
|
// primary display's top-left. On failure, returns an empty rect.
|
||||||
DesktopRect GetMonitorRect(HMONITOR monitor);
|
DesktopRect GetMonitorRect(HMONITOR monitor);
|
||||||
|
|
||||||
|
// Returns the DPI for the specified monitor. On failure, returns the system DPI
|
||||||
|
// or the Windows default DPI (96x96) if the system DPI can't be retrieved.
|
||||||
|
DesktopVector GetDpiForMonitor(HMONITOR monitor);
|
||||||
|
|
||||||
// Returns true if `screen` is a valid screen. The screen device key is
|
// Returns true if `screen` is a valid screen. The screen device key is
|
||||||
// returned through `device_key` if the screen is valid. The device key can be
|
// returned through `device_key` if the screen is valid. The device key can be
|
||||||
// used in GetScreenRect to verify the screen matches the previously obtained
|
// used in GetScreenRect to verify the screen matches the previously obtained
|
||||||
|
|||||||
@ -160,7 +160,7 @@ void ScreenCapturerWinGdi::PrepareCaptureResources() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the display configurations have changed then recreate GDI resources.
|
// If the display configurations have changed then recreate GDI resources.
|
||||||
if (display_configuration_monitor_.IsChanged()) {
|
if (display_configuration_monitor_.IsChanged(kFullDesktopScreenId)) {
|
||||||
if (desktop_dc_) {
|
if (desktop_dc_) {
|
||||||
ReleaseDC(NULL, desktop_dc_);
|
ReleaseDC(NULL, desktop_dc_);
|
||||||
desktop_dc_ = nullptr;
|
desktop_dc_ = nullptr;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user