[feat] add implementation for WGC capture on virtual screens

This commit is contained in:
dijunkun
2025-10-21 17:30:36 +08:00
parent 5fed09c1aa
commit 288bb96e0c
6 changed files with 799 additions and 226 deletions

View File

@@ -245,7 +245,10 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame, void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
int id) { int id) {
if (on_data_) { if (!on_data_) {
return;
}
if (!nv12_frame_) { if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
} }
@@ -258,7 +261,6 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame,
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, display_info_list_[id].name.c_str()); frame.height, display_info_list_[id].name.c_str());
} }
}
void ScreenCapturerWgc::CleanUp() { void ScreenCapturerWgc::CleanUp() {
if (inited_) { if (inited_) {

View File

@@ -43,6 +43,8 @@ class ScreenCapturerWgc : public ScreenCapturer,
std::vector<DisplayInfo> display_info_list_; std::vector<DisplayInfo> display_info_list_;
int monitor_index_ = 0; int monitor_index_ = 0;
HWND hwnd_ = nullptr;
private: private:
class WgcSessionInfo { class WgcSessionInfo {
public: public:
@@ -63,6 +65,9 @@ class ScreenCapturerWgc : public ScreenCapturer,
unsigned char* nv12_frame_ = nullptr; unsigned char* nv12_frame_ = nullptr;
unsigned char* nv12_frame_scaled_ = nullptr; unsigned char* nv12_frame_scaled_ = nullptr;
private:
bool CreateHiddenWindow();
}; };
#endif #endif

View File

@@ -0,0 +1,68 @@
#ifndef _SCREEN_CAPTURER_WGC_H_
#define _SCREEN_CAPTURER_WGC_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include <vector>
#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<DisplayInfo> 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<DisplayInfo> display_info_list_;
int monitor_index_ = 0;
private:
class WgcSessionInfo {
public:
std::unique_ptr<WgcSession> session_;
bool inited_ = false;
bool running_ = false;
bool paused_ = false;
};
std::vector<WgcSessionInfo> 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

View File

@@ -0,0 +1,187 @@
#include <Windows.h>
#include <d3d11_4.h>
#include <iostream>
#include <thread>
#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<WgcSessionImpl>(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();
}
}

View File

@@ -7,6 +7,8 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include "rd_log.h"
#define CHECK_INIT \ #define CHECK_INIT \
if (!is_initialized_) { \ if (!is_initialized_) { \
std::cout << "AE_NEED_INIT" << std::endl; \ std::cout << "AE_NEED_INIT" << std::endl; \
@@ -26,43 +28,47 @@ HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {} WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
WgcSessionImpl::~WgcSessionImpl() { WgcSessionImpl::~WgcSessionImpl() {
try {
Stop(); Stop();
CleanUp(); CleanUp();
} catch (...) {
}
} }
void WgcSessionImpl::Release() { delete this; } void WgcSessionImpl::Release() { delete this; }
int WgcSessionImpl::Initialize(HWND hwnd) { int WgcSessionImpl::Initialize(HWND hwnd) {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
target_.hwnd = hwnd; target_.hwnd = hwnd;
target_.is_window = true; target_.is_window = true;
return Initialize(); return Initialize();
} }
int WgcSessionImpl::Initialize(HMONITOR hmonitor) { int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
target_.hmonitor = hmonitor; target_.hmonitor = hmonitor;
target_.is_window = false; target_.is_window = false;
return Initialize(); return Initialize();
} }
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
observer_ = observer; observer_ = observer;
} }
int WgcSessionImpl::Start() { int WgcSessionImpl::Start() {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
CHECK_INIT;
if (is_running_) return 0; if (is_running_) return 0;
int error = 1;
CHECK_INIT;
try { try {
if (!capture_session_) { if (!capture_item_) {
std::cout << "AE_NO_CAPTURE_ITEM" << std::endl;
LOG_ERROR("No capture item");
return 2;
}
auto current_size = capture_item_.Size(); auto current_size = capture_item_.Size();
capture_framepool_ = capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool:: winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
@@ -70,72 +76,64 @@ int WgcSessionImpl::Start() {
winrt::Windows::Graphics::DirectX:: winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized, DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size); 2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_); capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size; capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived( capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame}); winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed( capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed}); 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); capture_session_.IsCursorCaptureEnabled(false);
capture_session_.StartCapture();
error = 0; is_running_ = true;
} catch (winrt::hresult_error) { is_paused_ = false;
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl; return 0;
} catch (winrt::hresult_error const& e) {
LOG_ERROR("Create WGC Capture Failed: {}", winrt::to_string(e.message()));
return 86; return 86;
} catch (...) { } catch (...) {
return 86; return 86;
} }
return error;
} }
int WgcSessionImpl::Stop() { int WgcSessionImpl::Stop() {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
CHECK_INIT; CHECK_INIT;
if (!is_running_) return 0;
is_running_ = false; is_running_ = false;
// if (loop_.joinable()) loop_.join(); try {
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke(); if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_close_trigger_) capture_close_trigger_.revoke();
if (capture_session_) { if (capture_session_) {
capture_session_.Close(); capture_session_.Close();
capture_session_ = nullptr; capture_session_ = nullptr;
} }
if (capture_framepool_) {
capture_framepool_.Close();
capture_framepool_ = nullptr;
}
} catch (...) {
}
return 0; return 0;
} }
int WgcSessionImpl::Pause() { int WgcSessionImpl::Pause() {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
is_paused_ = true;
CHECK_INIT; CHECK_INIT;
is_paused_ = true;
return 0; return 0;
} }
int WgcSessionImpl::Resume() { int WgcSessionImpl::Resume() {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
is_paused_ = false;
CHECK_INIT; CHECK_INIT;
is_paused_ = false;
return 0; return 0;
} }
@@ -143,19 +141,15 @@ auto WgcSessionImpl::CreateD3D11Device() {
winrt::com_ptr<ID3D11Device> d3d_device; winrt::com_ptr<ID3D11Device> d3d_device;
HRESULT hr; HRESULT hr;
WINRT_ASSERT(!d3d_device);
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE;
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags,
D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr); nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(),
nullptr, nullptr);
if (DXGI_ERROR_UNSUPPORTED == hr) { if (DXGI_ERROR_UNSUPPORTED == hr) {
// change D3D_DRIVER_TYPE hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, flags,
type = D3D_DRIVER_TYPE_WARP; nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(),
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, nullptr, nullptr);
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
nullptr);
} }
winrt::check_hresult(hr); winrt::check_hresult(hr);
@@ -168,10 +162,11 @@ auto WgcSessionImpl::CreateD3D11Device() {
} }
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) { auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
auto activation_factory = winrt::get_activation_factory< auto interop_factory =
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); winrt::get_activation_factory<
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem>()
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; .as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
interop_factory->CreateForWindow( interop_factory->CreateForWindow(
hwnd, hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
@@ -180,10 +175,11 @@ auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
} }
auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) { auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
auto activation_factory = winrt::get_activation_factory< auto interop_factory =
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); winrt::get_activation_factory<
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem>()
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr}; .as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
interop_factory->CreateForMonitor( interop_factory->CreateForMonitor(
hmonitor, hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
@@ -196,13 +192,10 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
unsigned int height) { unsigned int height) {
D3D11_TEXTURE2D_DESC src_desc; D3D11_TEXTURE2D_DESC src_desc;
src_texture->GetDesc(&src_desc); src_texture->GetDesc(&src_desc);
D3D11_TEXTURE2D_DESC map_desc;
map_desc.Width = width == 0 ? src_desc.Width : width; D3D11_TEXTURE2D_DESC map_desc = src_desc;
map_desc.Height = height == 0 ? src_desc.Height : height; map_desc.Width = width ? width : src_desc.Width;
map_desc.MipLevels = src_desc.MipLevels; map_desc.Height = height ? height : src_desc.Height;
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.Usage = D3D11_USAGE_STAGING;
map_desc.BindFlags = 0; map_desc.BindFlags = 0;
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
@@ -218,161 +211,99 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
void WgcSessionImpl::OnFrame( void WgcSessionImpl::OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
std::lock_guard locker(lock_); 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 = sender.TryGetNextFrame();
if (!frame) return;
auto frame_size = frame.ContentSize(); auto frame_size = frame.ContentSize();
bool size_changed = (frame_size.Width != capture_frame_size_.Width ||
if (frame_size.Width != capture_frame_size_.Width || frame_size.Height != capture_frame_size_.Height);
frame_size.Height != capture_frame_size_.Height) { if (size_changed) {
// 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; capture_frame_size_ = frame_size;
}
// copy to mapped texture
{
if (is_paused_) {
return;
}
auto frame_captured =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(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<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_, capture_framepool_.Recreate(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX:: winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized, DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, capture_frame_size_); 2, capture_frame_size_);
} }
auto frame_surface =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(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<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height), map_result.RowPitch,
reinterpret_cast<const unsigned char*>(map_result.pData)};
observer_->OnFrame(frame_info, id_);
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
}
} }
void WgcSessionImpl::OnClosed( void WgcSessionImpl::OnClosed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const&) { winrt::Windows::Foundation::IInspectable const&) {
OutputDebugStringW(L"WgcSessionImpl::OnClosed"); OutputDebugStringW(L"WgcSessionImpl::OnClosed");
Stop();
} }
int WgcSessionImpl::Initialize() { int WgcSessionImpl::Initialize() {
if (is_initialized_) return 0; 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; std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl;
return 1; return 1;
} }
try { try {
if (target_.is_window) capture_item_ = target_.is_window
capture_item_ = CreateCaptureItemForWindow(target_.hwnd); ? CreateCaptureItemForWindow(target_.hwnd)
else : CreateCaptureItemForMonitor(target_.hmonitor);
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
// Set up auto d3d_device =
auto d3d11_device =
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_); GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
d3d11_device->GetImmediateContext(d3d11_device_context_.put()); d3d_device->GetImmediateContext(d3d11_device_context_.put());
} catch (winrt::hresult_error) {
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
return 86;
} catch (...) { } catch (...) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86; return 86;
} }
is_initialized_ = true; is_initialized_ = true;
return 0; return 0;
} }
void WgcSessionImpl::CleanUp() { void WgcSessionImpl::CleanUp() {
std::lock_guard locker(lock_); std::scoped_lock locker(lock_);
if (cleaned_.exchange(true)) return;
auto expected = false;
if (cleaned_.compare_exchange_strong(expected, true)) {
capture_close_trigger_.revoke();
capture_framepool_trigger_.revoke();
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_framepool_) capture_framepool_.Close();
if (capture_session_) capture_session_.Close(); if (capture_session_) capture_session_.Close();
} catch (...) {
}
capture_framepool_ = nullptr; capture_framepool_ = nullptr;
capture_session_ = nullptr; capture_session_ = nullptr;
capture_item_ = nullptr; capture_item_ = nullptr;
d3d11_texture_mapped_ = nullptr;
d3d11_device_context_ = nullptr;
d3d11_direct_device_ = nullptr;
is_initialized_ = false; 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<HBRUSH>(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_);
// }

View File

@@ -0,0 +1,380 @@
#include "wgc_session_impl.h"
#include <Windows.Graphics.Capture.Interop.h>
#include <atomic>
#include <functional>
#include <iostream>
#include <memory>
#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<ID3D11Device> 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<IDXGIDevice>().get(), d3d11_device.put()));
return d3d11_device
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForWindow(
hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(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<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForMonitor(
hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item)));
return item;
}
HRESULT WgcSessionImpl::CreateMappedTexture(
winrt::com_ptr<ID3D11Texture2D> 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<ID3D11Device>(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<ID3D11Texture2D>(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<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_);
}
}
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<ID3D11Device>(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<HBRUSH>(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_);
// }