From 2eed1c974e27d24abd47922ae5ff65560a3182b9 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Wed, 27 May 2026 16:05:00 +0800 Subject: [PATCH] [fix] recover Windows capture backends after secure desktop exit --- .../windows/screen_capturer_dxgi.cpp | 38 +- .../windows/screen_capturer_dxgi.h | 3 +- .../windows/screen_capturer_wgc.cpp | 100 ++++- .../windows/screen_capturer_wgc.h | 4 +- .../windows/screen_capturer_win.cpp | 167 ++++++++- .../windows/screen_capturer_win.h | 4 + .../windows/wgc_session_impl.cpp | 347 ++++++++++-------- .../windows/wgc_session_impl.h | 7 +- 8 files changed, 491 insertions(+), 179 deletions(-) diff --git a/src/screen_capturer/windows/screen_capturer_dxgi.cpp b/src/screen_capturer/windows/screen_capturer_dxgi.cpp index f497ffd..e55e118 100644 --- a/src/screen_capturer/windows/screen_capturer_dxgi.cpp +++ b/src/screen_capturer/windows/screen_capturer_dxgi.cpp @@ -244,6 +244,31 @@ bool ScreenCapturerDxgi::CreateDuplicationForMonitor(int monitor_index) { 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(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() { staging_.Reset(); if (duplication_) { @@ -254,6 +279,8 @@ void ScreenCapturerDxgi::ReleaseDuplication() { void ScreenCapturerDxgi::CaptureLoop() { const int timeout_ms = 33; + auto last_duplication_retry = + std::chrono::steady_clock::now() - std::chrono::milliseconds(1000); while (running_) { if (paused_) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -261,6 +288,11 @@ void ScreenCapturerDxgi::CaptureLoop() { } 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)); continue; } @@ -274,9 +306,7 @@ void ScreenCapturerDxgi::CaptureLoop() { } if (FAILED(hr)) { LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr); - // attempt to recreate duplication - ReleaseDuplication(); - CreateDuplicationForMonitor(monitor_index_); + RecreateDuplicationForCurrentMonitor(); continue; } @@ -353,4 +383,4 @@ void ScreenCapturerDxgi::CaptureLoop() { } } -} // namespace crossdesk \ No newline at end of file +} // namespace crossdesk diff --git a/src/screen_capturer/windows/screen_capturer_dxgi.h b/src/screen_capturer/windows/screen_capturer_dxgi.h index 83e21f2..5c4db83 100644 --- a/src/screen_capturer/windows/screen_capturer_dxgi.h +++ b/src/screen_capturer/windows/screen_capturer_dxgi.h @@ -50,6 +50,7 @@ class ScreenCapturerDxgi : public ScreenCapturer { bool InitializeDxgi(); void EnumerateDisplays(); bool CreateDuplicationForMonitor(int monitor_index); + bool RecreateDuplicationForCurrentMonitor(); void CaptureLoop(); void ReleaseDuplication(); @@ -78,4 +79,4 @@ class ScreenCapturerDxgi : public ScreenCapturer { }; } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/src/screen_capturer/windows/screen_capturer_wgc.cpp b/src/screen_capturer/windows/screen_capturer_wgc.cpp index bcc1cd7..d8634cc 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.cpp +++ b/src/screen_capturer/windows/screen_capturer_wgc.cpp @@ -100,8 +100,7 @@ bool ScreenCapturerWgc::IsWgcSupported() { } int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { - int error = 0; - if (inited_ == true) return error; + if (inited_ == true) return 0; // nv12_frame_ = new unsigned char[rect.right * rect.bottom * 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()) { LOG_ERROR("WGC not supported"); - error = 2; - return error; + return 2; + } + + return RebuildSessions(monitor_index_); +} + +int ScreenCapturerWgc::RebuildSessions(int preferred_monitor_index) { + CleanUp(); + + if (!IsWgcSupported()) { + LOG_ERROR("WGC not supported"); + return 2; } monitor_ = GetPrimaryMonitor(); @@ -125,6 +134,13 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { return -1; } + if (preferred_monitor_index < 0 || + preferred_monitor_index >= static_cast(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++) { const auto& display = display_info_list_[i]; LOG_INFO( @@ -138,20 +154,28 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { sessions_.back().session_->RegisterObserver(this); error = sessions_.back().session_->Initialize((HMONITOR)display.handle); if (error != 0) { + LOG_ERROR("WGC: initialize session {} failed, ret={}", i, error); + CleanUp(); return error; } sessions_[i].inited_ = true; - inited_ = true; } LOG_INFO("Default on monitor {}:{}", monitor_index_, display_info_list_[monitor_index_].name); - initial_monitor_index_ = monitor_index_; + if (initial_monitor_index_ < 0 || + initial_monitor_index_ >= static_cast(display_info_list_.size())) { + initial_monitor_index_ = monitor_index_; + } + inited_ = true; return 0; } -int ScreenCapturerWgc::Destroy() { return 0; } +int ScreenCapturerWgc::Destroy() { + CleanUp(); + return 0; +} int ScreenCapturerWgc::Start(bool show_cursor) { if (running_ == true) { @@ -160,13 +184,37 @@ int ScreenCapturerWgc::Start(bool show_cursor) { } if (inited_ == false) { - LOG_ERROR("Screen capturer not inited"); - return 4; + const int ret = RebuildSessions(monitor_index_); + 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 active_started = false; 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(sessions_.size())) { + active_monitor = 0; + monitor_index_ = 0; + } + for (int i = 0; i < static_cast(sessions_.size()); i++) { if (sessions_[i].inited_ == false) { LOG_ERROR("Session {} not inited", i); continue; @@ -182,16 +230,27 @@ int ScreenCapturerWgc::Start(bool show_cursor) { continue; } - if (i != 0) { + if (i != active_monitor) { sessions_[i].session_->Pause(); sessions_[i].paused_ = true; + } else { + sessions_[i].session_->Resume(); + sessions_[i].paused_ = false; } sessions_[i].running_ = 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) { LOG_ERROR("WGC: no session started successfully"); return last_error != 0 ? last_error : -1; @@ -349,13 +408,16 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame, } void ScreenCapturerWgc::CleanUp() { - if (inited_) { - for (auto& session : sessions_) { - if (session.session_) { - session.session_->Stop(); - } + running_ = false; + for (auto& session : sessions_) { + if (session.session_) { + session.session_->Stop(); } - sessions_.clear(); } + sessions_.clear(); + display_info_list_.clear(); + gs_display_list.clear(); + monitor_ = nullptr; + inited_ = false; } -} // namespace crossdesk \ No newline at end of file +} // namespace crossdesk diff --git a/src/screen_capturer/windows/screen_capturer_wgc.h b/src/screen_capturer/windows/screen_capturer_wgc.h index dc507a5..35bb98d 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.h +++ b/src/screen_capturer/windows/screen_capturer_wgc.h @@ -40,6 +40,8 @@ class ScreenCapturerWgc : public ScreenCapturer, protected: void CleanUp(); + int RebuildSessions(int preferred_monitor_index); + int StartSessions(bool show_cursor); private: HMONITOR monitor_; @@ -74,4 +76,4 @@ class ScreenCapturerWgc : public ScreenCapturer, std::mutex frame_mutex_; }; } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/src/screen_capturer/windows/screen_capturer_win.cpp b/src/screen_capturer/windows/screen_capturer_win.cpp index 3275d10..8a50a56 100644 --- a/src/screen_capturer/windows/screen_capturer_win.cpp +++ b/src/screen_capturer/windows/screen_capturer_win.cpp @@ -33,6 +33,8 @@ constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 500; constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120; constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500; constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000; +constexpr DWORD kPostSecureDesktopRestartRetryMs = 500; +constexpr DWORD kPostSecureDesktopRestartTimeoutMs = 10000; constexpr int kSecureDesktopCaptureMinFps = 30; constexpr int kSecureDesktopCaptureMaxIntervalMs = 1000 / kSecureDesktopCaptureMinFps; @@ -392,21 +394,46 @@ int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) { return; } + const char* raw_display_name = display_name ? display_name : ""; std::string mapped_name; { std::lock_guard lock(alias_mutex_); - auto it = label_alias_.find(display_name); + auto it = label_alias_.find(raw_display_name); if (it != label_alias_.end()) mapped_name = it->second; else - mapped_name = display_name; + mapped_name = raw_display_name; } { std::lock_guard lock(alias_mutex_); 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; } } + 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()); }; @@ -524,6 +551,10 @@ int ScreenCapturerWin::Start(bool show_cursor) { running_.store(true, 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()) { secure_capture_thread_ = std::thread([this]() { SecureDesktopCaptureLoop(); }); @@ -534,6 +565,10 @@ int ScreenCapturerWin::Start(bool show_cursor) { int ScreenCapturerWin::Stop() { 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, + 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; if (impl_) { 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 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(), "DXGI", + false)) { + return true; + } + if (try_started_backend(std::make_unique(), "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, int* height, std::string* display_name) { @@ -900,6 +1022,9 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() { std::string last_stage; std::string last_service_error; 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; std::vector secure_frame; @@ -951,6 +1076,10 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() { std::memory_order_relaxed); if (status.capture_active != last_capture_active || 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; LOG_INFO( "Windows capturer secure desktop state: active={}, stage='{}', " @@ -959,12 +1088,46 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() { status.active_session_id); last_capture_active = status.capture_active; 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; } if (!status.capture_active || status.active_session_id == 0xFFFFFFFF) { 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::chrono::milliseconds(status.service_available ? 50 : 200)); continue; diff --git a/src/screen_capturer/windows/screen_capturer_win.h b/src/screen_capturer/windows/screen_capturer_win.h index 0f9e956..1196825 100644 --- a/src/screen_capturer/windows/screen_capturer_win.h +++ b/src/screen_capturer/windows/screen_capturer_win.h @@ -59,6 +59,9 @@ class ScreenCapturerWin : public ScreenCapturer { std::atomic monitor_index_{0}; int initial_monitor_index_ = 0; std::atomic secure_desktop_capture_active_{false}; + std::atomic post_secure_desktop_waiting_for_frame_{false}; + std::atomic post_secure_desktop_drop_logged_{false}; + std::atomic post_secure_desktop_started_tick_{0}; std::thread secure_capture_thread_; HANDLE secure_frame_mapping_ = nullptr; HANDLE secure_frame_ready_event_ = nullptr; @@ -77,6 +80,7 @@ class ScreenCapturerWin : public ScreenCapturer { void BuildCanonicalFromImpl(); void RebuildAliasesFromImpl(); void StopSecureCaptureThread(); + bool RestartCaptureBackendAfterSecureDesktop(); void SecureDesktopCaptureLoop(); bool GetCurrentCaptureRegion(int* left, int* top, int* width, int* height, std::string* display_name); diff --git a/src/screen_capturer/windows/wgc_session_impl.cpp b/src/screen_capturer/windows/wgc_session_impl.cpp index 5b68bcc..d73cac1 100644 --- a/src/screen_capturer/windows/wgc_session_impl.cpp +++ b/src/screen_capturer/windows/wgc_session_impl.cpp @@ -2,23 +2,10 @@ #include -#include -#include -#include +#include #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 { extern "C" { @@ -40,7 +27,7 @@ int WgcSessionImpl::Initialize(HWND hwnd) { target_.hwnd = hwnd; target_.is_window = true; - return Initialize(); + return InitializeLocked(); } int WgcSessionImpl::Initialize(HMONITOR hmonitor) { @@ -48,7 +35,7 @@ int WgcSessionImpl::Initialize(HMONITOR hmonitor) { target_.hmonitor = hmonitor; target_.is_window = false; - return Initialize(); + return InitializeLocked(); } void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { @@ -59,68 +46,13 @@ void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { int WgcSessionImpl::Start(bool show_cursor) { std::lock_guard locker(lock_); - if (is_running_) return 0; - - 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; + return StartLocked(show_cursor); } int WgcSessionImpl::Stop() { std::lock_guard locker(lock_); - CHECK_INIT; - - 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; - } - + CleanUpLocked(); return 0; } @@ -129,7 +61,10 @@ int WgcSessionImpl::Pause() { is_paused_ = true; - CHECK_INIT; + if (!is_initialized_) { + LOG_ERROR("AE_NEED_INIT"); + return 4; + } return 0; } @@ -138,7 +73,10 @@ int WgcSessionImpl::Resume() { is_paused_ = false; - CHECK_INIT; + if (!is_initialized_) { + LOG_ERROR("AE_NEED_INIT"); + return 4; + } return 0; } @@ -175,10 +113,10 @@ auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) { winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); auto interop_factory = activation_factory.as(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; - interop_factory->CreateForWindow( + winrt::check_hresult(interop_factory->CreateForWindow( hwnd, winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); + reinterpret_cast(winrt::put_abi(item)))); return item; } @@ -187,10 +125,10 @@ auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) { winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); auto interop_factory = activation_factory.as(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; - interop_factory->CreateForMonitor( + winrt::check_hresult(interop_factory->CreateForMonitor( hmonitor, winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); + reinterpret_cast(winrt::put_abi(item)))); return item; } @@ -218,6 +156,104 @@ HRESULT WgcSessionImpl::CreateMappedTexture( 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( winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { @@ -225,7 +261,7 @@ void WgcSessionImpl::OnFrame( auto is_new_size = false; - { + try { auto frame = sender.TryGetNextFrame(); auto frame_size = frame.ContentSize(); @@ -239,60 +275,63 @@ void WgcSessionImpl::OnFrame( } // copy to mapped texture - { - if (is_paused_) { - return; - } - - auto frame_captured = - GetDXGIInterfaceFromObject(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(frame_size.Width), - static_cast(frame_size.Height), - map_result.RowPitch, - const_cast( - (unsigned char*)map_result.pData)}, - id_); - } - - d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0); + if (is_paused_) { + return; } - } - if (is_new_size) { - capture_framepool_.Recreate(d3d11_direct_device_, - winrt::Windows::Graphics::DirectX:: - DirectXPixelFormat::B8G8R8A8UIntNormalized, - 2, capture_frame_size_); + auto frame_captured = + GetDXGIInterfaceFromObject(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(frame_size.Width), + static_cast(frame_size.Height), + map_result.RowPitch, + const_cast( + (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::Foundation::IInspectable const&) { std::lock_guard locker(lock_); + const bool was_running = is_running_; + const bool was_paused = is_paused_; try { - CleanUp(); - is_initialized_ = false; - if (Initialize() == 0) { - int ret = Start(last_show_cursor_); + CleanUpLocked(); + is_paused_ = was_paused; + if (InitializeLocked() == 0) { + int ret = was_running ? StartCaptureLocked(last_show_cursor_) : 0; if (ret == 0) { OutputDebugStringW(L"WgcSessionImpl::OnClosed: auto recovered"); } else { @@ -319,9 +360,14 @@ void WgcSessionImpl::OnClosed( } } -int WgcSessionImpl::Initialize() { +int WgcSessionImpl::InitializeLocked() { 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())) { LOG_ERROR("AE_D3D_CREATE_DEVICE_FAILED"); return 1; @@ -332,6 +378,10 @@ int WgcSessionImpl::Initialize() { capture_item_ = CreateCaptureItemForWindow(target_.hwnd); else capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor); + if (!capture_item_) { + LOG_ERROR("WGC: create capture item returned null"); + return 86; + } // Set up auto d3d11_device = @@ -353,21 +403,18 @@ int WgcSessionImpl::Initialize() { void WgcSessionImpl::CleanUp() { std::lock_guard locker(lock_); - auto expected = false; - if (cleaned_.compare_exchange_strong(expected, true)) { - capture_close_trigger_.revoke(); - capture_framepool_trigger_.revoke(); + CleanUpLocked(); +} - if (capture_framepool_) capture_framepool_.Close(); +void WgcSessionImpl::CleanUpLocked() { + StopLocked(); - if (capture_session_) capture_session_.Close(); - - capture_framepool_ = nullptr; - capture_session_ = nullptr; - capture_item_ = nullptr; - - is_initialized_ = false; - } + capture_item_ = nullptr; + d3d11_device_context_ = nullptr; + d3d11_direct_device_ = nullptr; + capture_frame_size_ = {}; + is_initialized_ = false; + is_paused_ = false; } LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, diff --git a/src/screen_capturer/windows/wgc_session_impl.h b/src/screen_capturer/windows/wgc_session_impl.h index f27edda..9dc6f4b 100644 --- a/src/screen_capturer/windows/wgc_session_impl.h +++ b/src/screen_capturer/windows/wgc_session_impl.h @@ -68,8 +68,12 @@ class WgcSessionImpl : public WgcSession { void OnClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem 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 CleanUpLocked(); // void message_func(); @@ -94,7 +98,6 @@ class WgcSessionImpl : public WgcSession { winrt::com_ptr d3d11_device_context_{nullptr}; winrt::com_ptr d3d11_texture_mapped_{nullptr}; - std::atomic cleaned_ = false; winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool capture_framepool_{nullptr}; winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::