mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 12:15:34 +08:00
[feat] add implementation for WGC capture on virtual screens
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
static std::vector<DisplayInfo> gs_display_list;
|
static std::vector<DisplayInfo> 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,
|
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0,
|
||||||
nullptr, nullptr);
|
nullptr, nullptr);
|
||||||
std::string result(size_needed, 0);
|
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) {
|
if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) {
|
||||||
gs_display_list.insert(
|
gs_display_list.insert(
|
||||||
gs_display_list.begin(),
|
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_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
|
||||||
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
||||||
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom});
|
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom});
|
||||||
*(HMONITOR *)data = hmonitor;
|
*(HMONITOR*)data = hmonitor;
|
||||||
} else {
|
} else {
|
||||||
gs_display_list.push_back(DisplayInfo(
|
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_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
|
||||||
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
||||||
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom));
|
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom));
|
||||||
@@ -81,7 +81,7 @@ bool ScreenCapturerWgc::IsWgcSupported() {
|
|||||||
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
|
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
|
||||||
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
||||||
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
|
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
|
||||||
} catch (const winrt::hresult_error &) {
|
} catch (const winrt::hresult_error&) {
|
||||||
return false;
|
return false;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return false;
|
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++) {
|
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(
|
LOG_INFO(
|
||||||
"index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - "
|
"index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - "
|
||||||
"({}, {})",
|
"({}, {})",
|
||||||
@@ -243,26 +243,28 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_) {
|
||||||
if (!nv12_frame_) {
|
return;
|
||||||
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 (!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() {
|
void ScreenCapturerWgc::CleanUp() {
|
||||||
if (inited_) {
|
if (inited_) {
|
||||||
for (auto &session : sessions_) {
|
for (auto& session : sessions_) {
|
||||||
if (session.session_) {
|
if (session.session_) {
|
||||||
session.session_->Stop();
|
session.session_->Stop();
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
68
src/screen_capturer/windows/screen_capturer_wgc.h.bak
Normal file
68
src/screen_capturer/windows/screen_capturer_wgc.h.bak
Normal 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
|
||||||
187
src/screen_capturer/windows/screen_capturer_wgc_warp.cpp
Normal file
187
src/screen_capturer/windows/screen_capturer_wgc_warp.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; \
|
||||||
@@ -20,122 +22,118 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
|
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
|
||||||
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
|
::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
|
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
|
||||||
|
|
||||||
WgcSessionImpl::~WgcSessionImpl() {
|
WgcSessionImpl::~WgcSessionImpl() {
|
||||||
Stop();
|
try {
|
||||||
CleanUp();
|
Stop();
|
||||||
|
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_) {
|
||||||
auto current_size = capture_item_.Size();
|
std::cout << "AE_NO_CAPTURE_ITEM" << std::endl;
|
||||||
capture_framepool_ =
|
LOG_ERROR("No capture item");
|
||||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
return 2;
|
||||||
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();
|
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
|
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
|
||||||
// CreateFreeThreaded instead of Create to create a capture frame pool,
|
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
|
||||||
// we need to test the performance later
|
capture_close_trigger_ = capture_item_.Closed(
|
||||||
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
|
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
|
||||||
|
|
||||||
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,26 +162,28 @@ 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>(),
|
||||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
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>(),
|
||||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -216,163 +209,101 @@ 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();
|
|
||||||
|
|
||||||
if (frame_size.Width != capture_frame_size_.Width ||
|
auto frame_size = frame.ContentSize();
|
||||||
frame_size.Height != capture_frame_size_.Height) {
|
bool size_changed = (frame_size.Width != capture_frame_size_.Width ||
|
||||||
// The thing we have been capturing has changed size.
|
frame_size.Height != capture_frame_size_.Height);
|
||||||
// We need to resize our swap chain first, then blit the pixels.
|
if (size_changed) {
|
||||||
// After we do that, retire the frame and then recreate our frame pool.
|
capture_frame_size_ = frame_size;
|
||||||
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_,
|
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_session_ = nullptr;
|
|
||||||
capture_item_ = nullptr;
|
|
||||||
|
|
||||||
is_initialized_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<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_);
|
|
||||||
// }
|
|
||||||
380
src/screen_capturer/windows/wgc_session_impl.cpp.bak
Normal file
380
src/screen_capturer/windows/wgc_session_impl.cpp.bak
Normal 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_);
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user