[fix] recover Windows capture backends after secure desktop exit

This commit is contained in:
dijunkun
2026-05-27 16:05:00 +08:00
parent 63a79a90ac
commit 2eed1c974e
8 changed files with 491 additions and 179 deletions
@@ -244,6 +244,31 @@ bool ScreenCapturerDxgi::CreateDuplicationForMonitor(int monitor_index) {
return true; return true;
} }
bool ScreenCapturerDxgi::RecreateDuplicationForCurrentMonitor() {
ReleaseDuplication();
int current_monitor = monitor_index_.load();
if (CreateDuplicationForMonitor(current_monitor)) {
return true;
}
EnumerateDisplays();
if (display_info_list_.empty()) {
LOG_ERROR("DXGI: no displays found while recreating duplication");
return false;
}
if (current_monitor < 0 ||
current_monitor >= static_cast<int>(display_info_list_.size())) {
current_monitor = 0;
monitor_index_ = 0;
}
if (CreateDuplicationForMonitor(current_monitor)) {
LOG_INFO("DXGI: recreated duplication for monitor {}",
monitor_index_.load());
return true;
}
return false;
}
void ScreenCapturerDxgi::ReleaseDuplication() { void ScreenCapturerDxgi::ReleaseDuplication() {
staging_.Reset(); staging_.Reset();
if (duplication_) { if (duplication_) {
@@ -254,6 +279,8 @@ void ScreenCapturerDxgi::ReleaseDuplication() {
void ScreenCapturerDxgi::CaptureLoop() { void ScreenCapturerDxgi::CaptureLoop() {
const int timeout_ms = 33; const int timeout_ms = 33;
auto last_duplication_retry =
std::chrono::steady_clock::now() - std::chrono::milliseconds(1000);
while (running_) { while (running_) {
if (paused_) { if (paused_) {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
@@ -261,6 +288,11 @@ void ScreenCapturerDxgi::CaptureLoop() {
} }
if (!duplication_) { if (!duplication_) {
const auto now = std::chrono::steady_clock::now();
if (now - last_duplication_retry >= std::chrono::milliseconds(500)) {
last_duplication_retry = now;
RecreateDuplicationForCurrentMonitor();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue; continue;
} }
@@ -274,9 +306,7 @@ void ScreenCapturerDxgi::CaptureLoop() {
} }
if (FAILED(hr)) { if (FAILED(hr)) {
LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr); LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr);
// attempt to recreate duplication RecreateDuplicationForCurrentMonitor();
ReleaseDuplication();
CreateDuplicationForMonitor(monitor_index_);
continue; continue;
} }
@@ -353,4 +383,4 @@ void ScreenCapturerDxgi::CaptureLoop() {
} }
} }
} // namespace crossdesk } // namespace crossdesk
@@ -50,6 +50,7 @@ class ScreenCapturerDxgi : public ScreenCapturer {
bool InitializeDxgi(); bool InitializeDxgi();
void EnumerateDisplays(); void EnumerateDisplays();
bool CreateDuplicationForMonitor(int monitor_index); bool CreateDuplicationForMonitor(int monitor_index);
bool RecreateDuplicationForCurrentMonitor();
void CaptureLoop(); void CaptureLoop();
void ReleaseDuplication(); void ReleaseDuplication();
@@ -78,4 +79,4 @@ class ScreenCapturerDxgi : public ScreenCapturer {
}; };
} // namespace crossdesk } // namespace crossdesk
#endif #endif
@@ -100,8 +100,7 @@ bool ScreenCapturerWgc::IsWgcSupported() {
} }
int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
int error = 0; if (inited_ == true) return 0;
if (inited_ == true) return error;
// nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2]; // nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
// nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2]; // nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
@@ -112,8 +111,18 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
if (!IsWgcSupported()) { if (!IsWgcSupported()) {
LOG_ERROR("WGC not supported"); LOG_ERROR("WGC not supported");
error = 2; return 2;
return error; }
return RebuildSessions(monitor_index_);
}
int ScreenCapturerWgc::RebuildSessions(int preferred_monitor_index) {
CleanUp();
if (!IsWgcSupported()) {
LOG_ERROR("WGC not supported");
return 2;
} }
monitor_ = GetPrimaryMonitor(); monitor_ = GetPrimaryMonitor();
@@ -125,6 +134,13 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
return -1; return -1;
} }
if (preferred_monitor_index < 0 ||
preferred_monitor_index >= static_cast<int>(display_info_list_.size())) {
preferred_monitor_index = 0;
}
monitor_index_ = preferred_monitor_index;
int error = 0;
for (int i = 0; i < display_info_list_.size(); i++) { for (int i = 0; i < display_info_list_.size(); i++) {
const auto& display = display_info_list_[i]; const auto& display = display_info_list_[i];
LOG_INFO( LOG_INFO(
@@ -138,20 +154,28 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
sessions_.back().session_->RegisterObserver(this); sessions_.back().session_->RegisterObserver(this);
error = sessions_.back().session_->Initialize((HMONITOR)display.handle); error = sessions_.back().session_->Initialize((HMONITOR)display.handle);
if (error != 0) { if (error != 0) {
LOG_ERROR("WGC: initialize session {} failed, ret={}", i, error);
CleanUp();
return error; return error;
} }
sessions_[i].inited_ = true; sessions_[i].inited_ = true;
inited_ = true;
} }
LOG_INFO("Default on monitor {}:{}", monitor_index_, LOG_INFO("Default on monitor {}:{}", monitor_index_,
display_info_list_[monitor_index_].name); display_info_list_[monitor_index_].name);
initial_monitor_index_ = monitor_index_; if (initial_monitor_index_ < 0 ||
initial_monitor_index_ >= static_cast<int>(display_info_list_.size())) {
initial_monitor_index_ = monitor_index_;
}
inited_ = true;
return 0; return 0;
} }
int ScreenCapturerWgc::Destroy() { return 0; } int ScreenCapturerWgc::Destroy() {
CleanUp();
return 0;
}
int ScreenCapturerWgc::Start(bool show_cursor) { int ScreenCapturerWgc::Start(bool show_cursor) {
if (running_ == true) { if (running_ == true) {
@@ -160,13 +184,37 @@ int ScreenCapturerWgc::Start(bool show_cursor) {
} }
if (inited_ == false) { if (inited_ == false) {
LOG_ERROR("Screen capturer not inited"); const int ret = RebuildSessions(monitor_index_);
return 4; if (ret != 0) {
LOG_ERROR("Screen capturer not inited");
return ret;
}
} }
int ret = StartSessions(show_cursor);
if (ret == 0) {
return 0;
}
LOG_WARN("WGC: start failed, rebuilding sessions");
ret = RebuildSessions(monitor_index_);
if (ret != 0) {
return ret;
}
return StartSessions(show_cursor);
}
int ScreenCapturerWgc::StartSessions(bool show_cursor) {
bool any_started = false; bool any_started = false;
bool active_started = false;
int last_error = 0; int last_error = 0;
for (int i = 0; i < sessions_.size(); i++) { int active_monitor = monitor_index_;
if (active_monitor < 0 ||
active_monitor >= static_cast<int>(sessions_.size())) {
active_monitor = 0;
monitor_index_ = 0;
}
for (int i = 0; i < static_cast<int>(sessions_.size()); i++) {
if (sessions_[i].inited_ == false) { if (sessions_[i].inited_ == false) {
LOG_ERROR("Session {} not inited", i); LOG_ERROR("Session {} not inited", i);
continue; continue;
@@ -182,16 +230,27 @@ int ScreenCapturerWgc::Start(bool show_cursor) {
continue; continue;
} }
if (i != 0) { if (i != active_monitor) {
sessions_[i].session_->Pause(); sessions_[i].session_->Pause();
sessions_[i].paused_ = true; sessions_[i].paused_ = true;
} else {
sessions_[i].session_->Resume();
sessions_[i].paused_ = false;
} }
sessions_[i].running_ = true; sessions_[i].running_ = true;
any_started = true; any_started = true;
if (i == active_monitor) {
active_started = true;
}
} }
running_ = running_ || any_started;
} }
running_ = active_started;
if (!active_started) {
LOG_ERROR("WGC: active session did not start successfully");
Stop();
return last_error != 0 ? last_error : -1;
}
if (!any_started) { if (!any_started) {
LOG_ERROR("WGC: no session started successfully"); LOG_ERROR("WGC: no session started successfully");
return last_error != 0 ? last_error : -1; return last_error != 0 ? last_error : -1;
@@ -349,13 +408,16 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
} }
void ScreenCapturerWgc::CleanUp() { void ScreenCapturerWgc::CleanUp() {
if (inited_) { running_ = false;
for (auto& session : sessions_) { for (auto& session : sessions_) {
if (session.session_) { if (session.session_) {
session.session_->Stop(); session.session_->Stop();
}
} }
sessions_.clear();
} }
sessions_.clear();
display_info_list_.clear();
gs_display_list.clear();
monitor_ = nullptr;
inited_ = false;
} }
} // namespace crossdesk } // namespace crossdesk
@@ -40,6 +40,8 @@ class ScreenCapturerWgc : public ScreenCapturer,
protected: protected:
void CleanUp(); void CleanUp();
int RebuildSessions(int preferred_monitor_index);
int StartSessions(bool show_cursor);
private: private:
HMONITOR monitor_; HMONITOR monitor_;
@@ -74,4 +76,4 @@ class ScreenCapturerWgc : public ScreenCapturer,
std::mutex frame_mutex_; std::mutex frame_mutex_;
}; };
} // namespace crossdesk } // namespace crossdesk
#endif #endif
@@ -33,6 +33,8 @@ constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 500;
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120; constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500; constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500;
constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000; constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000;
constexpr DWORD kPostSecureDesktopRestartRetryMs = 500;
constexpr DWORD kPostSecureDesktopRestartTimeoutMs = 10000;
constexpr int kSecureDesktopCaptureMinFps = 30; constexpr int kSecureDesktopCaptureMinFps = 30;
constexpr int kSecureDesktopCaptureMaxIntervalMs = constexpr int kSecureDesktopCaptureMaxIntervalMs =
1000 / kSecureDesktopCaptureMinFps; 1000 / kSecureDesktopCaptureMinFps;
@@ -392,21 +394,46 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) {
return; return;
} }
const char* raw_display_name = display_name ? display_name : "";
std::string mapped_name; std::string mapped_name;
{ {
std::lock_guard<std::mutex> lock(alias_mutex_); std::lock_guard<std::mutex> lock(alias_mutex_);
auto it = label_alias_.find(display_name); auto it = label_alias_.find(raw_display_name);
if (it != label_alias_.end()) if (it != label_alias_.end())
mapped_name = it->second; mapped_name = it->second;
else else
mapped_name = display_name; mapped_name = raw_display_name;
} }
{ {
std::lock_guard<std::mutex> lock(alias_mutex_); std::lock_guard<std::mutex> lock(alias_mutex_);
if (canonical_labels_.find(mapped_name) == canonical_labels_.end()) { if (canonical_labels_.find(mapped_name) == canonical_labels_.end()) {
if (post_secure_desktop_waiting_for_frame_.load(
std::memory_order_relaxed) &&
!post_secure_desktop_drop_logged_.exchange(
true, std::memory_order_relaxed)) {
LOG_WARN(
"Windows capturer dropping post-secure-desktop frame from "
"unknown display: display='{}', mapped='{}', size={}x{}, "
"bytes={}",
raw_display_name, mapped_name, w, h, size);
}
return; return;
} }
} }
if (post_secure_desktop_waiting_for_frame_.exchange(
false, std::memory_order_relaxed)) {
const ULONGLONG start_tick =
post_secure_desktop_started_tick_.exchange(
0, std::memory_order_relaxed);
const ULONGLONG elapsed_ms =
start_tick == 0 ? 0 : GetTickCount64() - start_tick;
post_secure_desktop_drop_logged_.store(false,
std::memory_order_relaxed);
LOG_INFO(
"Windows capturer first normal frame after secure desktop: "
"display='{}', mapped='{}', size={}x{}, bytes={}, elapsed_ms={}",
raw_display_name, mapped_name, w, h, size, elapsed_ms);
}
if (cb_orig_) cb_orig_(data, size, w, h, mapped_name.c_str()); if (cb_orig_) cb_orig_(data, size, w, h, mapped_name.c_str());
}; };
@@ -524,6 +551,10 @@ int ScreenCapturerWin::Start(bool show_cursor) {
running_.store(true, std::memory_order_relaxed); running_.store(true, std::memory_order_relaxed);
secure_desktop_capture_active_.store(false, std::memory_order_relaxed); secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
post_secure_desktop_waiting_for_frame_.store(false,
std::memory_order_relaxed);
post_secure_desktop_drop_logged_.store(false, std::memory_order_relaxed);
post_secure_desktop_started_tick_.store(0, std::memory_order_relaxed);
if (!secure_capture_thread_.joinable()) { if (!secure_capture_thread_.joinable()) {
secure_capture_thread_ = secure_capture_thread_ =
std::thread([this]() { SecureDesktopCaptureLoop(); }); std::thread([this]() { SecureDesktopCaptureLoop(); });
@@ -534,6 +565,10 @@ int ScreenCapturerWin::Start(bool show_cursor) {
int ScreenCapturerWin::Stop() { int ScreenCapturerWin::Stop() {
running_.store(false, std::memory_order_relaxed); running_.store(false, std::memory_order_relaxed);
secure_desktop_capture_active_.store(false, std::memory_order_relaxed); secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
post_secure_desktop_waiting_for_frame_.store(false,
std::memory_order_relaxed);
post_secure_desktop_drop_logged_.store(false, std::memory_order_relaxed);
post_secure_desktop_started_tick_.store(0, std::memory_order_relaxed);
int ret = 0; int ret = 0;
if (impl_) { if (impl_) {
ret = impl_->Stop(); ret = impl_->Stop();
@@ -626,6 +661,93 @@ void ScreenCapturerWin::StopSecureCaptureThread() {
} }
} }
bool ScreenCapturerWin::RestartCaptureBackendAfterSecureDesktop() {
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();
return true;
}
LOG_WARN(
"Windows capturer: capture backend restart after secure desktop failed "
"(ret={}), rebuilding backend",
ret);
impl_->Destroy();
ret = impl_->Init(fps_, cb_);
if (ret == 0) {
ret = impl_->Start(show_cursor);
}
if (ret == 0) {
restore_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)) {
return true;
}
if (impl_) {
LOG_WARN(
"Windows capturer: all backend restart attempts after secure desktop "
"failed (last_ret={})",
ret);
}
return false;
}
bool ScreenCapturerWin::GetCurrentCaptureRegion(int* left, int* top, int* width, bool ScreenCapturerWin::GetCurrentCaptureRegion(int* left, int* top, int* width,
int* height, int* height,
std::string* display_name) { std::string* display_name) {
@@ -900,6 +1022,9 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
std::string last_stage; std::string last_stage;
std::string last_service_error; std::string last_service_error;
ULONGLONG capture_stage_started_tick = 0; ULONGLONG capture_stage_started_tick = 0;
bool post_secure_restart_pending = false;
ULONGLONG post_secure_restart_deadline_tick = 0;
ULONGLONG last_post_secure_restart_tick = 0;
SecureDesktopServiceStatus status; SecureDesktopServiceStatus status;
std::vector<uint8_t> secure_frame; std::vector<uint8_t> secure_frame;
@@ -951,6 +1076,10 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
std::memory_order_relaxed); std::memory_order_relaxed);
if (status.capture_active != last_capture_active || if (status.capture_active != last_capture_active ||
status.interactive_stage != last_stage) { status.interactive_stage != last_stage) {
const bool secure_capture_started =
!last_capture_active && status.capture_active;
const bool secure_capture_ended =
last_capture_active && !status.capture_active;
capture_stage_started_tick = now; capture_stage_started_tick = now;
LOG_INFO( LOG_INFO(
"Windows capturer secure desktop state: active={}, stage='{}', " "Windows capturer secure desktop state: active={}, stage='{}', "
@@ -959,12 +1088,46 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
status.active_session_id); status.active_session_id);
last_capture_active = status.capture_active; last_capture_active = status.capture_active;
last_stage = status.interactive_stage; last_stage = status.interactive_stage;
if (secure_capture_started) {
post_secure_restart_pending = false;
post_secure_desktop_waiting_for_frame_.store(
false, std::memory_order_relaxed);
post_secure_desktop_drop_logged_.store(
false, std::memory_order_relaxed);
post_secure_desktop_started_tick_.store(
0, std::memory_order_relaxed);
} else if (secure_capture_ended) {
post_secure_restart_pending = true;
post_secure_restart_deadline_tick =
now + kPostSecureDesktopRestartTimeoutMs;
last_post_secure_restart_tick = 0;
post_secure_desktop_waiting_for_frame_.store(
true, std::memory_order_relaxed);
post_secure_desktop_drop_logged_.store(
false, std::memory_order_relaxed);
post_secure_desktop_started_tick_.store(
now, std::memory_order_relaxed);
}
} }
last_status_tick = now; last_status_tick = now;
} }
if (!status.capture_active || status.active_session_id == 0xFFFFFFFF) { if (!status.capture_active || status.active_session_id == 0xFFFFFFFF) {
StopSecureDesktopSharedCapture(secure_shared_session_id_); StopSecureDesktopSharedCapture(secure_shared_session_id_);
if (post_secure_restart_pending) {
if (now >= post_secure_restart_deadline_tick) {
LOG_WARN(
"Windows capturer: capture backend restart after secure desktop "
"timed out");
post_secure_restart_pending = false;
} else if (last_post_secure_restart_tick == 0 ||
now - last_post_secure_restart_tick >=
kPostSecureDesktopRestartRetryMs) {
last_post_secure_restart_tick = now;
post_secure_restart_pending =
!RestartCaptureBackendAfterSecureDesktop();
}
}
std::this_thread::sleep_for( std::this_thread::sleep_for(
std::chrono::milliseconds(status.service_available ? 50 : 200)); std::chrono::milliseconds(status.service_available ? 50 : 200));
continue; continue;
@@ -59,6 +59,9 @@ class ScreenCapturerWin : public ScreenCapturer {
std::atomic<int> monitor_index_{0}; std::atomic<int> monitor_index_{0};
int initial_monitor_index_ = 0; int initial_monitor_index_ = 0;
std::atomic<bool> secure_desktop_capture_active_{false}; std::atomic<bool> secure_desktop_capture_active_{false};
std::atomic<bool> post_secure_desktop_waiting_for_frame_{false};
std::atomic<bool> post_secure_desktop_drop_logged_{false};
std::atomic<ULONGLONG> post_secure_desktop_started_tick_{0};
std::thread secure_capture_thread_; std::thread secure_capture_thread_;
HANDLE secure_frame_mapping_ = nullptr; HANDLE secure_frame_mapping_ = nullptr;
HANDLE secure_frame_ready_event_ = nullptr; HANDLE secure_frame_ready_event_ = nullptr;
@@ -77,6 +80,7 @@ class ScreenCapturerWin : public ScreenCapturer {
void BuildCanonicalFromImpl(); void BuildCanonicalFromImpl();
void RebuildAliasesFromImpl(); void RebuildAliasesFromImpl();
void StopSecureCaptureThread(); void StopSecureCaptureThread();
bool RestartCaptureBackendAfterSecureDesktop();
void SecureDesktopCaptureLoop(); void SecureDesktopCaptureLoop();
bool GetCurrentCaptureRegion(int* left, int* top, int* width, int* height, bool GetCurrentCaptureRegion(int* left, int* top, int* width, int* height,
std::string* display_name); std::string* display_name);
+197 -150
View File
@@ -2,23 +2,10 @@
#include <Windows.Graphics.Capture.Interop.h> #include <Windows.Graphics.Capture.Interop.h>
#include <atomic> #include <string>
#include <functional>
#include <memory>
#include "rd_log.h" #include "rd_log.h"
#define CHECK_INIT \
if (!is_initialized_) { \
LOG_ERROR("AE_NEED_INIT"); \
return 4; \
}
#define CHECK_CLOSED \
if (cleaned_.load() == true) { \
throw winrt::hresult_error(RO_E_CLOSED); \
}
namespace crossdesk { namespace crossdesk {
extern "C" { extern "C" {
@@ -40,7 +27,7 @@ int WgcSessionImpl::Initialize(HWND hwnd) {
target_.hwnd = hwnd; target_.hwnd = hwnd;
target_.is_window = true; target_.is_window = true;
return Initialize(); return InitializeLocked();
} }
int WgcSessionImpl::Initialize(HMONITOR hmonitor) { int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
@@ -48,7 +35,7 @@ int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
target_.hmonitor = hmonitor; target_.hmonitor = hmonitor;
target_.is_window = false; target_.is_window = false;
return Initialize(); return InitializeLocked();
} }
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
@@ -59,68 +46,13 @@ void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
int WgcSessionImpl::Start(bool show_cursor) { int WgcSessionImpl::Start(bool show_cursor) {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
if (is_running_) return 0; return StartLocked(show_cursor);
int error = 1;
CHECK_INIT;
try {
last_show_cursor_ = show_cursor;
if (!capture_session_) {
auto current_size = capture_item_.Size();
capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
}
if (!capture_framepool_) throw std::exception();
is_running_ = true;
// we do not need to crate a thread to enter a message loop coz we use
// CreateFreeThreaded instead of Create to create a capture frame pool,
// we need to test the performance later
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
capture_session_.IsCursorCaptureEnabled(show_cursor);
capture_session_.StartCapture();
error = 0;
} catch (winrt::hresult_error) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86;
} catch (...) {
return 86;
}
return error;
} }
int WgcSessionImpl::Stop() { int WgcSessionImpl::Stop() {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
CHECK_INIT; CleanUpLocked();
is_running_ = false;
// if (loop_.joinable()) loop_.join();
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_session_) {
capture_session_.Close();
capture_session_ = nullptr;
}
return 0; return 0;
} }
@@ -129,7 +61,10 @@ int WgcSessionImpl::Pause() {
is_paused_ = true; is_paused_ = true;
CHECK_INIT; if (!is_initialized_) {
LOG_ERROR("AE_NEED_INIT");
return 4;
}
return 0; return 0;
} }
@@ -138,7 +73,10 @@ int WgcSessionImpl::Resume() {
is_paused_ = false; is_paused_ = false;
CHECK_INIT; if (!is_initialized_) {
LOG_ERROR("AE_NEED_INIT");
return 4;
}
return 0; return 0;
} }
@@ -175,10 +113,10 @@ auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>(); auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForWindow( winrt::check_hresult(interop_factory->CreateForWindow(
hwnd, hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item))); reinterpret_cast<void**>(winrt::put_abi(item))));
return item; return item;
} }
@@ -187,10 +125,10 @@ auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>(); auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForMonitor( winrt::check_hresult(interop_factory->CreateForMonitor(
hmonitor, hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item))); reinterpret_cast<void**>(winrt::put_abi(item))));
return item; return item;
} }
@@ -218,6 +156,104 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
d3d11_texture_mapped_.put()); d3d11_texture_mapped_.put());
} }
int WgcSessionImpl::StartCaptureLocked(bool show_cursor) {
if (!is_initialized_) {
LOG_ERROR("AE_NEED_INIT");
return 4;
}
if (!capture_item_) {
LOG_ERROR("WGC: capture item is null");
return 4;
}
try {
if (!capture_session_) {
auto current_size = capture_item_.Size();
capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
}
if (!capture_framepool_ || !capture_session_) {
throw std::exception();
}
capture_session_.IsCursorCaptureEnabled(show_cursor);
capture_session_.StartCapture();
is_running_ = true;
return 0;
} catch (const winrt::hresult_error&) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86;
} catch (...) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86;
}
}
int WgcSessionImpl::StartLocked(bool show_cursor) {
if (is_running_) return 0;
last_show_cursor_ = show_cursor;
if (!is_initialized_) {
const int init_ret = InitializeLocked();
if (init_ret != 0) {
return init_ret;
}
}
int ret = StartCaptureLocked(show_cursor);
if (ret == 0) {
return 0;
}
LOG_WARN("WGC: start capture failed, rebuilding capture item");
CleanUpLocked();
ret = InitializeLocked();
if (ret != 0) {
return ret;
}
return StartCaptureLocked(show_cursor);
}
void WgcSessionImpl::StopLocked() {
is_running_ = false;
// if (loop_.joinable()) loop_.join();
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_close_trigger_) capture_close_trigger_.revoke();
if (capture_session_) {
try {
capture_session_.Close();
} catch (...) {
LOG_WARN("WGC: capture session close failed");
}
capture_session_ = nullptr;
}
if (capture_framepool_) {
try {
capture_framepool_.Close();
} catch (...) {
LOG_WARN("WGC: frame pool close failed");
}
capture_framepool_ = nullptr;
}
d3d11_texture_mapped_ = nullptr;
}
void WgcSessionImpl::OnFrame( void WgcSessionImpl::OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
@@ -225,7 +261,7 @@ void WgcSessionImpl::OnFrame(
auto is_new_size = false; auto is_new_size = false;
{ try {
auto frame = sender.TryGetNextFrame(); auto frame = sender.TryGetNextFrame();
auto frame_size = frame.ContentSize(); auto frame_size = frame.ContentSize();
@@ -239,60 +275,63 @@ void WgcSessionImpl::OnFrame(
} }
// copy to mapped texture // copy to mapped texture
{ if (is_paused_) {
if (is_paused_) { return;
return;
}
auto frame_captured =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
if (!d3d11_texture_mapped_ || is_new_size) {
HRESULT tex_hr = CreateMappedTexture(frame_captured);
if (FAILED(tex_hr)) {
OutputDebugStringW(
(L"CreateMappedTexture failed: " + std::to_wstring(tex_hr))
.c_str());
return;
}
}
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_captured.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
0 /*coz we use CreateFreeThreaded, so we cant use flags
D3D11_MAP_FLAG_DO_NOT_WAIT*/
,
&map_result);
if (FAILED(hr)) {
OutputDebugStringW(
(L"map resource failed: " + std::to_wstring(hr)).c_str());
return;
}
// copy data from map_result.pData
if (map_result.pData && observer_) {
observer_->OnFrame(
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height),
map_result.RowPitch,
const_cast<const unsigned char*>(
(unsigned char*)map_result.pData)},
id_);
}
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
} }
}
if (is_new_size) { auto frame_captured =
capture_framepool_.Recreate(d3d11_direct_device_, GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized, if (!d3d11_texture_mapped_ || is_new_size) {
2, capture_frame_size_); HRESULT tex_hr = CreateMappedTexture(frame_captured);
if (FAILED(tex_hr)) {
OutputDebugStringW(
(L"CreateMappedTexture failed: " + std::to_wstring(tex_hr))
.c_str());
return;
}
}
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_captured.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
0 /*coz we use CreateFreeThreaded, so we cant use flags
D3D11_MAP_FLAG_DO_NOT_WAIT*/
,
&map_result);
if (FAILED(hr)) {
OutputDebugStringW(
(L"map resource failed: " + std::to_wstring(hr)).c_str());
return;
}
// copy data from map_result.pData
if (map_result.pData && observer_) {
observer_->OnFrame(
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height),
map_result.RowPitch,
const_cast<const unsigned char*>(
(unsigned char*)map_result.pData)},
id_);
}
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
if (is_new_size) {
capture_framepool_.Recreate(
d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, capture_frame_size_);
}
} catch (const winrt::hresult_error&) {
LOG_WARN("WGC: frame processing failed");
} catch (...) {
LOG_WARN("WGC: frame processing failed");
} }
} }
@@ -300,11 +339,13 @@ void WgcSessionImpl::OnClosed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const&) { winrt::Windows::Foundation::IInspectable const&) {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
const bool was_running = is_running_;
const bool was_paused = is_paused_;
try { try {
CleanUp(); CleanUpLocked();
is_initialized_ = false; is_paused_ = was_paused;
if (Initialize() == 0) { if (InitializeLocked() == 0) {
int ret = Start(last_show_cursor_); int ret = was_running ? StartCaptureLocked(last_show_cursor_) : 0;
if (ret == 0) { if (ret == 0) {
OutputDebugStringW(L"WgcSessionImpl::OnClosed: auto recovered"); OutputDebugStringW(L"WgcSessionImpl::OnClosed: auto recovered");
} else { } else {
@@ -319,9 +360,14 @@ void WgcSessionImpl::OnClosed(
} }
} }
int WgcSessionImpl::Initialize() { int WgcSessionImpl::InitializeLocked() {
if (is_initialized_) return 0; if (is_initialized_) return 0;
d3d11_texture_mapped_ = nullptr;
d3d11_device_context_ = nullptr;
d3d11_direct_device_ = nullptr;
capture_frame_size_ = {};
if (!(d3d11_direct_device_ = CreateD3D11Device())) { if (!(d3d11_direct_device_ = CreateD3D11Device())) {
LOG_ERROR("AE_D3D_CREATE_DEVICE_FAILED"); LOG_ERROR("AE_D3D_CREATE_DEVICE_FAILED");
return 1; return 1;
@@ -332,6 +378,10 @@ int WgcSessionImpl::Initialize() {
capture_item_ = CreateCaptureItemForWindow(target_.hwnd); capture_item_ = CreateCaptureItemForWindow(target_.hwnd);
else else
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor); capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
if (!capture_item_) {
LOG_ERROR("WGC: create capture item returned null");
return 86;
}
// Set up // Set up
auto d3d11_device = auto d3d11_device =
@@ -353,21 +403,18 @@ int WgcSessionImpl::Initialize() {
void WgcSessionImpl::CleanUp() { void WgcSessionImpl::CleanUp() {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
auto expected = false; CleanUpLocked();
if (cleaned_.compare_exchange_strong(expected, true)) { }
capture_close_trigger_.revoke();
capture_framepool_trigger_.revoke();
if (capture_framepool_) capture_framepool_.Close(); void WgcSessionImpl::CleanUpLocked() {
StopLocked();
if (capture_session_) capture_session_.Close(); capture_item_ = nullptr;
d3d11_device_context_ = nullptr;
capture_framepool_ = nullptr; d3d11_direct_device_ = nullptr;
capture_session_ = nullptr; capture_frame_size_ = {};
capture_item_ = nullptr; is_initialized_ = false;
is_paused_ = false;
is_initialized_ = false;
}
} }
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
@@ -68,8 +68,12 @@ class WgcSessionImpl : public WgcSession {
void OnClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, void OnClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const&); winrt::Windows::Foundation::IInspectable const&);
int Initialize(); int InitializeLocked();
int StartLocked(bool show_cursor);
int StartCaptureLocked(bool show_cursor);
void StopLocked();
void CleanUp(); void CleanUp();
void CleanUpLocked();
// void message_func(); // void message_func();
@@ -94,7 +98,6 @@ class WgcSessionImpl : public WgcSession {
winrt::com_ptr<ID3D11DeviceContext> d3d11_device_context_{nullptr}; winrt::com_ptr<ID3D11DeviceContext> d3d11_device_context_{nullptr};
winrt::com_ptr<ID3D11Texture2D> d3d11_texture_mapped_{nullptr}; winrt::com_ptr<ID3D11Texture2D> d3d11_texture_mapped_{nullptr};
std::atomic<bool> cleaned_ = false;
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool
capture_framepool_{nullptr}; capture_framepool_{nullptr};
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool:: winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::