[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;
}
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() {
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
} // namespace crossdesk
@@ -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
#endif
@@ -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<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++) {
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<int>(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<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) {
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
} // namespace crossdesk
@@ -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
#endif
@@ -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<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())
mapped_name = it->second;
else
mapped_name = display_name;
mapped_name = raw_display_name;
}
{
std::lock_guard<std::mutex> 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<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,
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<uint8_t> 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;
@@ -59,6 +59,9 @@ class ScreenCapturerWin : public ScreenCapturer {
std::atomic<int> monitor_index_{0};
int initial_monitor_index_ = 0;
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_;
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);
+197 -150
View File
@@ -2,23 +2,10 @@
#include <Windows.Graphics.Capture.Interop.h>
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#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<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForWindow(
winrt::check_hresult(interop_factory->CreateForWindow(
hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item)));
reinterpret_cast<void**>(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<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForMonitor(
winrt::check_hresult(interop_factory->CreateForMonitor(
hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item)));
reinterpret_cast<void**>(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<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_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<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) {
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,
@@ -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<ID3D11DeviceContext> d3d11_device_context_{nullptr};
winrt::com_ptr<ID3D11Texture2D> d3d11_texture_mapped_{nullptr};
std::atomic<bool> cleaned_ = false;
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool
capture_framepool_{nullptr};
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::