Get DeviceScaleFactor for the captured monitor/screen
Accesses DeviceScaleFactor using the windows API
GetScaleFactorForMonitor and adds it to the DesktopFrame. In a follow-up
CL, this value is propagated to
DesktopCaptureDevice::Core::OnCaptureResult where it is added to the
frame metadata.
In a follow-up CL, add RegisterScaleChangeEvent to get notified whenever
the device scale factor changes.
Design doc: go/expose-captured-surface-resolution
Bug: chromium:383946052
Change-Id: I363af33c569419d95ddf31a0cc2f9cecf6fb0c7b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374344
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Commit-Queue: Palak Agarwal <agpalak@google.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43827}
diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc
index be61196..ace60e2 100644
--- a/modules/desktop_capture/desktop_frame.cc
+++ b/modules/desktop_capture/desktop_frame.cc
@@ -134,6 +134,7 @@
set_top_left(other.top_left());
set_icc_profile(other.icc_profile());
set_may_contain_cursor(other.may_contain_cursor());
+ set_device_scale_factor(other.device_scale_factor());
}
void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) {
@@ -144,6 +145,7 @@
set_top_left(other->top_left());
set_icc_profile(other->icc_profile());
set_may_contain_cursor(other->may_contain_cursor());
+ set_device_scale_factor(other->device_scale_factor());
}
bool DesktopFrame::FrameDataIsBlack() const {
diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h
index 35ac8e2..03cc89c 100644
--- a/modules/desktop_capture/desktop_frame.h
+++ b/modules/desktop_capture/desktop_frame.h
@@ -74,6 +74,12 @@
const DesktopVector& dpi() const { return dpi_; }
void set_dpi(const DesktopVector& dpi) { dpi_ = dpi; }
+ std::optional<int32_t> device_scale_factor() const {
+ return device_scale_factor_;
+ }
+ void set_device_scale_factor(std::optional<int32_t> device_scale_factor) {
+ device_scale_factor_ = device_scale_factor;
+ }
// Indicates if this frame may have the mouse cursor in it. Capturers that
// support cursor capture may set this to true. If the cursor was
// outside of the captured area, this may be true even though the cursor is
@@ -172,6 +178,10 @@
int64_t capture_time_ms_;
uint32_t capturer_id_;
std::vector<uint8_t> icc_profile_;
+ // Currently only used on Windows. It stores the device scale factor of the
+ // captured surface and has distinct values possible in the range of
+ // [100,500].
+ std::optional<int32_t> device_scale_factor_;
};
// A DesktopFrame that stores data in the heap.
diff --git a/modules/desktop_capture/desktop_frame_unittest.cc b/modules/desktop_capture/desktop_frame_unittest.cc
index 7237695..8aab816 100644
--- a/modules/desktop_capture/desktop_frame_unittest.cc
+++ b/modules/desktop_capture/desktop_frame_unittest.cc
@@ -97,6 +97,18 @@
EXPECT_FALSE(frame->FrameDataIsBlack());
}
+TEST(DesktopFrameTest, FrameHasDefaultDeviceScaleFactor) {
+ auto frame = std::make_unique<BasicDesktopFrame>(DesktopSize());
+ EXPECT_EQ(frame->device_scale_factor(), std::nullopt);
+}
+
+TEST(DesktopFrameTest, FrameSetsDeviceScaleFactorCorrectly) {
+ auto frame = std::make_unique<BasicDesktopFrame>(DesktopSize());
+ EXPECT_EQ(frame->device_scale_factor(), std::nullopt);
+ frame->set_device_scale_factor(/*device_scale_factor=*/150);
+ EXPECT_EQ(frame->device_scale_factor(), 150);
+}
+
TEST(DesktopFrameTest, FrameDataSwitchesBetweenNonBlackAndBlack) {
auto frame = CreateTestFrame(DesktopRect::MakeXYWH(0, 0, 10, 10), 0xff);
EXPECT_FALSE(frame->FrameDataIsBlack());
diff --git a/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
index e06ed7a..6ad5f5c 100644
--- a/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
+++ b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
@@ -148,6 +148,13 @@
DesktopVector(), target);
}
+std::optional<int32_t> DxgiAdapterDuplicator::GetDeviceScaleFactor(
+ int screen_id) const {
+ RTC_DCHECK_GE(screen_id, 0);
+ RTC_DCHECK_LT(screen_id, duplicators_.size());
+ return duplicators_[screen_id].device_scale_factor();
+}
+
DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
RTC_DCHECK_GE(id, 0);
RTC_DCHECK_LT(id, duplicators_.size());
diff --git a/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/modules/desktop_capture/win/dxgi_adapter_duplicator.h
index 5948be8..b0539c4 100644
--- a/modules/desktop_capture/win/dxgi_adapter_duplicator.h
+++ b/modules/desktop_capture/win/dxgi_adapter_duplicator.h
@@ -55,6 +55,11 @@
// Returns desktop rect covered by this DxgiAdapterDuplicator.
DesktopRect desktop_rect() const { return desktop_rect_; }
+ // Returns the device scale factor of screen identified by `screen_id`, which
+ // is owned by this DxgiAdapterDuplicator. `screen_id` should be between [0,
+ // screen_count()).
+ std::optional<int32_t> GetDeviceScaleFactor(int screen_id) const;
+
// Returns the size of one screen owned by this DxgiAdapterDuplicator. `id`
// should be between [0, screen_count()).
DesktopRect ScreenRect(int id) const;
diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/modules/desktop_capture/win/dxgi_duplicator_controller.cc
index 3663066..94ad12e 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.cc
+++ b/modules/desktop_capture/win/dxgi_duplicator_controller.cc
@@ -189,7 +189,8 @@
return Result::INITIALIZATION_FAILED;
}
- if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) {
+ if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id,
+ GetDeviceScaleFactor(monitor_id))) {
return Result::FRAME_PREPARE_FAILED;
}
@@ -393,6 +394,19 @@
return desktop_rect_.size();
}
+std::optional<int32_t> DxgiDuplicatorController::GetDeviceScaleFactor(
+ int monitor_id) const {
+ RTC_CHECK_GE(monitor_id, 0);
+ for (const auto& duplicator : duplicators_) {
+ if (monitor_id >= duplicator.screen_count()) {
+ monitor_id -= duplicator.screen_count();
+ } else {
+ return duplicator.GetDeviceScaleFactor(monitor_id);
+ }
+ }
+ return std::nullopt;
+}
+
DesktopRect DxgiDuplicatorController::ScreenRect(int id) const {
RTC_DCHECK(id >= 0);
for (size_t i = 0; i < duplicators_.size(); i++) {
diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.h b/modules/desktop_capture/win/dxgi_duplicator_controller.h
index 5a92560..2df7b86 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.h
+++ b/modules/desktop_capture/win/dxgi_duplicator_controller.h
@@ -206,6 +206,13 @@
// Returns a DesktopSize to cover entire `desktop_rect_`.
DesktopSize desktop_size() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+ // Returns the device scale factor of one screen. `monitor_id` should be >= 0.
+ // If system does not support DXGI based capturer, or `monitor_id` is greater
+ // than the total screen count of all the Duplicators, this function returns
+ // std::nullopt.
+ std::optional<int32_t> GetDeviceScaleFactor(int monitor_id) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
// Returns the size of one screen. `id` should be >= 0. If system does not
// support DXGI based capturer, or `id` is greater than the total screen count
// of all the Duplicators, this function returns an empty DesktopRect.
diff --git a/modules/desktop_capture/win/dxgi_frame.cc b/modules/desktop_capture/win/dxgi_frame.cc
index 13d5b4b..223e27d 100644
--- a/modules/desktop_capture/win/dxgi_frame.cc
+++ b/modules/desktop_capture/win/dxgi_frame.cc
@@ -25,7 +25,9 @@
DxgiFrame::~DxgiFrame() = default;
-bool DxgiFrame::Prepare(DesktopSize size, DesktopCapturer::SourceId source_id) {
+bool DxgiFrame::Prepare(DesktopSize size,
+ DesktopCapturer::SourceId source_id,
+ std::optional<int32_t> device_scale_factor) {
if (source_id != source_id_) {
// Once the source has been changed, the entire source should be copied.
source_id_ = source_id;
@@ -57,7 +59,7 @@
} else {
frame.reset(new BasicDesktopFrame(size));
}
-
+ frame->set_device_scale_factor(device_scale_factor);
frame_ = SharedDesktopFrame::Wrap(std::move(frame));
}
diff --git a/modules/desktop_capture/win/dxgi_frame.h b/modules/desktop_capture/win/dxgi_frame.h
index 6a9ce86..3cb0d84 100644
--- a/modules/desktop_capture/win/dxgi_frame.h
+++ b/modules/desktop_capture/win/dxgi_frame.h
@@ -45,8 +45,11 @@
// as well as Context class.
friend class DxgiDuplicatorController;
- // Prepares current instance with desktop size and source id.
- bool Prepare(DesktopSize size, DesktopCapturer::SourceId source_id);
+ // Prepares current instance with desktop size, source id and device scale
+ // factor.
+ bool Prepare(DesktopSize size,
+ DesktopCapturer::SourceId source_id,
+ std::optional<int32_t> device_scale_factor);
// Should not be called if Prepare() is not executed or returns false.
Context* context();
diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.cc b/modules/desktop_capture/win/dxgi_output_duplicator.cc
index ac028ce..a9ed785 100644
--- a/modules/desktop_capture/win/dxgi_output_duplicator.cc
+++ b/modules/desktop_capture/win/dxgi_output_duplicator.cc
@@ -73,6 +73,9 @@
RTC_DCHECK(!desktop_rect_.is_empty());
RTC_DCHECK_GT(desktop_rect_.width(), 0);
RTC_DCHECK_GT(desktop_rect_.height(), 0);
+ HRESULT hr = GetScaleFactorForMonitor(desc.Monitor, &device_scale_factor_);
+ RTC_LOG_IF(LS_ERROR, FAILED(hr))
+ << "Failed to get scale factor for monitor: " << hr;
}
DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
@@ -418,6 +421,13 @@
return num_frames_captured_;
}
+std::optional<DEVICE_SCALE_FACTOR> DxgiOutputDuplicator::device_scale_factor()
+ const {
+ return (device_scale_factor_ != DEVICE_SCALE_FACTOR_INVALID)
+ ? std::make_optional(device_scale_factor_)
+ : std::nullopt;
+}
+
void DxgiOutputDuplicator::TranslateRect(const DesktopVector& position) {
desktop_rect_.Translate(position);
RTC_DCHECK_GE(desktop_rect_.left(), 0);
diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.h b/modules/desktop_capture/win/dxgi_output_duplicator.h
index a4ce035..6bad18d 100644
--- a/modules/desktop_capture/win/dxgi_output_duplicator.h
+++ b/modules/desktop_capture/win/dxgi_output_duplicator.h
@@ -14,6 +14,7 @@
#include <comdef.h>
#include <dxgi.h>
#include <dxgi1_2.h>
+#include <shellscalingapi.h>
#include <wrl/client.h>
#include <memory>
@@ -83,6 +84,10 @@
// How many frames have been captured by this DxigOutputDuplicator.
int64_t num_frames_captured() const;
+ // Device scale factor of the monitor associated with this
+ // DxigOutputDuplicator.
+ std::optional<DEVICE_SCALE_FACTOR> device_scale_factor() const;
+
// Moves `desktop_rect_`. See DxgiDuplicatorController::TranslateRect().
void TranslateRect(const DesktopVector& position);
@@ -133,6 +138,7 @@
std::unique_ptr<DxgiTexture> texture_;
Rotation rotation_;
DesktopSize unrotated_size_;
+ DEVICE_SCALE_FACTOR device_scale_factor_ = DEVICE_SCALE_FACTOR_INVALID;
// After each AcquireNextFrame() function call, updated_region_(s) of all
// active Context(s) need to be updated. Since they have missed the
diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc
index 1c86f8e..0d4b337 100644
--- a/modules/desktop_capture/win/wgc_capture_session.cc
+++ b/modules/desktop_capture/win/wgc_capture_session.cc
@@ -22,6 +22,7 @@
#include <utility>
#include <vector>
+#include "modules/desktop_capture/win/screen_capture_utils.h"
#include "modules/desktop_capture/win/wgc_desktop_frame.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@@ -99,12 +100,23 @@
} // namespace
-WgcCaptureSession::WgcCaptureSession(ComPtr<ID3D11Device> d3d11_device,
+WgcCaptureSession::WgcCaptureSession(intptr_t source_id,
+ ComPtr<ID3D11Device> d3d11_device,
ComPtr<WGC::IGraphicsCaptureItem> item,
ABI::Windows::Graphics::SizeInt32 size)
: d3d11_device_(std::move(d3d11_device)),
item_(std::move(item)),
- size_(size) {}
+ size_(size) {
+ RTC_CHECK(source_id);
+ HMONITOR monitor = 0;
+ if (!GetHmonitorFromDeviceIndex(source_id, &monitor)) {
+ monitor = MonitorFromWindow(reinterpret_cast<HWND>(source_id),
+ /*dwFlags=*/MONITOR_DEFAULTTONEAREST);
+ }
+ HRESULT hr = GetScaleFactorForMonitor(monitor, &device_scale_factor_);
+ RTC_LOG_IF(LS_ERROR, FAILED(hr))
+ << "Failed to get scale factor for monitor: " << hr;
+}
WgcCaptureSession::~WgcCaptureSession() {
RemoveEventHandler();
@@ -456,6 +468,9 @@
}
DesktopFrame* current_frame = queue_.current_frame();
+ if (device_scale_factor_ != DEVICE_SCALE_FACTOR_INVALID) {
+ current_frame->set_device_scale_factor(device_scale_factor_);
+ }
DesktopFrame* previous_frame = queue_.previous_frame();
// Will be set to true while copying the frame data to the `current_frame` if
diff --git a/modules/desktop_capture/win/wgc_capture_session.h b/modules/desktop_capture/win/wgc_capture_session.h
index d2901d9..a7af8f4 100644
--- a/modules/desktop_capture/win/wgc_capture_session.h
+++ b/modules/desktop_capture/win/wgc_capture_session.h
@@ -12,6 +12,7 @@
#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
#include <d3d11.h>
+#include <shellscalingapi.h>
#include <windows.graphics.capture.h>
#include <windows.graphics.h>
#include <wrl/client.h>
@@ -29,7 +30,9 @@
class WgcCaptureSession final {
public:
+ // `source_id` is used to retreive the HMONITOR for the captured window.
WgcCaptureSession(
+ intptr_t source_id,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item,
@@ -146,6 +149,12 @@
// false.
DesktopRegion damage_region_;
+ // Captures the device scale factor of the monitor where the frame is captured
+ // from. This value is the same as the scale from windows settings. Valid
+ // values are some distinct numbers in the range of [100,500], for example,
+ // 100, 150, 250, etc.
+ DEVICE_SCALE_FACTOR device_scale_factor_ = DEVICE_SCALE_FACTOR_INVALID;
+
SequenceChecker sequence_checker_;
};
diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc
index 54a0917..fff2a9e 100644
--- a/modules/desktop_capture/win/wgc_capturer_win.cc
+++ b/modules/desktop_capture/win/wgc_capturer_win.cc
@@ -342,8 +342,8 @@
iter_success_pair = ongoing_captures_.emplace(
std::piecewise_construct,
std::forward_as_tuple(capture_source_->GetSourceId()),
- std::forward_as_tuple(d3d11_device_, item,
- capture_source_->GetSize()));
+ std::forward_as_tuple(capture_source_->GetSourceId(), d3d11_device_,
+ item, capture_source_->GetSize()));
RTC_DCHECK(iter_success_pair.second);
capture_session = &iter_success_pair.first->second;
} else {