Add GetTopLeft to WgcCaptureSource to facilitate cursor capture.
This change disables native cursor capture in WgcCapturerWin to better support the existing idiom of wrapping a capturer in a DesktopAndCursorComposer. That means we also need to set the top_left property of output DesktopFrames, so I've also implemented GetTopLeft in WgcCaptureSource to facilitate this. I've also added a few unit tests for WgcCaptureSource. Bug: webrtc:12654 Change-Id: I5c9988a6f8548b584451b073ac29fbb482e09e2e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215102 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Commit-Queue: Austin Orion <auorion@microsoft.com> Cr-Commit-Position: refs/heads/master@{#33821}
This commit is contained in:
parent
70efbb839b
commit
66241e4fa4
@ -145,7 +145,10 @@ if (rtc_include_tests) {
|
||||
sources += [ "screen_capturer_mac_unittest.cc" ]
|
||||
}
|
||||
if (rtc_enable_win_wgc) {
|
||||
sources += [ "win/wgc_capturer_win_unittest.cc" ]
|
||||
sources += [
|
||||
"win/wgc_capture_source_unittest.cc",
|
||||
"win/wgc_capturer_win_unittest.cc",
|
||||
]
|
||||
}
|
||||
deps += [
|
||||
":desktop_capture_mock",
|
||||
|
||||
@ -99,6 +99,18 @@ bool IsMonitorValid(const HMONITOR monitor) {
|
||||
return GetMonitorInfoA(monitor, &monitor_info);
|
||||
}
|
||||
|
||||
DesktopRect GetMonitorRect(const HMONITOR monitor) {
|
||||
MONITORINFO monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFO);
|
||||
if (!GetMonitorInfoA(monitor, &monitor_info)) {
|
||||
return DesktopRect();
|
||||
}
|
||||
|
||||
return DesktopRect::MakeLTRB(
|
||||
monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
|
||||
monitor_info.rcMonitor.right, monitor_info.rcMonitor.bottom);
|
||||
}
|
||||
|
||||
bool IsScreenValid(const DesktopCapturer::SourceId screen,
|
||||
std::wstring* device_key) {
|
||||
if (screen == kFullDesktopScreenId) {
|
||||
|
||||
@ -37,6 +37,10 @@ bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index,
|
||||
// WM_DISPLAYCHANGE message has been received.
|
||||
bool IsMonitorValid(const HMONITOR monitor);
|
||||
|
||||
// Returns the rect of the monitor identified by |monitor|, relative to the
|
||||
// primary display's top-left. On failure, returns an empty rect.
|
||||
DesktopRect GetMonitorRect(const HMONITOR monitor);
|
||||
|
||||
// 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
|
||||
// used in GetScreenRect to verify the screen matches the previously obtained
|
||||
|
||||
@ -75,8 +75,16 @@ WindowInfo CreateTestWindow(const WCHAR* window_title,
|
||||
}
|
||||
|
||||
void ResizeTestWindow(const HWND hwnd, const int width, const int height) {
|
||||
// SWP_NOMOVE results in the x and y params being ignored.
|
||||
::SetWindowPos(hwnd, HWND_TOP, /*x-coord=*/0, /*y-coord=*/0, width, height,
|
||||
SWP_SHOWWINDOW);
|
||||
SWP_SHOWWINDOW | SWP_NOMOVE);
|
||||
::UpdateWindow(hwnd);
|
||||
}
|
||||
|
||||
void MoveTestWindow(const HWND hwnd, const int x, const int y) {
|
||||
// SWP_NOSIZE results in the width and height params being ignored.
|
||||
::SetWindowPos(hwnd, HWND_TOP, x, y, /*width=*/0, /*height=*/0,
|
||||
SWP_SHOWWINDOW | SWP_NOSIZE);
|
||||
::UpdateWindow(hwnd);
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ WindowInfo CreateTestWindow(const WCHAR* window_title,
|
||||
|
||||
void ResizeTestWindow(const HWND hwnd, const int width, const int height);
|
||||
|
||||
void MoveTestWindow(const HWND hwnd, const int x, const int y);
|
||||
|
||||
void MinimizeTestWindow(const HWND hwnd);
|
||||
|
||||
void UnminimizeTestWindow(const HWND hwnd);
|
||||
|
||||
@ -72,6 +72,14 @@ WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id)
|
||||
: WgcCaptureSource(source_id) {}
|
||||
WgcWindowSource::~WgcWindowSource() = default;
|
||||
|
||||
DesktopVector WgcWindowSource::GetTopLeft() {
|
||||
DesktopRect window_rect;
|
||||
if (!GetWindowRect(reinterpret_cast<HWND>(GetSourceId()), &window_rect))
|
||||
return DesktopVector();
|
||||
|
||||
return window_rect.top_left();
|
||||
}
|
||||
|
||||
bool WgcWindowSource::IsCapturable() {
|
||||
if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
|
||||
return false;
|
||||
@ -113,17 +121,26 @@ HRESULT WgcWindowSource::CreateCaptureItem(
|
||||
}
|
||||
|
||||
WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id)
|
||||
: WgcCaptureSource(source_id) {}
|
||||
: WgcCaptureSource(source_id) {
|
||||
// Getting the HMONITOR could fail if the source_id is invalid. In that case,
|
||||
// we leave hmonitor_ uninitialized and |IsCapturable()| will fail.
|
||||
HMONITOR hmon;
|
||||
if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon))
|
||||
hmonitor_ = hmon;
|
||||
}
|
||||
|
||||
WgcScreenSource::~WgcScreenSource() = default;
|
||||
|
||||
bool WgcScreenSource::IsCapturable() {
|
||||
if (!hmonitor_) {
|
||||
HMONITOR hmon;
|
||||
if (!GetHmonitorFromDeviceIndex(GetSourceId(), &hmon))
|
||||
return false;
|
||||
DesktopVector WgcScreenSource::GetTopLeft() {
|
||||
if (!hmonitor_)
|
||||
return DesktopVector();
|
||||
|
||||
hmonitor_ = hmon;
|
||||
}
|
||||
return GetMonitorRect(*hmonitor_)->top_left();
|
||||
}
|
||||
|
||||
bool WgcScreenSource::IsCapturable() {
|
||||
if (!hmonitor_)
|
||||
return false;
|
||||
|
||||
if (!IsMonitorValid(*hmonitor_))
|
||||
return false;
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/desktop_geometry.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -30,6 +31,7 @@ class WgcCaptureSource {
|
||||
explicit WgcCaptureSource(DesktopCapturer::SourceId source_id);
|
||||
virtual ~WgcCaptureSource();
|
||||
|
||||
virtual DesktopVector GetTopLeft() = 0;
|
||||
virtual bool IsCapturable();
|
||||
virtual bool FocusOnSource();
|
||||
HRESULT GetCaptureItem(
|
||||
@ -93,6 +95,7 @@ class WgcWindowSource final : public WgcCaptureSource {
|
||||
|
||||
~WgcWindowSource() override;
|
||||
|
||||
DesktopVector GetTopLeft() override;
|
||||
bool IsCapturable() override;
|
||||
bool FocusOnSource() override;
|
||||
|
||||
@ -113,6 +116,7 @@ class WgcScreenSource final : public WgcCaptureSource {
|
||||
|
||||
~WgcScreenSource() override;
|
||||
|
||||
DesktopVector GetTopLeft() override;
|
||||
bool IsCapturable() override;
|
||||
|
||||
private:
|
||||
|
||||
138
modules/desktop_capture/win/wgc_capture_source_unittest.cc
Normal file
138
modules/desktop_capture/win/wgc_capture_source_unittest.cc
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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_source.h"
|
||||
|
||||
#include <windows.graphics.capture.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_types.h"
|
||||
#include "modules/desktop_capture/desktop_geometry.h"
|
||||
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
||||
#include "modules/desktop_capture/win/test_support/test_window.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/win/scoped_com_initializer.h"
|
||||
#include "rtc_base/win/windows_version.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
const WCHAR kWindowTitle[] = L"WGC Capture Source Test Window";
|
||||
|
||||
const int kFirstXCoord = 25;
|
||||
const int kFirstYCoord = 50;
|
||||
const int kSecondXCoord = 50;
|
||||
const int kSecondYCoord = 75;
|
||||
|
||||
enum SourceType { kWindowSource = 0, kScreenSource = 1 };
|
||||
|
||||
} // namespace
|
||||
|
||||
class WgcCaptureSourceTest : public ::testing::TestWithParam<SourceType> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Skipping WgcCaptureSourceTests on Windows versions < RS5.";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
com_initializer_ =
|
||||
std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
|
||||
ASSERT_TRUE(com_initializer_->Succeeded());
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (window_open_) {
|
||||
DestroyTestWindow(window_info_);
|
||||
}
|
||||
}
|
||||
|
||||
void SetUpForWindowSource() {
|
||||
window_info_ = CreateTestWindow(kWindowTitle);
|
||||
window_open_ = true;
|
||||
source_id_ = reinterpret_cast<DesktopCapturer::SourceId>(window_info_.hwnd);
|
||||
source_factory_ = std::make_unique<WgcWindowSourceFactory>();
|
||||
}
|
||||
|
||||
void SetUpForScreenSource() {
|
||||
source_id_ = kFullDesktopScreenId;
|
||||
source_factory_ = std::make_unique<WgcScreenSourceFactory>();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ScopedCOMInitializer> com_initializer_;
|
||||
std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
|
||||
std::unique_ptr<WgcCaptureSource> source_;
|
||||
DesktopCapturer::SourceId source_id_;
|
||||
WindowInfo window_info_;
|
||||
bool window_open_ = false;
|
||||
};
|
||||
|
||||
// Window specific test
|
||||
TEST_F(WgcCaptureSourceTest, WindowPosition) {
|
||||
SetUpForWindowSource();
|
||||
source_ = source_factory_->CreateCaptureSource(source_id_);
|
||||
ASSERT_TRUE(source_);
|
||||
EXPECT_EQ(source_->GetSourceId(), source_id_);
|
||||
|
||||
MoveTestWindow(window_info_.hwnd, kFirstXCoord, kFirstYCoord);
|
||||
DesktopVector source_vector = source_->GetTopLeft();
|
||||
EXPECT_EQ(source_vector.x(), kFirstXCoord);
|
||||
EXPECT_EQ(source_vector.y(), kFirstYCoord);
|
||||
|
||||
MoveTestWindow(window_info_.hwnd, kSecondXCoord, kSecondYCoord);
|
||||
source_vector = source_->GetTopLeft();
|
||||
EXPECT_EQ(source_vector.x(), kSecondXCoord);
|
||||
EXPECT_EQ(source_vector.y(), kSecondYCoord);
|
||||
}
|
||||
|
||||
// Screen specific test
|
||||
TEST_F(WgcCaptureSourceTest, ScreenPosition) {
|
||||
SetUpForScreenSource();
|
||||
source_ = source_factory_->CreateCaptureSource(source_id_);
|
||||
ASSERT_TRUE(source_);
|
||||
EXPECT_EQ(source_id_, source_->GetSourceId());
|
||||
|
||||
DesktopRect screen_rect = GetFullscreenRect();
|
||||
DesktopVector source_vector = source_->GetTopLeft();
|
||||
EXPECT_EQ(source_vector.x(), screen_rect.left());
|
||||
EXPECT_EQ(source_vector.y(), screen_rect.top());
|
||||
}
|
||||
|
||||
// Source agnostic test
|
||||
TEST_P(WgcCaptureSourceTest, CreateSource) {
|
||||
if (GetParam() == SourceType::kWindowSource) {
|
||||
SetUpForWindowSource();
|
||||
} else {
|
||||
SetUpForScreenSource();
|
||||
}
|
||||
|
||||
source_ = source_factory_->CreateCaptureSource(source_id_);
|
||||
ASSERT_TRUE(source_);
|
||||
EXPECT_EQ(source_id_, source_->GetSourceId());
|
||||
EXPECT_TRUE(source_->IsCapturable());
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
|
||||
item;
|
||||
EXPECT_TRUE(SUCCEEDED(source_->GetCaptureItem(&item)));
|
||||
EXPECT_TRUE(item);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
|
||||
WgcCaptureSourceTest,
|
||||
::testing::Values(SourceType::kWindowSource,
|
||||
SourceType::kScreenSource));
|
||||
|
||||
} // namespace webrtc
|
||||
@ -199,6 +199,7 @@ void WgcCapturerWin::CaptureFrame() {
|
||||
frame->set_capture_time_ms(capture_time_ms);
|
||||
frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin);
|
||||
frame->set_may_contain_cursor(true);
|
||||
frame->set_top_left(capture_source_->GetTopLeft());
|
||||
RecordWgcCapturerResult(WgcCapturerResult::kSuccess);
|
||||
callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS,
|
||||
std::move(frame));
|
||||
|
||||
@ -77,7 +77,7 @@ class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
|
||||
void SetUp() override {
|
||||
if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Skipping WgcWindowCaptureTests on Windows versions < RS5.";
|
||||
<< "Skipping WgcCapturerWinTests on Windows versions < RS5.";
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
|
||||
@ -207,8 +207,8 @@ declare_args() {
|
||||
rtc_win_undef_unicode = false
|
||||
|
||||
# When set to true, a capturer implementation that uses the
|
||||
# Windows.Graphics.Capture APIs will be available for use. These APIs are
|
||||
# available in the Win 10 SDK v10.0.19041.
|
||||
# Windows.Graphics.Capture APIs will be available for use. This introduces a
|
||||
# dependency on the Win 10 SDK v10.0.17763.0.
|
||||
rtc_enable_win_wgc = false
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user