From 288bb96e0cf8783cbe4a5dd426a1f0632c227dc2 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Tue, 21 Oct 2025 17:30:36 +0800 Subject: [PATCH] [feat] add implementation for WGC capture on virtual screens --- ...er_wgc.cpp => screen_capturer_wgc.cpp.bak} | 42 +- .../windows/screen_capturer_wgc.h | 5 + .../windows/screen_capturer_wgc.h.bak | 68 ++++ .../windows/screen_capturer_wgc_warp.cpp | 187 +++++++++ .../windows/wgc_session_impl.cpp | 343 +++++++--------- .../windows/wgc_session_impl.cpp.bak | 380 ++++++++++++++++++ 6 files changed, 799 insertions(+), 226 deletions(-) rename src/screen_capturer/windows/{screen_capturer_wgc.cpp => screen_capturer_wgc.cpp.bak} (87%) create mode 100644 src/screen_capturer/windows/screen_capturer_wgc.h.bak create mode 100644 src/screen_capturer/windows/screen_capturer_wgc_warp.cpp create mode 100644 src/screen_capturer/windows/wgc_session_impl.cpp.bak diff --git a/src/screen_capturer/windows/screen_capturer_wgc.cpp b/src/screen_capturer/windows/screen_capturer_wgc.cpp.bak similarity index 87% rename from src/screen_capturer/windows/screen_capturer_wgc.cpp rename to src/screen_capturer/windows/screen_capturer_wgc.cpp.bak index 5ff52fe..3c31209 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.cpp +++ b/src/screen_capturer/windows/screen_capturer_wgc.cpp.bak @@ -12,7 +12,7 @@ static std::vector gs_display_list; -std::string WideToUtf8(const wchar_t *wideStr) { +std::string WideToUtf8(const wchar_t* wideStr) { int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0, nullptr, nullptr); std::string result(size_needed, 0); @@ -31,14 +31,14 @@ BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, [[maybe_unused]] HDC hdc, if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) { gs_display_list.insert( gs_display_list.begin(), - {(void *)hmonitor, WideToUtf8(monitor_info_.szDevice), + {(void*)hmonitor, WideToUtf8(monitor_info_.szDevice), (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom}); - *(HMONITOR *)data = hmonitor; + *(HMONITOR*)data = hmonitor; } else { gs_display_list.push_back(DisplayInfo( - (void *)hmonitor, WideToUtf8(monitor_info_.szDevice), + (void*)hmonitor, WideToUtf8(monitor_info_.szDevice), (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom)); @@ -81,7 +81,7 @@ bool ScreenCapturerWgc::IsWgcSupported() { /* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */ return winrt::Windows::Foundation::Metadata::ApiInformation:: IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8); - } catch (const winrt::hresult_error &) { + } catch (const winrt::hresult_error&) { return false; } catch (...) { return false; @@ -115,7 +115,7 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { } 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( "index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - " "({}, {})", @@ -243,26 +243,28 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) { return 0; } -void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame, +void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame, int id) { - if (on_data_) { - if (!nv12_frame_) { - nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; - } - - libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4, - (uint8_t *)nv12_frame_, frame.width, - (uint8_t *)(nv12_frame_ + frame.width * frame.height), - frame.width, frame.width, frame.height); - - on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, - frame.height, display_info_list_[id].name.c_str()); + if (!on_data_) { + return; } + + if (!nv12_frame_) { + nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; + } + + libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4, + (uint8_t*)nv12_frame_, frame.width, + (uint8_t*)(nv12_frame_ + frame.width * frame.height), + frame.width, frame.width, frame.height); + + on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, + frame.height, display_info_list_[id].name.c_str()); } void ScreenCapturerWgc::CleanUp() { if (inited_) { - for (auto &session : sessions_) { + for (auto& session : sessions_) { if (session.session_) { session.session_->Stop(); } diff --git a/src/screen_capturer/windows/screen_capturer_wgc.h b/src/screen_capturer/windows/screen_capturer_wgc.h index 73a5228..5cea7c9 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.h +++ b/src/screen_capturer/windows/screen_capturer_wgc.h @@ -43,6 +43,8 @@ class ScreenCapturerWgc : public ScreenCapturer, std::vector display_info_list_; int monitor_index_ = 0; + HWND hwnd_ = nullptr; + private: class WgcSessionInfo { public: @@ -63,6 +65,9 @@ class ScreenCapturerWgc : public ScreenCapturer, unsigned char* nv12_frame_ = nullptr; unsigned char* nv12_frame_scaled_ = nullptr; + + private: + bool CreateHiddenWindow(); }; #endif \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_wgc.h.bak b/src/screen_capturer/windows/screen_capturer_wgc.h.bak new file mode 100644 index 0000000..73a5228 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_wgc.h.bak @@ -0,0 +1,68 @@ +#ifndef _SCREEN_CAPTURER_WGC_H_ +#define _SCREEN_CAPTURER_WGC_H_ + +#include +#include +#include +#include +#include + +#include "screen_capturer.h" +#include "wgc_session.h" +#include "wgc_session_impl.h" + +class ScreenCapturerWgc : public ScreenCapturer, + public WgcSession::wgc_session_observer { + public: + ScreenCapturerWgc(); + ~ScreenCapturerWgc(); + + public: + bool IsWgcSupported(); + + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start() override; + int Stop() override; + + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; + + std::vector GetDisplayInfoList() { return display_info_list_; } + + int SwitchTo(int monitor_index); + + void OnFrame(const WgcSession::wgc_session_frame& frame, int id); + + protected: + void CleanUp(); + + private: + HMONITOR monitor_; + MONITORINFOEX monitor_info_; + std::vector display_info_list_; + int monitor_index_ = 0; + + private: + class WgcSessionInfo { + public: + std::unique_ptr session_; + bool inited_ = false; + bool running_ = false; + bool paused_ = false; + }; + + std::vector sessions_; + + std::atomic_bool running_; + std::atomic_bool inited_; + + int fps_ = 60; + + cb_desktop_data on_data_ = nullptr; + + unsigned char* nv12_frame_ = nullptr; + unsigned char* nv12_frame_scaled_ = nullptr; +}; + +#endif \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_wgc_warp.cpp b/src/screen_capturer/windows/screen_capturer_wgc_warp.cpp new file mode 100644 index 0000000..1036a88 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_wgc_warp.cpp @@ -0,0 +1,187 @@ +#include +#include + +#include +#include + +#include "libyuv.h" +#include "rd_log.h" +#include "screen_capturer_wgc.h" + +// Dummy window proc for hidden window +static LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam) { + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +// ======================= 构造函数 / 析构函数 ======================= +ScreenCapturerWgc::ScreenCapturerWgc() + : monitor_(nullptr), + hwnd_(nullptr), + monitor_index_(0), + running_(false), + inited_(false), + fps_(60), + on_data_(nullptr), + nv12_frame_(nullptr), + nv12_frame_scaled_(nullptr) {} + +ScreenCapturerWgc::~ScreenCapturerWgc() { + Stop(); + CleanUp(); + + if (nv12_frame_) { + delete[] nv12_frame_; + nv12_frame_ = nullptr; + } + + if (nv12_frame_scaled_) { + delete[] nv12_frame_scaled_; + nv12_frame_scaled_ = nullptr; + } +} + +// ======================= 隐藏窗口创建 ======================= +bool ScreenCapturerWgc::CreateHiddenWindow() { + const wchar_t kClassName[] = L"ScreenCapturerHiddenWindow"; + + WNDCLASSW wc = {}; + wc.lpfnWndProc = DummyWndProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = kClassName; + + if (!RegisterClassW(&wc)) { + std::cerr << "Failed to register dummy window class\n"; + return false; + } + + hwnd_ = CreateWindowW(kClassName, L"", WS_OVERLAPPEDWINDOW, 0, 0, 1, 1, + nullptr, nullptr, wc.hInstance, nullptr); + if (!hwnd_) { + std::cerr << "Failed to create dummy window\n"; + return false; + } + + ShowWindow(hwnd_, SW_HIDE); + return true; +} + +// ======================= 初始化 ======================= +int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) { + if (inited_) return 0; + + fps_ = fps; + on_data_ = cb; + + // 创建隐藏窗口 + if (!CreateHiddenWindow()) { + return -1; + } + + // 初始化 WGC Session + sessions_.push_back( + {std::make_unique(0), false, false, false}); + sessions_.back().session_->RegisterObserver(this); + int error = sessions_.back().session_->Initialize(hwnd_); + if (error != 0) { + std::cerr << "WGC session init failed\n"; + return error; + } + sessions_[0].inited_ = true; + + inited_ = true; + return 0; +} + +int ScreenCapturerWgc::Destroy() { + Stop(); + CleanUp(); + return 0; +} + +int ScreenCapturerWgc::Pause(int monitor_index) { + // 目前只支持隐藏窗口,所以忽略 monitor_index + if (!running_) return -1; + sessions_[0].session_->Pause(); + sessions_[0].paused_ = true; + return 0; +} + +int ScreenCapturerWgc::Resume(int monitor_index) { + if (!running_) return -1; + sessions_[0].session_->Resume(); + sessions_[0].paused_ = false; + return 0; +} + +int ScreenCapturerWgc::SwitchTo(int monitor_index) { + // 单隐藏窗口模式,不支持切换 + return 0; +} + +// ======================= 开始 ======================= +int ScreenCapturerWgc::Start() { + if (!inited_) return -1; + if (running_) return 0; + if (sessions_.empty()) return -1; + + sessions_[0].session_->Start(); + sessions_[0].running_ = true; + running_ = true; + + return 0; +} + +// ======================= 停止 ======================= +int ScreenCapturerWgc::Stop() { + if (!running_) return 0; + + if (!sessions_.empty()) { + sessions_[0].session_->Stop(); + sessions_[0].running_ = false; + } + + running_ = false; + + if (hwnd_) { + DestroyWindow(hwnd_); + hwnd_ = nullptr; + } + + inited_ = false; + sessions_.clear(); + + return 0; +} + +// ======================= 帧回调 ======================= +void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame, + int id) { + if (!on_data_) { + return; + } + + if (!nv12_frame_) { + nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; + } + + libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4, + (uint8_t*)nv12_frame_, frame.width, + (uint8_t*)(nv12_frame_ + frame.width * frame.height), + frame.width, frame.width, frame.height); + + on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, + frame.height, "hidden_window"); +} + +// ======================= 清理 ======================= +void ScreenCapturerWgc::CleanUp() { + if (inited_) { + for (auto& session : sessions_) { + if (session.session_) { + session.session_->Stop(); + } + } + sessions_.clear(); + } +} diff --git a/src/screen_capturer/windows/wgc_session_impl.cpp b/src/screen_capturer/windows/wgc_session_impl.cpp index c6f778f..e2ac2ca 100644 --- a/src/screen_capturer/windows/wgc_session_impl.cpp +++ b/src/screen_capturer/windows/wgc_session_impl.cpp @@ -7,6 +7,8 @@ #include #include +#include "rd_log.h" + #define CHECK_INIT \ if (!is_initialized_) { \ std::cout << "AE_NEED_INIT" << std::endl; \ @@ -20,122 +22,118 @@ extern "C" { HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( - ::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); + ::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice); } WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {} WgcSessionImpl::~WgcSessionImpl() { - Stop(); - CleanUp(); + try { + Stop(); + CleanUp(); + } catch (...) { + } } void WgcSessionImpl::Release() { delete this; } int WgcSessionImpl::Initialize(HWND hwnd) { - std::lock_guard locker(lock_); - + std::scoped_lock locker(lock_); target_.hwnd = hwnd; target_.is_window = true; return Initialize(); } int WgcSessionImpl::Initialize(HMONITOR hmonitor) { - std::lock_guard locker(lock_); - + std::scoped_lock locker(lock_); target_.hmonitor = hmonitor; target_.is_window = false; return Initialize(); } -void WgcSessionImpl::RegisterObserver(wgc_session_observer *observer) { - std::lock_guard locker(lock_); +void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { + std::scoped_lock locker(lock_); observer_ = observer; } int WgcSessionImpl::Start() { - std::lock_guard locker(lock_); + std::scoped_lock locker(lock_); + CHECK_INIT; if (is_running_) return 0; - int error = 1; - - CHECK_INIT; 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_item_) { + std::cout << "AE_NO_CAPTURE_ITEM" << std::endl; + LOG_ERROR("No capture item"); + return 2; } - if (!capture_framepool_) throw std::exception(); + 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); - is_running_ = true; + capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_); + capture_frame_size_ = current_size; - // 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_.StartCapture(); + capture_framepool_trigger_ = capture_framepool_.FrameArrived( + winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame}); + capture_close_trigger_ = capture_item_.Closed( + winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed}); capture_session_.IsCursorCaptureEnabled(false); + capture_session_.StartCapture(); - error = 0; - } catch (winrt::hresult_error) { - std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl; + is_running_ = true; + is_paused_ = false; + return 0; + } catch (winrt::hresult_error const& e) { + LOG_ERROR("Create WGC Capture Failed: {}", winrt::to_string(e.message())); return 86; } catch (...) { return 86; } - - return error; } int WgcSessionImpl::Stop() { - std::lock_guard locker(lock_); - + std::scoped_lock locker(lock_); CHECK_INIT; + if (!is_running_) return 0; 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; + try { + if (capture_framepool_trigger_) capture_framepool_trigger_.revoke(); + if (capture_close_trigger_) capture_close_trigger_.revoke(); + if (capture_session_) { + capture_session_.Close(); + capture_session_ = nullptr; + } + if (capture_framepool_) { + capture_framepool_.Close(); + capture_framepool_ = nullptr; + } + } catch (...) { } return 0; } int WgcSessionImpl::Pause() { - std::lock_guard locker(lock_); - - is_paused_ = true; - + std::scoped_lock locker(lock_); CHECK_INIT; + is_paused_ = true; return 0; } int WgcSessionImpl::Resume() { - std::lock_guard locker(lock_); - - is_paused_ = false; - + std::scoped_lock locker(lock_); CHECK_INIT; + is_paused_ = false; return 0; } @@ -143,19 +141,15 @@ auto WgcSessionImpl::CreateD3D11Device() { winrt::com_ptr d3d_device; HRESULT hr; - WINRT_ASSERT(!d3d_device); - - D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE; UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, - D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr); + hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, + nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), + nullptr, nullptr); if (DXGI_ERROR_UNSUPPORTED == hr) { - // change D3D_DRIVER_TYPE - type = D3D_DRIVER_TYPE_WARP; - hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, - D3D11_SDK_VERSION, d3d_device.put(), nullptr, - nullptr); + hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, flags, + nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), + nullptr, nullptr); } winrt::check_hresult(hr); @@ -168,26 +162,28 @@ auto WgcSessionImpl::CreateD3D11Device() { } auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) { - auto activation_factory = winrt::get_activation_factory< - winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); - auto interop_factory = activation_factory.as(); - winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; + auto interop_factory = + winrt::get_activation_factory< + winrt::Windows::Graphics::Capture::GraphicsCaptureItem>() + .as(); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr}; interop_factory->CreateForWindow( hwnd, winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); + reinterpret_cast(winrt::put_abi(item))); return item; } auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) { - auto activation_factory = winrt::get_activation_factory< - winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); - auto interop_factory = activation_factory.as(); - winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; + auto interop_factory = + winrt::get_activation_factory< + winrt::Windows::Graphics::Capture::GraphicsCaptureItem>() + .as(); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr}; interop_factory->CreateForMonitor( hmonitor, winrt::guid_of(), - reinterpret_cast(winrt::put_abi(item))); + reinterpret_cast(winrt::put_abi(item))); return item; } @@ -196,13 +192,10 @@ HRESULT WgcSessionImpl::CreateMappedTexture( unsigned int height) { D3D11_TEXTURE2D_DESC src_desc; src_texture->GetDesc(&src_desc); - D3D11_TEXTURE2D_DESC map_desc; - map_desc.Width = width == 0 ? src_desc.Width : width; - map_desc.Height = height == 0 ? src_desc.Height : height; - map_desc.MipLevels = src_desc.MipLevels; - map_desc.ArraySize = src_desc.ArraySize; - map_desc.Format = src_desc.Format; - map_desc.SampleDesc = src_desc.SampleDesc; + + D3D11_TEXTURE2D_DESC map_desc = src_desc; + map_desc.Width = width ? width : src_desc.Width; + map_desc.Height = height ? height : src_desc.Height; map_desc.Usage = D3D11_USAGE_STAGING; map_desc.BindFlags = 0; map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; @@ -216,163 +209,101 @@ HRESULT WgcSessionImpl::CreateMappedTexture( } void WgcSessionImpl::OnFrame( - winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, - [[maybe_unused]] winrt::Windows::Foundation::IInspectable const &args) { - std::lock_guard locker(lock_); + winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, + [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { + if (!is_running_ || is_paused_) return; + std::scoped_lock locker(lock_); - auto is_new_size = false; + if (!observer_) return; - { - auto frame = sender.TryGetNextFrame(); - auto frame_size = frame.ContentSize(); + auto frame = sender.TryGetNextFrame(); + if (!frame) return; - if (frame_size.Width != capture_frame_size_.Width || - frame_size.Height != capture_frame_size_.Height) { - // The thing we have been capturing has changed size. - // We need to resize our swap chain first, then blit the pixels. - // After we do that, retire the frame and then recreate our frame pool. - is_new_size = true; - capture_frame_size_ = frame_size; - } - - // copy to mapped texture - { - if (is_paused_) { - return; - } - - auto frame_captured = - GetDXGIInterfaceFromObject(frame.Surface()); - - if (!d3d11_texture_mapped_ || is_new_size) - CreateMappedTexture(frame_captured); - - 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()); - } - - // 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) { + auto frame_size = frame.ContentSize(); + bool size_changed = (frame_size.Width != capture_frame_size_.Width || + frame_size.Height != capture_frame_size_.Height); + if (size_changed) { + capture_frame_size_ = frame_size; capture_framepool_.Recreate(d3d11_direct_device_, winrt::Windows::Graphics::DirectX:: DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, capture_frame_size_); } + + auto frame_surface = + GetDXGIInterfaceFromObject(frame.Surface()); + + if (!d3d11_texture_mapped_ || size_changed) + CreateMappedTexture(frame_surface, frame_size.Width, frame_size.Height); + + d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(), + frame_surface.get()); + + D3D11_MAPPED_SUBRESOURCE map_result; + HRESULT hr = d3d11_device_context_->Map(d3d11_texture_mapped_.get(), 0, + D3D11_MAP_READ, 0, &map_result); + if (SUCCEEDED(hr)) { + wgc_session_frame frame_info{ + static_cast(frame_size.Width), + static_cast(frame_size.Height), map_result.RowPitch, + reinterpret_cast(map_result.pData)}; + observer_->OnFrame(frame_info, id_); + d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0); + } } void WgcSessionImpl::OnClosed( - winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &, - winrt::Windows::Foundation::IInspectable const &) { + winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, + winrt::Windows::Foundation::IInspectable const&) { OutputDebugStringW(L"WgcSessionImpl::OnClosed"); + Stop(); } int WgcSessionImpl::Initialize() { if (is_initialized_) return 0; - if (!(d3d11_direct_device_ = CreateD3D11Device())) { + d3d11_direct_device_ = CreateD3D11Device(); + if (!d3d11_direct_device_) { std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl; return 1; } try { - if (target_.is_window) - capture_item_ = CreateCaptureItemForWindow(target_.hwnd); - else - capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor); + capture_item_ = target_.is_window + ? CreateCaptureItemForWindow(target_.hwnd) + : CreateCaptureItemForMonitor(target_.hmonitor); - // Set up - auto d3d11_device = + auto d3d_device = GetDXGIInterfaceFromObject(d3d11_direct_device_); - d3d11_device->GetImmediateContext(d3d11_device_context_.put()); - - } catch (winrt::hresult_error) { - std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl; - return 86; + d3d_device->GetImmediateContext(d3d11_device_context_.put()); } catch (...) { + LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED"); return 86; } is_initialized_ = true; - return 0; } 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(); + std::scoped_lock locker(lock_); + if (cleaned_.exchange(true)) return; + try { + if (capture_framepool_trigger_) capture_framepool_trigger_.revoke(); + if (capture_close_trigger_) capture_close_trigger_.revoke(); if (capture_framepool_) capture_framepool_.Close(); - if (capture_session_) capture_session_.Close(); - - capture_framepool_ = nullptr; - capture_session_ = nullptr; - capture_item_ = nullptr; - - is_initialized_ = false; + } catch (...) { } + + capture_framepool_ = nullptr; + capture_session_ = nullptr; + capture_item_ = nullptr; + d3d11_texture_mapped_ = nullptr; + d3d11_device_context_ = nullptr; + d3d11_direct_device_ = nullptr; + + is_initialized_ = false; + is_running_ = false; } - -LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, - LPARAM l_param) { - return DefWindowProc(window, message, w_param, l_param); -} - -// void WgcSessionImpl::message_func() { -// const std::wstring kClassName = L"am_fake_window"; - -// WNDCLASS wc = {}; - -// wc.style = CS_HREDRAW | CS_VREDRAW; -// wc.lpfnWndProc = DefWindowProc; -// wc.hCursor = LoadCursor(nullptr, IDC_ARROW); -// wc.hbrBackground = reinterpret_cast(COLOR_WINDOW); -// wc.lpszClassName = kClassName.c_str(); - -// if (!::RegisterClassW(&wc)) return; - -// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW, -// 0, -// 0, 0, 0, nullptr, nullptr, nullptr, nullptr); -// MSG msg; -// while (is_running_) { -// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { -// if (!is_running_) break; -// TranslateMessage(&msg); -// DispatchMessage(&msg); -// } -// Sleep(10); -// } - -// ::CloseWindow(hwnd_); -// ::DestroyWindow(hwnd_); -// } \ No newline at end of file diff --git a/src/screen_capturer/windows/wgc_session_impl.cpp.bak b/src/screen_capturer/windows/wgc_session_impl.cpp.bak new file mode 100644 index 0000000..feb2e95 --- /dev/null +++ b/src/screen_capturer/windows/wgc_session_impl.cpp.bak @@ -0,0 +1,380 @@ +#include "wgc_session_impl.h" + +#include + +#include +#include +#include +#include + +#include "rd_log.h" + +#define CHECK_INIT \ + if (!is_initialized_) { \ + std::cout << "AE_NEED_INIT" << std::endl; \ + return 4; \ + } + +#define CHECK_CLOSED \ + if (cleaned_.load() == true) { \ + throw winrt::hresult_error(RO_E_CLOSED); \ + } + +extern "C" { +HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( + ::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice); +} + +WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {} + +WgcSessionImpl::~WgcSessionImpl() { + Stop(); + CleanUp(); +} + +void WgcSessionImpl::Release() { delete this; } + +int WgcSessionImpl::Initialize(HWND hwnd) { + std::lock_guard locker(lock_); + + target_.hwnd = hwnd; + target_.is_window = true; + return Initialize(); +} + +int WgcSessionImpl::Initialize(HMONITOR hmonitor) { + std::lock_guard locker(lock_); + + target_.hmonitor = hmonitor; + target_.is_window = false; + return Initialize(); +} + +void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { + std::lock_guard locker(lock_); + observer_ = observer; +} + +int WgcSessionImpl::Start() { + std::lock_guard locker(lock_); + + if (is_running_) return 0; + + int error = 1; + + CHECK_INIT; + 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_) 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_.StartCapture(); + + capture_session_.IsCursorCaptureEnabled(false); + + error = 0; + } catch (winrt::hresult_error) { + LOG_ERROR("Create WGC Capture Failed"); + return 86; + } catch (...) { + return 86; + } + + return error; +} + +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; + } + + return 0; +} + +int WgcSessionImpl::Pause() { + std::lock_guard locker(lock_); + + is_paused_ = true; + + CHECK_INIT; + return 0; +} + +int WgcSessionImpl::Resume() { + std::lock_guard locker(lock_); + + is_paused_ = false; + + CHECK_INIT; + return 0; +} + +auto WgcSessionImpl::CreateD3D11Device() { + winrt::com_ptr d3d_device; + HRESULT hr; + + WINRT_ASSERT(!d3d_device); + + D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE; + UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, + D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr); + + if (DXGI_ERROR_UNSUPPORTED == hr) { + // change D3D_DRIVER_TYPE + type = D3D_DRIVER_TYPE_WARP; + hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, + D3D11_SDK_VERSION, d3d_device.put(), nullptr, + nullptr); + } + + winrt::check_hresult(hr); + + winrt::com_ptr<::IInspectable> d3d11_device; + winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice( + d3d_device.as().get(), d3d11_device.put())); + return d3d11_device + .as(); +} + +auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) { + auto activation_factory = winrt::get_activation_factory< + winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); + auto interop_factory = activation_factory.as(); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; + interop_factory->CreateForWindow( + hwnd, + winrt::guid_of(), + reinterpret_cast(winrt::put_abi(item))); + return item; +} + +auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) { + auto activation_factory = winrt::get_activation_factory< + winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); + auto interop_factory = activation_factory.as(); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; + interop_factory->CreateForMonitor( + hmonitor, + winrt::guid_of(), + reinterpret_cast(winrt::put_abi(item))); + return item; +} + +HRESULT WgcSessionImpl::CreateMappedTexture( + winrt::com_ptr src_texture, unsigned int width, + unsigned int height) { + D3D11_TEXTURE2D_DESC src_desc; + src_texture->GetDesc(&src_desc); + D3D11_TEXTURE2D_DESC map_desc; + map_desc.Width = width == 0 ? src_desc.Width : width; + map_desc.Height = height == 0 ? src_desc.Height : height; + map_desc.MipLevels = src_desc.MipLevels; + map_desc.ArraySize = src_desc.ArraySize; + map_desc.Format = src_desc.Format; + map_desc.SampleDesc = src_desc.SampleDesc; + map_desc.Usage = D3D11_USAGE_STAGING; + map_desc.BindFlags = 0; + map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + map_desc.MiscFlags = 0; + + auto d3dDevice = + GetDXGIInterfaceFromObject(d3d11_direct_device_); + + return d3dDevice->CreateTexture2D(&map_desc, nullptr, + d3d11_texture_mapped_.put()); +} + +void WgcSessionImpl::OnFrame( + winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, + [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { + std::lock_guard locker(lock_); + + auto is_new_size = false; + + { + auto frame = sender.TryGetNextFrame(); + auto frame_size = frame.ContentSize(); + + if (frame_size.Width != capture_frame_size_.Width || + frame_size.Height != capture_frame_size_.Height) { + // The thing we have been capturing has changed size. + // We need to resize our swap chain first, then blit the pixels. + // After we do that, retire the frame and then recreate our frame pool. + is_new_size = true; + capture_frame_size_ = frame_size; + } + + // copy to mapped texture + { + if (is_paused_) { + return; + } + + auto frame_captured = + GetDXGIInterfaceFromObject(frame.Surface()); + + if (!d3d11_texture_mapped_ || is_new_size) + CreateMappedTexture(frame_captured); + + 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()); + } + + // 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_); + } +} + +void WgcSessionImpl::OnClosed( + winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, + winrt::Windows::Foundation::IInspectable const&) { + OutputDebugStringW(L"WgcSessionImpl::OnClosed"); +} + +int WgcSessionImpl::Initialize() { + if (is_initialized_) return 0; + + if (!(d3d11_direct_device_ = CreateD3D11Device())) { + std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl; + return 1; + } + + try { + if (target_.is_window) + capture_item_ = CreateCaptureItemForWindow(target_.hwnd); + else + capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor); + + // Set up + auto d3d11_device = + GetDXGIInterfaceFromObject(d3d11_direct_device_); + d3d11_device->GetImmediateContext(d3d11_device_context_.put()); + + } catch (winrt::hresult_error) { + LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED"); + return 86; + } catch (...) { + return 86; + } + + is_initialized_ = true; + + return 0; +} + +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(); + + if (capture_framepool_) capture_framepool_.Close(); + + if (capture_session_) capture_session_.Close(); + + capture_framepool_ = nullptr; + capture_session_ = nullptr; + capture_item_ = nullptr; + + is_initialized_ = false; + } +} + +LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param, + LPARAM l_param) { + return DefWindowProc(window, message, w_param, l_param); +} + +// void WgcSessionImpl::message_func() { +// const std::wstring kClassName = L"am_fake_window"; + +// WNDCLASS wc = {}; + +// wc.style = CS_HREDRAW | CS_VREDRAW; +// wc.lpfnWndProc = DefWindowProc; +// wc.hCursor = LoadCursor(nullptr, IDC_ARROW); +// wc.hbrBackground = reinterpret_cast(COLOR_WINDOW); +// wc.lpszClassName = kClassName.c_str(); + +// if (!::RegisterClassW(&wc)) return; + +// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW, +// 0, +// 0, 0, 0, nullptr, nullptr, nullptr, nullptr); +// MSG msg; +// while (is_running_) { +// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { +// if (!is_running_) break; +// TranslateMessage(&msg); +// DispatchMessage(&msg); +// } +// Sleep(10); +// } + +// ::CloseWindow(hwnd_); +// ::DestroyWindow(hwnd_); +// } \ No newline at end of file