mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-10 17:49:20 +08:00
[feat] add Windows privacy screen and prefer DXGI capture
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
#include "screen_capturer_dxgi.h"
|
||||
|
||||
#include <display_stream_id.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <display_stream_id.h>
|
||||
#include "libyuv.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
@@ -218,11 +219,13 @@ void ScreenCapturerDxgi::EnumerateDisplays() {
|
||||
mi.rcMonitor.left, mi.rcMonitor.top,
|
||||
mi.rcMonitor.right, mi.rcMonitor.bottom);
|
||||
// primary first
|
||||
if (is_primary)
|
||||
if (is_primary) {
|
||||
display_info_list_.insert(display_info_list_.begin(), info);
|
||||
else
|
||||
outputs_.insert(outputs_.begin(), output);
|
||||
} else {
|
||||
display_info_list_.push_back(info);
|
||||
outputs_.push_back(output);
|
||||
outputs_.push_back(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,6 +249,11 @@ bool ScreenCapturerDxgi::CreateDuplicationForMonitor(int monitor_index) {
|
||||
}
|
||||
|
||||
staging_.Reset();
|
||||
DXGI_OUTDUPL_DESC desc{};
|
||||
duplication_->GetDesc(&desc);
|
||||
rotation_ = desc.Rotation;
|
||||
LOG_INFO("DXGI: duplication ready, monitor={}, rotation={}", monitor_index,
|
||||
static_cast<int>(rotation_));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -276,6 +284,7 @@ bool ScreenCapturerDxgi::RecreateDuplicationForCurrentMonitor() {
|
||||
}
|
||||
|
||||
void ScreenCapturerDxgi::ReleaseDuplication() {
|
||||
++duplication_generation_;
|
||||
staging_.Reset();
|
||||
if (duplication_) {
|
||||
duplication_->ReleaseFrame();
|
||||
@@ -284,16 +293,27 @@ void ScreenCapturerDxgi::ReleaseDuplication() {
|
||||
}
|
||||
|
||||
void ScreenCapturerDxgi::CaptureLoop() {
|
||||
const int timeout_ms = 33;
|
||||
const int timeout_ms = (std::max)(1, 1000 / (std::max)(1, fps_));
|
||||
bool cached_frame_valid = false;
|
||||
int cached_monitor = -1;
|
||||
uint64_t cached_generation = 0;
|
||||
std::vector<uint8_t> rotated_frame;
|
||||
auto last_duplication_retry =
|
||||
std::chrono::steady_clock::now() - std::chrono::milliseconds(1000);
|
||||
while (running_) {
|
||||
if (paused_) {
|
||||
cached_frame_valid = false;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Duplication/staging textures belong to one monitor. Do not release or
|
||||
// replace them in SwitchTo while AcquireNextFrame/Map is using them.
|
||||
std::unique_lock capture_lock(switch_mutex_);
|
||||
if (paused_ || !running_) continue;
|
||||
if (!duplication_) {
|
||||
cached_frame_valid = false;
|
||||
capture_lock.unlock();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now - last_duplication_retry >= std::chrono::milliseconds(500)) {
|
||||
last_duplication_retry = now;
|
||||
@@ -303,15 +323,32 @@ void ScreenCapturerDxgi::CaptureLoop() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int frame_monitor = monitor_index_.load();
|
||||
DXGI_OUTDUPL_FRAME_INFO frame_info{};
|
||||
Microsoft::WRL::ComPtr<IDXGIResource> desktop_resource;
|
||||
HRESULT hr = duplication_->AcquireNextFrame(
|
||||
timeout_ms, &frame_info, desktop_resource.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
|
||||
// DXGI explicitly reports an unchanged desktop. Re-emit only a frame
|
||||
// successfully acquired for this monitor, so a static screen still has
|
||||
// a delivery heartbeat after temporary privacy probes are removed.
|
||||
if (cached_frame_valid && cached_generation == duplication_generation_ &&
|
||||
cached_monitor == monitor_index_.load() && callback_ && nv12_frame_) {
|
||||
const auto stream_id = MakeDisplayStreamId(cached_monitor);
|
||||
capture_lock.unlock();
|
||||
callback_(nv12_frame_, nv12_width_ * nv12_height_ * 3 / 2, nv12_width_,
|
||||
nv12_height_, stream_id.c_str(), nullptr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Never replay a cached image after an acquisition/conversion failure or
|
||||
// a backend rebuild. Normal error/reset handling must pause privacy.
|
||||
cached_frame_valid = false;
|
||||
if (FAILED(hr)) {
|
||||
LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr);
|
||||
capture_lock.unlock();
|
||||
if (callback_)
|
||||
callback_(nullptr, ScreenCapturer::kBackendReset, 0, 0, "", nullptr);
|
||||
RecreateDuplicationForCurrentMonitor();
|
||||
continue;
|
||||
}
|
||||
@@ -356,9 +393,42 @@ void ScreenCapturerDxgi::CaptureLoop() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pixels = static_cast<const uint8_t*>(mapped.pData);
|
||||
int stride = static_cast<int>(mapped.RowPitch);
|
||||
int logical_width = static_cast<int>(src_desc.Width);
|
||||
int logical_height = static_cast<int>(src_desc.Height);
|
||||
libyuv::RotationMode rotation = libyuv::kRotate0;
|
||||
switch (rotation_) {
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
rotation = libyuv::kRotate90;
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
rotation = libyuv::kRotate180;
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
rotation = libyuv::kRotate270;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (rotation != libyuv::kRotate0) {
|
||||
const bool swap_axes = rotation != libyuv::kRotate180;
|
||||
const int rotated_width = swap_axes ? logical_height : logical_width;
|
||||
rotated_frame.resize(static_cast<size_t>(logical_width) * logical_height *
|
||||
4);
|
||||
if (libyuv::ARGBRotate(pixels, stride, rotated_frame.data(),
|
||||
rotated_width * 4, logical_width, logical_height,
|
||||
rotation) != 0) {
|
||||
d3d_context_->Unmap(staging_.Get(), 0);
|
||||
duplication_->ReleaseFrame();
|
||||
continue;
|
||||
}
|
||||
pixels = rotated_frame.data();
|
||||
stride = rotated_width * 4;
|
||||
if (swap_axes) std::swap(logical_width, logical_height);
|
||||
}
|
||||
int even_width = logical_width & ~1;
|
||||
int even_height = static_cast<int>(src_desc.Height) & ~1;
|
||||
int even_height = logical_height & ~1;
|
||||
if (even_width <= 0 || even_height <= 0) {
|
||||
d3d_context_->Unmap(staging_.Get(), 0);
|
||||
duplication_->ReleaseFrame();
|
||||
@@ -374,25 +444,29 @@ void ScreenCapturerDxgi::CaptureLoop() {
|
||||
nv12_height_ = even_height;
|
||||
}
|
||||
|
||||
libyuv::ARGBToNV12(static_cast<const uint8_t*>(mapped.pData),
|
||||
static_cast<int>(mapped.RowPitch), nv12_frame_,
|
||||
even_width, nv12_frame_ + even_width * even_height,
|
||||
even_width, even_width, even_height);
|
||||
const int converted =
|
||||
libyuv::ARGBToNV12(pixels, stride, nv12_frame_, even_width,
|
||||
nv12_frame_ + even_width * even_height, even_width,
|
||||
even_width, even_height);
|
||||
|
||||
if (callback_) {
|
||||
int idx = monitor_index_.load();
|
||||
cached_generation = duplication_generation_;
|
||||
d3d_context_->Unmap(staging_.Get(), 0);
|
||||
duplication_->ReleaseFrame();
|
||||
capture_lock.unlock();
|
||||
|
||||
if (converted == 0 && frame_monitor == monitor_index_.load() && callback_) {
|
||||
int idx = frame_monitor;
|
||||
if (idx >= 0 && idx < static_cast<int>(display_info_list_.size())) {
|
||||
const std::string stream_id = MakeDisplayStreamId(idx);
|
||||
callback_(nv12_frame_, nv12_size, even_width, even_height,
|
||||
stream_id.c_str(), nullptr);
|
||||
cached_monitor = idx;
|
||||
cached_frame_valid = true;
|
||||
} else {
|
||||
LOG_ERROR("DXGI: CaptureLoop invalid monitor_index {} (list size {})",
|
||||
idx, display_info_list_.size());
|
||||
}
|
||||
}
|
||||
|
||||
d3d_context_->Unmap(staging_.Get(), 0);
|
||||
duplication_->ReleaseFrame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ class ScreenCapturerDxgi : public ScreenCapturer {
|
||||
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d_context_;
|
||||
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> duplication_;
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_;
|
||||
DXGI_MODE_ROTATION rotation_ = DXGI_MODE_ROTATION_IDENTITY;
|
||||
uint64_t duplication_generation_ = 0;
|
||||
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> paused_{false};
|
||||
|
||||
@@ -351,6 +351,10 @@ int ScreenCapturerWgc::ResetToInitialMonitor() {
|
||||
}
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
|
||||
int id) {
|
||||
if (!frame.data && frame.width == 0 && frame.height == 0) {
|
||||
if (on_data_) on_data_(nullptr, ScreenCapturer::kBackendReset, 0, 0, "", nullptr);
|
||||
return;
|
||||
}
|
||||
if (!running_ || !on_data_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,13 @@ class WgcPluginCapturer final : public ScreenCapturer {
|
||||
DestroyFn destroy_fn_ = nullptr;
|
||||
};
|
||||
|
||||
const char* CaptureBackendName(const ScreenCapturer* capturer) {
|
||||
if (dynamic_cast<const ScreenCapturerDxgi*>(capturer)) return "DXGI";
|
||||
if (dynamic_cast<const WgcPluginCapturer*>(capturer)) return "WGC";
|
||||
if (dynamic_cast<const ScreenCapturerGdi*>(capturer)) return "GDI";
|
||||
return "unavailable";
|
||||
}
|
||||
|
||||
std::string BuildSecureCaptureCommand(int left, int top, int width, int height,
|
||||
bool show_cursor,
|
||||
const std::string& stage,
|
||||
@@ -330,7 +337,13 @@ bool QuerySecureDesktopHelperFrame(
|
||||
ScreenCapturerWin::ScreenCapturerWin() {}
|
||||
ScreenCapturerWin::~ScreenCapturerWin() { Destroy(); }
|
||||
|
||||
void ScreenCapturerWin::NotifyPrivacyCapture(bool running) {
|
||||
if (privacy_)
|
||||
privacy_->CaptureChanged(CaptureBackendName(impl_.get()), running);
|
||||
}
|
||||
|
||||
int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
|
||||
NotifyPrivacyCapture(false);
|
||||
fps_ = fps;
|
||||
cb_orig_ = cb;
|
||||
native_output_logged_.store(false, std::memory_order_relaxed);
|
||||
@@ -344,12 +357,17 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
|
||||
cb_ = [this](unsigned char* data, int size, int w, int h,
|
||||
const char* reported_stream_id,
|
||||
const MiniRtcNativeVideoFrame* native_frame) {
|
||||
if (size == ScreenCapturer::kBackendReset) {
|
||||
if (privacy_) privacy_->CaptureInterrupted();
|
||||
return;
|
||||
}
|
||||
if (secure_desktop_capture_active_.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* raw_stream_id = reported_stream_id ? reported_stream_id : "";
|
||||
std::string mapped_stream_id;
|
||||
DisplayInfo privacy_display{"", 0, 0, 0, 0};
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(alias_mutex_);
|
||||
auto it = stream_id_alias_.find(raw_stream_id);
|
||||
@@ -363,6 +381,21 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
|
||||
mapped_stream_id = ResolveDisplayStreamId(
|
||||
mapped_stream_id.c_str(), canonical_displays_.size(),
|
||||
monitor_index_.load(std::memory_order_relaxed));
|
||||
for (size_t index = 0; index < canonical_displays_.size(); ++index) {
|
||||
if (mapped_stream_id == MakeDisplayStreamId(index)) {
|
||||
privacy_display = canonical_displays_[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (privacy_) {
|
||||
if (privacy_->Engaged() && !IsWindowsPrivacyDesktopAvailable()) {
|
||||
privacy_->Fail("Secure or unavailable desktop; privacy cannot cover system security UI. Remote operation paused.");
|
||||
return;
|
||||
}
|
||||
if (!privacy_->ObserveFrame(data, size > 0 ? static_cast<size_t>(size) : 0,
|
||||
w, h, privacy_display.left, privacy_display.top,
|
||||
privacy_display.width, privacy_display.height)) return;
|
||||
}
|
||||
if (mapped_stream_id.empty()) {
|
||||
if (!invalid_stream_id_logged_.exchange(true,
|
||||
@@ -393,42 +426,23 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
|
||||
native_frame);
|
||||
};
|
||||
|
||||
impl_.reset();
|
||||
int ret = -1;
|
||||
|
||||
impl_ = WgcPluginCapturer::Create();
|
||||
impl_is_wgc_plugin_ = (impl_ != nullptr);
|
||||
ret = impl_ ? impl_->Init(fps_, cb_) : -1;
|
||||
if (ret == 0) {
|
||||
LOG_INFO("Windows capturer: using WGC plugin");
|
||||
BuildCanonicalFromImpl();
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
initial_monitor_index_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_WARN("Windows capturer: WGC plugin init failed (ret={}), try DXGI", ret);
|
||||
impl_.reset();
|
||||
impl_is_wgc_plugin_ = false;
|
||||
|
||||
impl_ = std::make_unique<ScreenCapturerDxgi>();
|
||||
impl_is_wgc_plugin_ = false;
|
||||
ret = impl_->Init(fps_, cb_);
|
||||
if (ret == 0) {
|
||||
LOG_INFO("Windows capturer: using DXGI Desktop Duplication");
|
||||
BuildCanonicalFromImpl();
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
initial_monitor_index_ = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_WARN("Windows capturer: DXGI init failed (ret={}), fallback to GDI", ret);
|
||||
impl_.reset();
|
||||
|
||||
impl_ = std::make_unique<ScreenCapturerGdi>();
|
||||
impl_is_wgc_plugin_ = false;
|
||||
ret = impl_->Init(fps_, cb_);
|
||||
if (ret == 0) {
|
||||
LOG_INFO("Windows capturer: using GDI BitBlt");
|
||||
auto try_init = [&](std::unique_ptr<ScreenCapturer> candidate) {
|
||||
ret = candidate ? candidate->Init(fps_, cb_) : -1;
|
||||
if (ret != 0) {
|
||||
LOG_WARN("Windows capturer: {} init failed (ret={})",
|
||||
CaptureBackendName(candidate.get()), ret);
|
||||
return false;
|
||||
}
|
||||
impl_ = std::move(candidate);
|
||||
return true;
|
||||
};
|
||||
// Short-circuiting also keeps the unused WGC plugin unloaded.
|
||||
if (try_init(std::make_unique<ScreenCapturerDxgi>()) ||
|
||||
try_init(WgcPluginCapturer::Create()) ||
|
||||
try_init(std::make_unique<ScreenCapturerGdi>())) {
|
||||
LOG_INFO("Windows capturer: using {}", CaptureBackendName(impl_.get()));
|
||||
BuildCanonicalFromImpl();
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
initial_monitor_index_ = 0;
|
||||
@@ -436,7 +450,6 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
|
||||
}
|
||||
|
||||
LOG_ERROR("Windows capturer: all implementations failed, ret={}", ret);
|
||||
impl_.reset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -446,7 +459,6 @@ int ScreenCapturerWin::Destroy() {
|
||||
if (impl_) {
|
||||
impl_->Destroy();
|
||||
impl_.reset();
|
||||
impl_is_wgc_plugin_ = false;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(alias_mutex_);
|
||||
@@ -459,8 +471,15 @@ int ScreenCapturerWin::Destroy() {
|
||||
|
||||
void ScreenCapturerWin::EmitCapturedFrame(
|
||||
unsigned char* data, int size, int width, int height,
|
||||
const char* stream_id, const MiniRtcNativeVideoFrame* native_frame) {
|
||||
if (!cb_orig_) {
|
||||
const char* stream_id, const MiniRtcNativeVideoFrame* native_frame,
|
||||
bool from_secure_desktop) {
|
||||
// A helper IPC already in flight when enable began must not bypass the
|
||||
// verified ordinary-desktop callback when it completes later.
|
||||
if (from_secure_desktop && privacy_ && privacy_->Engaged()) {
|
||||
privacy_->Fail("Secure desktop helper frame arrived during privacy; remote operation paused");
|
||||
return;
|
||||
}
|
||||
if (!cb_orig_ || (privacy_ && !privacy_->RemoteAllowed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -499,11 +518,43 @@ void ScreenCapturerWin::EmitCapturedFrame(
|
||||
owned_frame->Release();
|
||||
}
|
||||
|
||||
void ScreenCapturerWin::RestoreMonitor(int monitor_index) {
|
||||
RebuildAliasesFromImpl();
|
||||
if (monitor_index > 0 && impl_->SwitchTo(monitor_index) != 0) {
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenCapturerWin::TryStartBackend(
|
||||
std::unique_ptr<ScreenCapturer> candidate, int monitor_index) {
|
||||
if (!candidate) {
|
||||
LOG_WARN("Windows capturer: WGC plugin unavailable");
|
||||
return false;
|
||||
}
|
||||
const char* name = CaptureBackendName(candidate.get());
|
||||
int ret = candidate->Init(fps_, cb_);
|
||||
if (ret == 0)
|
||||
ret = candidate->Start(show_cursor_.load(std::memory_order_relaxed));
|
||||
if (ret != 0) {
|
||||
LOG_WARN("Windows capturer: {} initialization/start failed (ret={})", name,
|
||||
ret);
|
||||
candidate->Destroy();
|
||||
return false;
|
||||
}
|
||||
// Commit only after the replacement is running. Failed candidates release
|
||||
// their resources without replacing the current backend.
|
||||
impl_ = std::move(candidate);
|
||||
RestoreMonitor(monitor_index);
|
||||
LOG_INFO("Windows capturer: started {}", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
int ScreenCapturerWin::Start(bool show_cursor) {
|
||||
if (!impl_) return -1;
|
||||
if (running_.load(std::memory_order_relaxed)) {
|
||||
return 0;
|
||||
}
|
||||
NotifyPrivacyCapture(false);
|
||||
|
||||
show_cursor_.store(show_cursor, std::memory_order_relaxed);
|
||||
paused_.store(false, std::memory_order_relaxed);
|
||||
@@ -528,37 +579,15 @@ int ScreenCapturerWin::Start(bool show_cursor) {
|
||||
LOG_WARN("Windows capturer: refresh/start failed (ret={}), trying fallback",
|
||||
ret);
|
||||
|
||||
auto try_init_start = [&](std::unique_ptr<ScreenCapturer> cand) -> bool {
|
||||
int r = cand->Init(fps_, cb_);
|
||||
if (r != 0) return false;
|
||||
int s = cand->Start(show_cursor);
|
||||
if (s == 0) {
|
||||
impl_ = std::move(cand);
|
||||
impl_is_wgc_plugin_ = false;
|
||||
RebuildAliasesFromImpl();
|
||||
if (requested_monitor > 0 &&
|
||||
impl_->SwitchTo(requested_monitor) != 0) {
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool fallback_started = false;
|
||||
if (impl_is_wgc_plugin_) {
|
||||
if (try_init_start(std::make_unique<ScreenCapturerDxgi>())) {
|
||||
LOG_INFO("Windows capturer: fallback to DXGI");
|
||||
fallback_started = true;
|
||||
} else if (try_init_start(std::make_unique<ScreenCapturerGdi>())) {
|
||||
LOG_INFO("Windows capturer: fallback to GDI");
|
||||
fallback_started = true;
|
||||
}
|
||||
} else if (dynamic_cast<ScreenCapturerDxgi*>(impl_.get())) {
|
||||
if (try_init_start(std::make_unique<ScreenCapturerGdi>())) {
|
||||
LOG_INFO("Windows capturer: fallback to GDI");
|
||||
fallback_started = true;
|
||||
}
|
||||
if (dynamic_cast<ScreenCapturerDxgi*>(impl_.get()) &&
|
||||
TryStartBackend(WgcPluginCapturer::Create(), requested_monitor)) {
|
||||
fallback_started = true;
|
||||
}
|
||||
if (!fallback_started && (dynamic_cast<WgcPluginCapturer*>(impl_.get()) ||
|
||||
dynamic_cast<ScreenCapturerDxgi*>(impl_.get()))) {
|
||||
fallback_started = TryStartBackend(std::make_unique<ScreenCapturerGdi>(),
|
||||
requested_monitor);
|
||||
}
|
||||
|
||||
if (!fallback_started) {
|
||||
@@ -568,6 +597,7 @@ int ScreenCapturerWin::Start(bool show_cursor) {
|
||||
}
|
||||
|
||||
running_.store(true, std::memory_order_relaxed);
|
||||
NotifyPrivacyCapture(true);
|
||||
secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
|
||||
post_secure_desktop_waiting_for_frame_.store(false,
|
||||
std::memory_order_relaxed);
|
||||
@@ -581,6 +611,7 @@ int ScreenCapturerWin::Start(bool show_cursor) {
|
||||
}
|
||||
|
||||
int ScreenCapturerWin::Stop() {
|
||||
NotifyPrivacyCapture(false);
|
||||
running_.store(false, std::memory_order_relaxed);
|
||||
secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
|
||||
post_secure_desktop_waiting_for_frame_.store(false,
|
||||
@@ -610,9 +641,12 @@ int ScreenCapturerWin::Resume(int monitor_index) {
|
||||
|
||||
int ScreenCapturerWin::SwitchTo(int monitor_index) {
|
||||
if (!impl_) return -1;
|
||||
if (privacy_) privacy_->DisplayChanging();
|
||||
const int ret = impl_->SwitchTo(monitor_index);
|
||||
if (ret == 0) {
|
||||
monitor_index_.store(monitor_index, std::memory_order_relaxed);
|
||||
} else if (privacy_) {
|
||||
privacy_->Fail("Display switch failed; remote operation paused");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -735,53 +769,19 @@ void ScreenCapturerWin::StopSecureCaptureThread() {
|
||||
}
|
||||
|
||||
bool ScreenCapturerWin::RestartCaptureBackendAfterSecureDesktop() {
|
||||
NotifyPrivacyCapture(false);
|
||||
if (!impl_ || !running_.load(std::memory_order_relaxed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool show_cursor = show_cursor_.load(std::memory_order_relaxed);
|
||||
const int current_monitor = monitor_index_.load(std::memory_order_relaxed);
|
||||
auto restore_monitor = [&]() {
|
||||
RebuildAliasesFromImpl();
|
||||
if (current_monitor > 0 && impl_->SwitchTo(current_monitor) != 0) {
|
||||
monitor_index_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
auto try_started_backend = [&](std::unique_ptr<ScreenCapturer> cand,
|
||||
const char* name,
|
||||
bool is_wgc_plugin) -> bool {
|
||||
if (!cand) {
|
||||
return false;
|
||||
}
|
||||
const int init_ret = cand->Init(fps_, cb_);
|
||||
if (init_ret != 0) {
|
||||
LOG_WARN("Windows capturer: {} init after secure desktop failed (ret={})",
|
||||
name, init_ret);
|
||||
return false;
|
||||
}
|
||||
const int start_ret = cand->Start(show_cursor);
|
||||
if (start_ret != 0) {
|
||||
LOG_WARN(
|
||||
"Windows capturer: {} start after secure desktop failed (ret={})",
|
||||
name, start_ret);
|
||||
cand->Destroy();
|
||||
return false;
|
||||
}
|
||||
if (impl_) {
|
||||
impl_->Destroy();
|
||||
}
|
||||
impl_ = std::move(cand);
|
||||
impl_is_wgc_plugin_ = is_wgc_plugin;
|
||||
restore_monitor();
|
||||
LOG_INFO("Windows capturer: restarted {} after secure desktop", name);
|
||||
return true;
|
||||
};
|
||||
|
||||
LOG_INFO("Windows capturer: restarting capture backend after secure desktop");
|
||||
impl_->Stop();
|
||||
int ret = impl_->Start(show_cursor);
|
||||
if (ret == 0) {
|
||||
restore_monitor();
|
||||
RestoreMonitor(current_monitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -795,20 +795,14 @@ bool ScreenCapturerWin::RestartCaptureBackendAfterSecureDesktop() {
|
||||
ret = impl_->Start(show_cursor);
|
||||
}
|
||||
if (ret == 0) {
|
||||
restore_monitor();
|
||||
RestoreMonitor(current_monitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (impl_is_wgc_plugin_ &&
|
||||
try_started_backend(WgcPluginCapturer::Create(), "WGC plugin", true)) {
|
||||
return true;
|
||||
}
|
||||
if (try_started_backend(std::make_unique<ScreenCapturerDxgi>(), "DXGI",
|
||||
false)) {
|
||||
return true;
|
||||
}
|
||||
if (try_started_backend(std::make_unique<ScreenCapturerGdi>(), "GDI",
|
||||
false)) {
|
||||
if (TryStartBackend(std::make_unique<ScreenCapturerDxgi>(),
|
||||
current_monitor) ||
|
||||
TryStartBackend(WgcPluginCapturer::Create(), current_monitor) ||
|
||||
TryStartBackend(std::make_unique<ScreenCapturerGdi>(), current_monitor)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1117,14 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
||||
std::vector<uint8_t> secure_frame;
|
||||
|
||||
while (running_.load(std::memory_order_relaxed)) {
|
||||
// A privacy session must NEVER continue using the helper's secure-desktop
|
||||
// GDI frames. Its HWNDs belong to the ordinary interactive desktop only.
|
||||
if (privacy_ && privacy_->Engaged()) {
|
||||
if (!IsWindowsPrivacyDesktopAvailable())
|
||||
privacy_->Fail("Secure desktop or lock screen; remote operation paused. Privacy cannot cover Windows security UI.");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
continue;
|
||||
}
|
||||
if (paused_.load(std::memory_order_relaxed)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
continue;
|
||||
@@ -1295,7 +1297,7 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
||||
if (frame_delivered && !secure_frame.empty()) {
|
||||
EmitCapturedFrame(secure_frame.data(),
|
||||
static_cast<int>(secure_frame.size()), captured_width,
|
||||
captured_height, display_name.c_str());
|
||||
captured_height, display_name.c_str(), nullptr, true);
|
||||
}
|
||||
|
||||
if (!frame_delivered && !frame_pending) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "screen_capturer.h"
|
||||
#include "privacy_controller.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
@@ -43,10 +44,12 @@ class ScreenCapturerWin : public ScreenCapturer {
|
||||
int ResetToInitialMonitor() override;
|
||||
|
||||
std::vector<DisplayInfo> GetDisplayInfoList() override;
|
||||
void SetPrivacyController(PrivacyController* privacy) { privacy_ = privacy; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ScreenCapturer> impl_;
|
||||
bool impl_is_wgc_plugin_ = false;
|
||||
PrivacyController* privacy_ = nullptr;
|
||||
void NotifyPrivacyCapture(bool running);
|
||||
int fps_ = 60;
|
||||
cb_desktop_data cb_;
|
||||
cb_desktop_data cb_orig_;
|
||||
@@ -86,9 +89,13 @@ class ScreenCapturerWin : public ScreenCapturer {
|
||||
|
||||
void BuildCanonicalFromImpl();
|
||||
void RebuildAliasesFromImpl();
|
||||
void RestoreMonitor(int monitor_index);
|
||||
bool TryStartBackend(std::unique_ptr<ScreenCapturer> candidate,
|
||||
int monitor_index);
|
||||
void EmitCapturedFrame(unsigned char* data, int size, int width, int height,
|
||||
const char* stream_id,
|
||||
const MiniRtcNativeVideoFrame* native_frame = nullptr);
|
||||
const MiniRtcNativeVideoFrame* native_frame = nullptr,
|
||||
bool from_secure_desktop = false);
|
||||
void StopSecureCaptureThread();
|
||||
bool RestartCaptureBackendAfterSecureDesktop();
|
||||
void SecureDesktopCaptureLoop();
|
||||
|
||||
@@ -339,6 +339,7 @@ void WgcSessionImpl::OnClosed(
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
|
||||
winrt::Windows::Foundation::IInspectable const&) {
|
||||
std::lock_guard locker(lock_);
|
||||
if (observer_) observer_->OnFrame({}, id_);
|
||||
const bool was_running = is_running_;
|
||||
const bool was_paused = is_paused_;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user