From b10a6512fe2c1b846b371d1863fa2947edf10666 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Fri, 27 Feb 2026 13:55:41 +0800 Subject: [PATCH] =?UTF-8?q?[feat]=20add=20Windows=20DXGI/GDI=20screen=20ca?= =?UTF-8?q?pture=20with=20WGC=E2=86=92DXGI=E2=86=92GDI=20fallback=20suppor?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/screen_capturer/screen_capturer_factory.h | 6 +- .../windows/screen_capturer_dxgi.cpp | 333 ++++++++++++++++++ .../windows/screen_capturer_dxgi.h | 79 +++++ .../windows/screen_capturer_gdi.cpp | 206 +++++++++++ .../windows/screen_capturer_gdi.h | 66 ++++ .../windows/screen_capturer_win.cpp | 91 +++++ .../windows/screen_capturer_win.h | 42 +++ xmake.lua | 2 +- 8 files changed, 821 insertions(+), 4 deletions(-) create mode 100644 src/screen_capturer/windows/screen_capturer_dxgi.cpp create mode 100644 src/screen_capturer/windows/screen_capturer_dxgi.h create mode 100644 src/screen_capturer/windows/screen_capturer_gdi.cpp create mode 100644 src/screen_capturer/windows/screen_capturer_gdi.h create mode 100644 src/screen_capturer/windows/screen_capturer_win.cpp create mode 100644 src/screen_capturer/windows/screen_capturer_win.h diff --git a/src/screen_capturer/screen_capturer_factory.h b/src/screen_capturer/screen_capturer_factory.h index 27088e9..d4fdf24 100644 --- a/src/screen_capturer/screen_capturer_factory.h +++ b/src/screen_capturer/screen_capturer_factory.h @@ -8,7 +8,7 @@ #define _SCREEN_CAPTURER_FACTORY_H_ #ifdef _WIN32 -#include "screen_capturer_wgc.h" +#include "screen_capturer_win.h" #elif __linux__ #include "screen_capturer_x11.h" #elif __APPLE__ @@ -25,7 +25,7 @@ class ScreenCapturerFactory { public: ScreenCapturer* Create() { #ifdef _WIN32 - return new ScreenCapturerWgc(); + return new ScreenCapturerWin(); #elif __linux__ return new ScreenCapturerX11(); #elif __APPLE__ @@ -37,4 +37,4 @@ class ScreenCapturerFactory { } }; } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/src/screen_capturer/windows/screen_capturer_dxgi.cpp b/src/screen_capturer/windows/screen_capturer_dxgi.cpp new file mode 100644 index 0000000..b46f8ce --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_dxgi.cpp @@ -0,0 +1,333 @@ +#include "screen_capturer_dxgi.h" + +#include +#include +#include +#include + +#include "libyuv.h" +#include "rd_log.h" + +namespace crossdesk { + +namespace { +std::string WideToUtf8(const std::wstring& wstr) { + if (wstr.empty()) return {}; + int size_needed = WideCharToMultiByte( + CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr); + std::string result(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), result.data(), + size_needed, nullptr, nullptr); + return result; +} + +std::string CleanDisplayName(const std::wstring& wide_name) { + std::string name = WideToUtf8(wide_name); + name.erase(std::remove_if(name.begin(), name.end(), + [](unsigned char c) { return !std::isalnum(c); }), + name.end()); + return name; +} +} // namespace + +ScreenCapturerDxgi::ScreenCapturerDxgi() {} +ScreenCapturerDxgi::~ScreenCapturerDxgi() { + Stop(); + Destroy(); +} + +int ScreenCapturerDxgi::Init(const int fps, cb_desktop_data cb) { + fps_ = fps; + callback_ = cb; + if (!callback_) { + LOG_ERROR("DXGI: callback is null"); + return -1; + } + + if (!InitializeDxgi()) { + LOG_ERROR("DXGI: initialize DXGI failed"); + return -2; + } + + EnumerateDisplays(); + if (display_info_list_.empty()) { + LOG_ERROR("DXGI: no displays found"); + return -3; + } + + monitor_index_ = 0; + return 0; +} + +int ScreenCapturerDxgi::Destroy() { + Stop(); + ReleaseDuplication(); + outputs_.clear(); + d3d_context_.Reset(); + d3d_device_.Reset(); + dxgi_factory_.Reset(); + if (nv12_frame_) { + delete[] nv12_frame_; + nv12_frame_ = nullptr; + nv12_width_ = 0; + nv12_height_ = 0; + } + return 0; +} + +int ScreenCapturerDxgi::Start(bool show_cursor) { + if (running_) return 0; + show_cursor_ = show_cursor; + + if (!CreateDuplicationForMonitor(monitor_index_)) { + LOG_ERROR("DXGI: create duplication failed for monitor {}", + monitor_index_.load()); + return -1; + } + + paused_ = false; + running_ = true; + thread_ = std::thread([this]() { CaptureLoop(); }); + return 0; +} + +int ScreenCapturerDxgi::Stop() { + if (!running_) return 0; + running_ = false; + if (thread_.joinable()) thread_.join(); + ReleaseDuplication(); + return 0; +} + +int ScreenCapturerDxgi::Pause(int monitor_index) { + paused_ = true; + return 0; +} + +int ScreenCapturerDxgi::Resume(int monitor_index) { + paused_ = false; + return 0; +} + +int ScreenCapturerDxgi::SwitchTo(int monitor_index) { + if (monitor_index < 0 || monitor_index >= (int)display_info_list_.size()) { + LOG_ERROR("DXGI: invalid monitor index {}", monitor_index); + return -1; + } + paused_ = true; + monitor_index_ = monitor_index; + ReleaseDuplication(); + if (!CreateDuplicationForMonitor(monitor_index_)) { + LOG_ERROR("DXGI: create duplication failed for monitor {}", + monitor_index_.load()); + return -2; + } + paused_ = false; + LOG_INFO("DXGI: switched to monitor {}:{}", monitor_index_.load(), + display_info_list_[monitor_index_].name); + return 0; +} + +bool ScreenCapturerDxgi::InitializeDxgi() { + UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#ifdef _DEBUG + flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0}; + + D3D_FEATURE_LEVEL out_level{}; + HRESULT hr = D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, feature_levels, + ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, d3d_device_.GetAddressOf(), + &out_level, d3d_context_.GetAddressOf()); + if (FAILED(hr)) { + hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, flags, + feature_levels, ARRAYSIZE(feature_levels), + D3D11_SDK_VERSION, d3d_device_.GetAddressOf(), + &out_level, d3d_context_.GetAddressOf()); + if (FAILED(hr)) { + LOG_ERROR("DXGI: D3D11CreateDevice failed, hr={}", (int)hr); + return false; + } + } + + hr = CreateDXGIFactory1( + __uuidof(IDXGIFactory1), + reinterpret_cast(dxgi_factory_.GetAddressOf())); + if (FAILED(hr)) { + LOG_ERROR("DXGI: CreateDXGIFactory1 failed, hr={}", (int)hr); + return false; + } + return true; +} + +void ScreenCapturerDxgi::EnumerateDisplays() { + display_info_list_.clear(); + outputs_.clear(); + + Microsoft::WRL::ComPtr adapter; + for (UINT a = 0; + dxgi_factory_->EnumAdapters(a, adapter.ReleaseAndGetAddressOf()) != + DXGI_ERROR_NOT_FOUND; + ++a) { + Microsoft::WRL::ComPtr output; + for (UINT o = 0; adapter->EnumOutputs(o, output.ReleaseAndGetAddressOf()) != + DXGI_ERROR_NOT_FOUND; + ++o) { + DXGI_OUTPUT_DESC desc{}; + if (FAILED(output->GetDesc(&desc))) { + continue; + } + std::string name = CleanDisplayName(desc.DeviceName); + MONITORINFOEX mi{}; + mi.cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo(desc.Monitor, &mi)) { + bool is_primary = (mi.dwFlags & MONITORINFOF_PRIMARY) ? true : false; + DisplayInfo info((void*)desc.Monitor, name, is_primary, + mi.rcMonitor.left, mi.rcMonitor.top, + mi.rcMonitor.right, mi.rcMonitor.bottom); + // primary first + if (is_primary) + display_info_list_.insert(display_info_list_.begin(), info); + else + display_info_list_.push_back(info); + outputs_.push_back(output); + } + } + } +} + +bool ScreenCapturerDxgi::CreateDuplicationForMonitor(int monitor_index) { + if (monitor_index < 0 || monitor_index >= (int)outputs_.size()) return false; + Microsoft::WRL::ComPtr output1; + HRESULT hr = outputs_[monitor_index]->QueryInterface( + IID_PPV_ARGS(output1.GetAddressOf())); + if (FAILED(hr)) { + LOG_ERROR("DXGI: Query IDXGIOutput1 failed, hr={}", (int)hr); + return false; + } + + duplication_.Reset(); + hr = output1->DuplicateOutput(d3d_device_.Get(), duplication_.GetAddressOf()); + if (FAILED(hr)) { + LOG_ERROR("DXGI: DuplicateOutput failed, hr={}", (int)hr); + return false; + } + + staging_.Reset(); + return true; +} + +void ScreenCapturerDxgi::ReleaseDuplication() { + staging_.Reset(); + if (duplication_) { + duplication_->ReleaseFrame(); + } + duplication_.Reset(); +} + +void ScreenCapturerDxgi::CaptureLoop() { + const int timeout_ms = 33; + while (running_) { + if (paused_) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + if (!duplication_) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + DXGI_OUTDUPL_FRAME_INFO frame_info{}; + Microsoft::WRL::ComPtr desktop_resource; + HRESULT hr = duplication_->AcquireNextFrame( + timeout_ms, &frame_info, desktop_resource.GetAddressOf()); + if (hr == DXGI_ERROR_WAIT_TIMEOUT) { + continue; + } + if (FAILED(hr)) { + LOG_ERROR("DXGI: AcquireNextFrame failed, hr={}", (int)hr); + // attempt to recreate duplication + ReleaseDuplication(); + CreateDuplicationForMonitor(monitor_index_); + continue; + } + + Microsoft::WRL::ComPtr acquired_tex; + if (desktop_resource) { + hr = desktop_resource->QueryInterface( + IID_PPV_ARGS(acquired_tex.GetAddressOf())); + if (FAILED(hr)) { + duplication_->ReleaseFrame(); + continue; + } + } else { + duplication_->ReleaseFrame(); + continue; + } + + D3D11_TEXTURE2D_DESC src_desc{}; + acquired_tex->GetDesc(&src_desc); + + if (!staging_) { + D3D11_TEXTURE2D_DESC staging_desc = src_desc; + staging_desc.Usage = D3D11_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + staging_desc.MiscFlags = 0; + hr = d3d_device_->CreateTexture2D(&staging_desc, nullptr, + staging_.GetAddressOf()); + if (FAILED(hr)) { + LOG_ERROR("DXGI: CreateTexture2D staging failed, hr={}", (int)hr); + duplication_->ReleaseFrame(); + continue; + } + } + + d3d_context_->CopyResource(staging_.Get(), acquired_tex.Get()); + + D3D11_MAPPED_SUBRESOURCE mapped{}; + hr = d3d_context_->Map(staging_.Get(), 0, D3D11_MAP_READ, 0, &mapped); + if (FAILED(hr)) { + duplication_->ReleaseFrame(); + continue; + } + + int logical_width = static_cast(src_desc.Width); + int even_width = logical_width & ~1; + int even_height = static_cast(src_desc.Height) & ~1; + if (even_width <= 0 || even_height <= 0) { + d3d_context_->Unmap(staging_.Get(), 0); + duplication_->ReleaseFrame(); + continue; + } + + int nv12_size = even_width * even_height * 3 / 2; + if (!nv12_frame_ || nv12_width_ != even_width || + nv12_height_ != even_height) { + delete[] nv12_frame_; + nv12_frame_ = new unsigned char[nv12_size]; + nv12_width_ = even_width; + nv12_height_ = even_height; + } + + libyuv::ARGBToNV12(static_cast(mapped.pData), + static_cast(mapped.RowPitch), nv12_frame_, + even_width, nv12_frame_ + even_width * even_height, + even_width, even_width, even_height); + + if (callback_) { + callback_(nv12_frame_, nv12_size, even_width, even_height, + display_info_list_[monitor_index_].name.c_str()); + } + + d3d_context_->Unmap(staging_.Get(), 0); + duplication_->ReleaseFrame(); + } +} + +} // namespace crossdesk \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_dxgi.h b/src/screen_capturer/windows/screen_capturer_dxgi.h new file mode 100644 index 0000000..e5cdf6b --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_dxgi.h @@ -0,0 +1,79 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-02-27 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _SCREEN_CAPTURER_DXGI_H_ +#define _SCREEN_CAPTURER_DXGI_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rd_log.h" +#include "screen_capturer.h" + +namespace crossdesk { + +class ScreenCapturerDxgi : public ScreenCapturer { + public: + ScreenCapturerDxgi(); + ~ScreenCapturerDxgi(); + + public: + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start(bool show_cursor) override; + int Stop() override; + + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; + + int SwitchTo(int monitor_index) override; + + std::vector GetDisplayInfoList() override { + return display_info_list_; + } + + private: + bool InitializeDxgi(); + void EnumerateDisplays(); + bool CreateDuplicationForMonitor(int monitor_index); + void CaptureLoop(); + void ReleaseDuplication(); + + private: + std::vector display_info_list_; + std::vector> outputs_; + + Microsoft::WRL::ComPtr dxgi_factory_; + Microsoft::WRL::ComPtr d3d_device_; + Microsoft::WRL::ComPtr d3d_context_; + Microsoft::WRL::ComPtr duplication_; + Microsoft::WRL::ComPtr staging_; + + std::atomic running_{false}; + std::atomic paused_{false}; + std::atomic monitor_index_{0}; + std::atomic show_cursor_{true}; + std::thread thread_; + int fps_ = 60; + cb_desktop_data callback_ = nullptr; + + unsigned char* nv12_frame_ = nullptr; + int nv12_width_ = 0; + int nv12_height_ = 0; +}; +} // namespace crossdesk + +#endif \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_gdi.cpp b/src/screen_capturer/windows/screen_capturer_gdi.cpp new file mode 100644 index 0000000..a967245 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_gdi.cpp @@ -0,0 +1,206 @@ +#include "screen_capturer_gdi.h" + +#include +#include +#include +#include + +#include "libyuv.h" +#include "rd_log.h" + +namespace crossdesk { + +namespace { +std::string WideToUtf8(const std::wstring& wstr) { + if (wstr.empty()) return {}; + int size_needed = WideCharToMultiByte( + CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr); + std::string result(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), result.data(), + size_needed, nullptr, nullptr); + return result; +} + +std::string CleanDisplayName(const std::wstring& wide_name) { + std::string name = WideToUtf8(wide_name); + name.erase(std::remove_if(name.begin(), name.end(), + [](unsigned char c) { return !std::isalnum(c); }), + name.end()); + return name; +} +} // namespace + +ScreenCapturerGdi::ScreenCapturerGdi() {} +ScreenCapturerGdi::~ScreenCapturerGdi() { + Stop(); + Destroy(); +} + +BOOL CALLBACK ScreenCapturerGdi::EnumMonitorProc(HMONITOR hMonitor, HDC, LPRECT, + LPARAM data) { + auto displays = reinterpret_cast*>(data); + MONITORINFOEX mi{}; + mi.cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo(hMonitor, &mi)) { + std::string name = CleanDisplayName(mi.szDevice); + bool is_primary = (mi.dwFlags & MONITORINFOF_PRIMARY) ? true : false; + DisplayInfo info((void*)hMonitor, name, is_primary, mi.rcMonitor.left, + mi.rcMonitor.top, mi.rcMonitor.right, mi.rcMonitor.bottom); + if (is_primary) + displays->insert(displays->begin(), info); + else + displays->push_back(info); + } + return TRUE; +} + +void ScreenCapturerGdi::EnumerateDisplays() { + display_info_list_.clear(); + EnumDisplayMonitors(nullptr, nullptr, EnumMonitorProc, + (LPARAM)&display_info_list_); +} + +int ScreenCapturerGdi::Init(const int fps, cb_desktop_data cb) { + fps_ = fps; + callback_ = cb; + if (!callback_) { + LOG_ERROR("GDI: callback is null"); + return -1; + } + EnumerateDisplays(); + if (display_info_list_.empty()) { + LOG_ERROR("GDI: no displays found"); + return -2; + } + monitor_index_ = 0; + return 0; +} + +int ScreenCapturerGdi::Destroy() { + Stop(); + if (nv12_frame_) { + delete[] nv12_frame_; + nv12_frame_ = nullptr; + nv12_width_ = 0; + nv12_height_ = 0; + } + return 0; +} + +int ScreenCapturerGdi::Start(bool show_cursor) { + if (running_) return 0; + show_cursor_ = show_cursor; + paused_ = false; + running_ = true; + thread_ = std::thread([this]() { CaptureLoop(); }); + return 0; +} + +int ScreenCapturerGdi::Stop() { + if (!running_) return 0; + running_ = false; + if (thread_.joinable()) thread_.join(); + return 0; +} + +int ScreenCapturerGdi::Pause(int monitor_index) { + paused_ = true; + return 0; +} + +int ScreenCapturerGdi::Resume(int monitor_index) { + paused_ = false; + return 0; +} + +int ScreenCapturerGdi::SwitchTo(int monitor_index) { + if (monitor_index < 0 || monitor_index >= (int)display_info_list_.size()) { + LOG_ERROR("GDI: invalid monitor index {}", monitor_index); + return -1; + } + monitor_index_ = monitor_index; + LOG_INFO("GDI: switched to monitor {}:{}", monitor_index_.load(), + display_info_list_[monitor_index_].name); + return 0; +} + +void ScreenCapturerGdi::CaptureLoop() { + int interval_ms = fps_ > 0 ? (1000 / fps_) : 16; + HDC screen_dc = GetDC(nullptr); + while (running_) { + if (paused_) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + if (!screen_dc) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + const auto& di = display_info_list_[monitor_index_]; + int left = di.left; + int top = di.top; + int width = di.width & ~1; + int height = di.height & ~1; + if (width <= 0 || height <= 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms)); + continue; + } + + BITMAPINFO bmi{}; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + void* bits = nullptr; + HDC mem_dc = CreateCompatibleDC(screen_dc); + HBITMAP dib = + CreateDIBSection(mem_dc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); + HGDIOBJ old = SelectObject(mem_dc, dib); + + BitBlt(mem_dc, 0, 0, width, height, screen_dc, left, top, + SRCCOPY | CAPTUREBLT); + + if (show_cursor_) { + CURSORINFO ci{}; + ci.cbSize = sizeof(CURSORINFO); + if (GetCursorInfo(&ci) && ci.flags == CURSOR_SHOWING && ci.hCursor) { + POINT pt = ci.ptScreenPos; + int cx = pt.x - left; + int cy = pt.y - top; + if (cx >= -64 && cy >= -64 && cx < width + 64 && cy < height + 64) { + DrawIconEx(mem_dc, cx, cy, ci.hCursor, 0, 0, 0, nullptr, DI_NORMAL); + } + } + } + + int stride_argb = width * 4; + int nv12_size = width * height * 3 / 2; + if (!nv12_frame_ || nv12_width_ != width || nv12_height_ != height) { + delete[] nv12_frame_; + nv12_frame_ = new unsigned char[nv12_size]; + nv12_width_ = width; + nv12_height_ = height; + } + + libyuv::ARGBToNV12(static_cast(bits), stride_argb, + nv12_frame_, width, nv12_frame_ + width * height, width, + width, height); + + if (callback_) { + callback_(nv12_frame_, nv12_size, width, height, di.name.c_str()); + } + + SelectObject(mem_dc, old); + DeleteObject(dib); + DeleteDC(mem_dc); + + std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms)); + } + ReleaseDC(nullptr, screen_dc); +} + +} // namespace crossdesk \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_gdi.h b/src/screen_capturer/windows/screen_capturer_gdi.h new file mode 100644 index 0000000..af30b78 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_gdi.h @@ -0,0 +1,66 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-02-27 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _SCREEN_CAPTURER_GDI_H_ +#define _SCREEN_CAPTURER_GDI_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "rd_log.h" +#include "screen_capturer.h" + +namespace crossdesk { + +class ScreenCapturerGdi : public ScreenCapturer { + public: + ScreenCapturerGdi(); + ~ScreenCapturerGdi(); + + public: + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start(bool show_cursor) override; + int Stop() override; + + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; + + int SwitchTo(int monitor_index) override; + + std::vector GetDisplayInfoList() override { + return display_info_list_; + } + + private: + static BOOL CALLBACK EnumMonitorProc(HMONITOR hMonitor, HDC, LPRECT, + LPARAM data); + void EnumerateDisplays(); + void CaptureLoop(); + + private: + std::vector display_info_list_; + std::atomic running_{false}; + std::atomic paused_{false}; + std::atomic monitor_index_{0}; + std::atomic show_cursor_{true}; + std::thread thread_; + int fps_ = 60; + cb_desktop_data callback_ = nullptr; + + unsigned char* nv12_frame_ = nullptr; + int nv12_width_ = 0; + int nv12_height_ = 0; +}; +} // namespace crossdesk + +#endif \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_win.cpp b/src/screen_capturer/windows/screen_capturer_win.cpp new file mode 100644 index 0000000..70b3521 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_win.cpp @@ -0,0 +1,91 @@ +#include "screen_capturer_win.h" + +#include + +#include "rd_log.h" +#include "screen_capturer_dxgi.h" +#include "screen_capturer_gdi.h" +#include "screen_capturer_wgc.h" + +namespace crossdesk { + +ScreenCapturerWin::ScreenCapturerWin() {} +ScreenCapturerWin::~ScreenCapturerWin() { Destroy(); } + +int ScreenCapturerWin::Init(const int fps, cb_desktop_data cb) { + fps_ = fps; + cb_ = cb; + + int ret = -1; + + impl_ = std::make_unique(); + ret = impl_->Init(fps_, cb_); + if (ret == 0) { + LOG_INFO("Windows capturer: using WGC"); + return 0; + } + + LOG_WARN("Windows capturer: WGC init failed (ret={}), try DXGI", ret); + impl_.reset(); + + impl_ = std::make_unique(); + ret = impl_->Init(fps_, cb_); + if (ret == 0) { + LOG_INFO("Windows capturer: using DXGI Desktop Duplication"); + return 0; + } + + LOG_WARN("Windows capturer: DXGI init failed (ret={}), fallback to GDI", ret); + impl_.reset(); + + impl_ = std::make_unique(); + ret = impl_->Init(fps_, cb_); + if (ret == 0) { + LOG_INFO("Windows capturer: using GDI BitBlt"); + return 0; + } + + LOG_ERROR("Windows capturer: all implementations failed, ret={}", ret); + impl_.reset(); + return -1; +} + +int ScreenCapturerWin::Destroy() { + if (impl_) { + impl_->Destroy(); + impl_.reset(); + } + return 0; +} + +int ScreenCapturerWin::Start(bool show_cursor) { + if (!impl_) return -1; + return impl_->Start(show_cursor); +} + +int ScreenCapturerWin::Stop() { + if (!impl_) return 0; + return impl_->Stop(); +} + +int ScreenCapturerWin::Pause(int monitor_index) { + if (!impl_) return -1; + return impl_->Pause(monitor_index); +} + +int ScreenCapturerWin::Resume(int monitor_index) { + if (!impl_) return -1; + return impl_->Resume(monitor_index); +} + +int ScreenCapturerWin::SwitchTo(int monitor_index) { + if (!impl_) return -1; + return impl_->SwitchTo(monitor_index); +} + +std::vector ScreenCapturerWin::GetDisplayInfoList() { + if (!impl_) return {}; + return impl_->GetDisplayInfoList(); +} + +} // namespace crossdesk \ No newline at end of file diff --git a/src/screen_capturer/windows/screen_capturer_win.h b/src/screen_capturer/windows/screen_capturer_win.h new file mode 100644 index 0000000..82903a3 --- /dev/null +++ b/src/screen_capturer/windows/screen_capturer_win.h @@ -0,0 +1,42 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-02-27 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _SCREEN_CAPTURER_WIN_H_ +#define _SCREEN_CAPTURER_WIN_H_ + +#include +#include + +#include "screen_capturer.h" + +namespace crossdesk { + +class ScreenCapturerWin : public ScreenCapturer { + public: + ScreenCapturerWin(); + ~ScreenCapturerWin(); + + public: + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start(bool show_cursor) override; + int Stop() override; + + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; + + int SwitchTo(int monitor_index) override; + + std::vector GetDisplayInfoList() override; + + private: + std::unique_ptr impl_; + int fps_ = 60; + cb_desktop_data cb_; +}; +} // namespace crossdesk + +#endif \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index b866d39..bb69651 100644 --- a/xmake.lua +++ b/xmake.lua @@ -39,7 +39,7 @@ if is_os("windows") then add_requires("libyuv", "miniaudio 0.11.21") add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32", "SDL3-static", "gdi32", "winmm", "setupapi", "version", - "Imm32", "iphlpapi") + "Imm32", "iphlpapi", "d3d11", "dxgi") add_cxflags("/WX") set_runtimes("MT") elseif is_os("linux") then