2026-01-02 20:00:18 +01:00
|
|
|
|
#pragma once
|
2025-03-13 23:34:33 +08:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
#include "ScreenCapture.h"
|
|
|
|
|
|
#include "common/commands.h"
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 只要你安装了 Windows 8 SDK 或更高版本的 Windows SDK,编译器就能找到 dxgi1_2.h 并成功编译。
|
|
|
|
|
|
// 仅在 Windows 8 及更新版本上受支持
|
2025-03-13 23:34:33 +08:00
|
|
|
|
#include <dxgi1_2.h>
|
|
|
|
|
|
#include <d3d11.h>
|
2026-01-08 22:10:38 +01:00
|
|
|
|
#include <iniFile.h>
|
2025-03-13 23:34:33 +08:00
|
|
|
|
|
|
|
|
|
|
#pragma comment(lib, "d3d11.lib")
|
2025-08-02 23:41:02 +08:00
|
|
|
|
#pragma comment(lib, "dxgi.lib")
|
2025-03-13 23:34:33 +08:00
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
// author: ChatGPT
|
2025-03-13 23:34:33 +08:00
|
|
|
|
// update: 962914132@qq.com
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// DXGI 1.2(IDXGIOutputDuplication)相比传统 GDI 截屏,性能提升通常在 3 倍到 10 倍之间,具体取决于硬件、分辨率和使用场景。
|
2025-10-15 04:32:59 +08:00
|
|
|
|
class ScreenCapturerDXGI : public ScreenCapture
|
|
|
|
|
|
{
|
2025-03-13 23:34:33 +08:00
|
|
|
|
private:
|
2025-10-15 04:32:59 +08:00
|
|
|
|
ID3D11Device* d3dDevice = nullptr;
|
|
|
|
|
|
ID3D11DeviceContext* d3dContext = nullptr;
|
|
|
|
|
|
IDXGIOutputDuplication* deskDupl = nullptr;
|
|
|
|
|
|
ID3D11Texture2D* cpuTexture = nullptr;
|
|
|
|
|
|
BYTE* m_NextBuffer = nullptr;
|
2025-03-13 23:34:33 +08:00
|
|
|
|
|
|
|
|
|
|
public:
|
2025-10-15 04:32:59 +08:00
|
|
|
|
ScreenCapturerDXGI(BYTE algo, int gop = DEFAULT_GOP, BOOL all = FALSE) : ScreenCapture(32, algo, all)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_GOP = gop;
|
|
|
|
|
|
InitDXGI(all);
|
|
|
|
|
|
Mprintf("Capture screen with DXGI: GOP= %d\n", m_GOP);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
~ScreenCapturerDXGI()
|
|
|
|
|
|
{
|
|
|
|
|
|
CleanupDXGI();
|
|
|
|
|
|
|
|
|
|
|
|
SAFE_DELETE_ARRAY(m_FirstBuffer);
|
|
|
|
|
|
SAFE_DELETE_ARRAY(m_NextBuffer);
|
|
|
|
|
|
SAFE_DELETE_ARRAY(m_RectBuffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-21 00:27:40 +01:00
|
|
|
|
virtual BOOL UsingDXGI() const
|
|
|
|
|
|
{
|
2025-12-11 10:40:59 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
void InitDXGI(BOOL all)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_iScreenX = 0;
|
|
|
|
|
|
m_iScreenY = 0;
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 1. 创建 D3D11 设备
|
2025-10-15 04:32:59 +08:00
|
|
|
|
D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, nullptr, &d3dContext);
|
|
|
|
|
|
|
|
|
|
|
|
IDXGIFactory1* pFactory = nullptr;
|
|
|
|
|
|
IDXGIAdapter1* dxgiAdapter = nullptr;
|
|
|
|
|
|
IDXGIOutput* dxgiOutput = nullptr;
|
|
|
|
|
|
IDXGIOutput1* dxgiOutput1 = nullptr;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 2. 创建工厂
|
2025-10-15 04:32:59 +08:00
|
|
|
|
CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory);
|
|
|
|
|
|
if (!pFactory) return;
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 3. 获取设备
|
2025-10-15 04:32:59 +08:00
|
|
|
|
static UINT idx = 0;
|
|
|
|
|
|
idx = pFactory->EnumAdapters1(idx, &dxgiAdapter) == DXGI_ERROR_NOT_FOUND ? 0 : idx;
|
|
|
|
|
|
if (!dxgiAdapter) pFactory->EnumAdapters1(idx, &dxgiAdapter);
|
|
|
|
|
|
if (!dxgiAdapter)break;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 4. 获取 DXGI 输出(屏幕)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
static UINT screen = 0;
|
|
|
|
|
|
HRESULT r = dxgiAdapter->EnumOutputs(screen++, &dxgiOutput);
|
|
|
|
|
|
if (r == DXGI_ERROR_NOT_FOUND && all) {
|
|
|
|
|
|
screen = 0;
|
|
|
|
|
|
idx ++;
|
|
|
|
|
|
dxgiAdapter->Release();
|
|
|
|
|
|
dxgiAdapter = nullptr;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!dxgiOutput)break;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 5. 获取 DXGI 输出 1
|
2025-10-15 04:32:59 +08:00
|
|
|
|
dxgiOutput->QueryInterface(__uuidof(IDXGIOutput1), (void**)&dxgiOutput1);
|
|
|
|
|
|
if (!dxgiOutput1)break;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 6. 创建 Desktop Duplication
|
2025-10-15 04:32:59 +08:00
|
|
|
|
dxgiOutput1->DuplicateOutput(d3dDevice, &deskDupl);
|
|
|
|
|
|
if (!deskDupl)break;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 7. 获取屏幕大小
|
2025-10-15 04:32:59 +08:00
|
|
|
|
DXGI_OUTDUPL_DESC duplDesc;
|
|
|
|
|
|
deskDupl->GetDesc(&duplDesc);
|
|
|
|
|
|
m_ulFullWidth = duplDesc.ModeDesc.Width;
|
|
|
|
|
|
m_ulFullHeight = duplDesc.ModeDesc.Height;
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 8. 创建 CPU 访问纹理
|
2025-10-15 04:32:59 +08:00
|
|
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
|
|
|
|
desc.Width = m_ulFullWidth;
|
|
|
|
|
|
desc.Height = m_ulFullHeight;
|
|
|
|
|
|
desc.MipLevels = 1;
|
|
|
|
|
|
desc.ArraySize = 1;
|
|
|
|
|
|
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
|
|
desc.SampleDesc.Count = 1;
|
|
|
|
|
|
desc.Usage = D3D11_USAGE_STAGING;
|
|
|
|
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
|
|
d3dDevice->CreateTexture2D(&desc, NULL, &cpuTexture);
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 9. 初始化 BITMAPINFO
|
2025-12-24 09:24:15 +01:00
|
|
|
|
m_BitmapInfor_Full = ConstructBitmapInfo(32, m_ulFullWidth, m_ulFullHeight);
|
2026-01-10 16:59:02 +01:00
|
|
|
|
iniFile cfg(CLIENT_PATH);
|
2026-01-12 22:54:15 +01:00
|
|
|
|
int strategy = HasSSE2() ? cfg.GetInt("settings", "ScreenStrategy", 0) : 1;
|
2026-01-08 22:10:38 +01:00
|
|
|
|
switch (strategy) {
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
m_BitmapInfor_Send = new BITMAPINFO(*m_BitmapInfor_Full);
|
|
|
|
|
|
if (m_bAlgorithm != ALGORITHM_H264) {
|
|
|
|
|
|
m_BitmapInfor_Send->bmiHeader.biWidth = min(1920, m_BitmapInfor_Send->bmiHeader.biWidth);
|
|
|
|
|
|
m_BitmapInfor_Send->bmiHeader.biHeight = min(1080, m_BitmapInfor_Send->bmiHeader.biHeight);
|
|
|
|
|
|
m_BitmapInfor_Send->bmiHeader.biSizeImage =
|
|
|
|
|
|
((m_BitmapInfor_Send->bmiHeader.biWidth * m_BitmapInfor_Send->bmiHeader.biBitCount + 31) / 32) *
|
|
|
|
|
|
4 * m_BitmapInfor_Send->bmiHeader.biHeight;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 10. 分配屏幕缓冲区
|
2025-10-15 04:32:59 +08:00
|
|
|
|
m_FirstBuffer = new BYTE[m_BitmapInfor_Full->bmiHeader.biSizeImage + 1];
|
|
|
|
|
|
m_NextBuffer = new BYTE[m_BitmapInfor_Full->bmiHeader.biSizeImage + 1];
|
|
|
|
|
|
m_RectBuffer = new BYTE[m_BitmapInfor_Full->bmiHeader.biSizeImage * 2 + 12];
|
2026-01-10 16:59:02 +01:00
|
|
|
|
m_BmpZoomBuffer = new BYTE[m_BitmapInfor_Send->bmiHeader.biSizeImage * 2 + 12];
|
|
|
|
|
|
m_BmpZoomFirst = nullptr;
|
2025-10-15 04:32:59 +08:00
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
} while (true);
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 释放 DXGI 资源
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (dxgiOutput1) dxgiOutput1->Release();
|
|
|
|
|
|
if (dxgiOutput) dxgiOutput->Release();
|
|
|
|
|
|
if (dxgiAdapter) dxgiAdapter->Release();
|
|
|
|
|
|
if (pFactory) pFactory->Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool IsInitSucceed() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return cpuTexture;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CleanupDXGI()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cpuTexture) cpuTexture->Release();
|
|
|
|
|
|
if (deskDupl) deskDupl->Release();
|
|
|
|
|
|
if (d3dContext) d3dContext->Release();
|
|
|
|
|
|
if (d3dDevice) d3dDevice->Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-10 16:59:02 +01:00
|
|
|
|
virtual LPBYTE scaleBitmap(LPBYTE target, LPBYTE bitmap) override
|
|
|
|
|
|
{
|
2025-12-24 09:24:15 +01:00
|
|
|
|
if (m_ulFullWidth == m_BitmapInfor_Send->bmiHeader.biWidth && m_ulFullHeight == m_BitmapInfor_Send->bmiHeader.biHeight) {
|
|
|
|
|
|
memcpy(target, bitmap, m_BitmapInfor_Send->bmiHeader.biSizeImage);
|
|
|
|
|
|
return bitmap;
|
|
|
|
|
|
}
|
2026-01-10 16:59:02 +01:00
|
|
|
|
return ScaleBitmap(target, (uint8_t*)bitmap, m_ulFullWidth, m_ulFullHeight, m_BitmapInfor_Send->bmiHeader.biWidth,
|
|
|
|
|
|
m_BitmapInfor_Send->bmiHeader.biHeight);
|
|
|
|
|
|
}
|
2025-12-24 09:24:15 +01:00
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
LPBYTE GetFirstScreenData(ULONG* ulFirstScreenLength) override
|
|
|
|
|
|
{
|
|
|
|
|
|
int ret = CaptureFrame(m_FirstBuffer, ulFirstScreenLength, 1);
|
2025-12-24 09:24:15 +01:00
|
|
|
|
scaleBitmap(m_BmpZoomBuffer, m_FirstBuffer);
|
|
|
|
|
|
memcpy(m_FirstBuffer, m_BmpZoomBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (ret)
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
if (m_bAlgorithm == ALGORITHM_GRAY) {
|
2025-12-24 09:24:15 +01:00
|
|
|
|
ToGray(1 + m_RectBuffer, 1 + m_RectBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
m_FirstBuffer[0] = TOKEN_FIRSTSCREEN;
|
|
|
|
|
|
return m_FirstBuffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LPBYTE ScanNextScreen() override
|
|
|
|
|
|
{
|
|
|
|
|
|
ULONG ulNextScreenLength = 0;
|
|
|
|
|
|
int ret = CaptureFrame(m_NextBuffer, &ulNextScreenLength, 0);
|
2026-01-10 16:59:02 +01:00
|
|
|
|
scaleBitmap(m_BmpZoomBuffer, m_NextBuffer);
|
|
|
|
|
|
memcpy(m_NextBuffer, m_BmpZoomBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage);
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (ret)
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
return m_NextBuffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virtual LPBYTE GetFirstBuffer() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_FirstBuffer + 1;
|
|
|
|
|
|
}
|
2025-03-13 23:34:33 +08:00
|
|
|
|
|
|
|
|
|
|
private:
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 重新初始化 Desktop Duplication
|
2025-12-19 14:19:02 +01:00
|
|
|
|
BOOL ReinitDuplication()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (deskDupl) {
|
|
|
|
|
|
deskDupl->Release();
|
|
|
|
|
|
deskDupl = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!d3dDevice) return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
IDXGIDevice* dxgiDevice = nullptr;
|
|
|
|
|
|
IDXGIAdapter* dxgiAdapter = nullptr;
|
|
|
|
|
|
IDXGIOutput* dxgiOutput = nullptr;
|
|
|
|
|
|
IDXGIOutput1* dxgiOutput1 = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT hr = d3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
|
|
|
|
|
|
if (FAILED(hr)) return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
hr = dxgiDevice->GetAdapter(&dxgiAdapter);
|
|
|
|
|
|
dxgiDevice->Release();
|
|
|
|
|
|
if (FAILED(hr)) return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
hr = dxgiAdapter->EnumOutputs(0, &dxgiOutput);
|
|
|
|
|
|
dxgiAdapter->Release();
|
|
|
|
|
|
if (FAILED(hr)) return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
hr = dxgiOutput->QueryInterface(__uuidof(IDXGIOutput1), (void**)&dxgiOutput1);
|
|
|
|
|
|
dxgiOutput->Release();
|
|
|
|
|
|
if (FAILED(hr)) return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(100);
|
|
|
|
|
|
|
|
|
|
|
|
hr = dxgiOutput1->DuplicateOutput(d3dDevice, &deskDupl);
|
|
|
|
|
|
dxgiOutput1->Release();
|
|
|
|
|
|
|
|
|
|
|
|
return SUCCEEDED(hr) && deskDupl;
|
|
|
|
|
|
}
|
2025-10-15 04:32:59 +08:00
|
|
|
|
int CaptureFrame(LPBYTE buffer, ULONG* frameSize, int reservedBytes)
|
|
|
|
|
|
{
|
2025-12-19 14:19:02 +01:00
|
|
|
|
if (!deskDupl) {
|
|
|
|
|
|
if (!ReinitDuplication()) return -10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 1. 获取下一帧
|
2025-10-15 04:32:59 +08:00
|
|
|
|
IDXGIResource* desktopResource = nullptr;
|
|
|
|
|
|
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
|
|
|
|
|
HRESULT hr = deskDupl->AcquireNextFrame(100, &frameInfo, &desktopResource);
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 处理全屏切换导致的访问丢失
|
2025-12-19 14:19:02 +01:00
|
|
|
|
if (hr == DXGI_ERROR_ACCESS_LOST) {
|
|
|
|
|
|
if (ReinitDuplication()) {
|
|
|
|
|
|
hr = deskDupl->AcquireNextFrame(100, &frameInfo, &desktopResource);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 04:32:59 +08:00
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 2. 获取 ID3D11Texture2D
|
2025-10-15 04:32:59 +08:00
|
|
|
|
ID3D11Texture2D* texture = nullptr;
|
|
|
|
|
|
hr = desktopResource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texture);
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
|
deskDupl->ReleaseFrame();
|
|
|
|
|
|
return -2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 3. 复制到 CPU 纹理
|
2025-10-15 04:32:59 +08:00
|
|
|
|
d3dContext->CopyResource(cpuTexture, texture);
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 4. 释放 DXGI 资源
|
2025-10-15 04:32:59 +08:00
|
|
|
|
deskDupl->ReleaseFrame();
|
|
|
|
|
|
texture->Release();
|
|
|
|
|
|
desktopResource->Release();
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 5. 读取纹理数据
|
2025-10-15 04:32:59 +08:00
|
|
|
|
D3D11_MAPPED_SUBRESOURCE mapped;
|
|
|
|
|
|
hr = d3dContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
|
return -3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 6. 复制数据到缓冲区(垂直翻转)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
BYTE* pData = (BYTE*)mapped.pData;
|
2026-01-02 20:00:18 +01:00
|
|
|
|
int rowSize = m_ulFullWidth * 4; // 每行的字节数(RGBA)
|
2025-10-15 04:32:59 +08:00
|
|
|
|
|
2025-12-29 17:54:47 +01:00
|
|
|
|
BYTE* dest = buffer + reservedBytes + (m_ulFullHeight - 1) * rowSize;
|
|
|
|
|
|
BYTE* src = pData;
|
2025-10-15 04:32:59 +08:00
|
|
|
|
for (int y = 0; y < m_ulFullHeight; y++) {
|
2025-12-29 17:54:47 +01:00
|
|
|
|
memcpy(dest, src, rowSize);
|
|
|
|
|
|
dest -= rowSize;
|
|
|
|
|
|
src += mapped.RowPitch;
|
2025-10-15 04:32:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-02 20:00:18 +01:00
|
|
|
|
// 7. 清理
|
2025-10-15 04:32:59 +08:00
|
|
|
|
d3dContext->Unmap(cpuTexture, 0);
|
|
|
|
|
|
|
|
|
|
|
|
*frameSize = m_ulFullWidth * m_ulFullHeight * 4;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
2025-03-13 23:34:33 +08:00
|
|
|
|
};
|