-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathWinDesktopDup.cpp
More file actions
200 lines (169 loc) · 5.97 KB
/
WinDesktopDup.cpp
File metadata and controls
200 lines (169 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include "stdafx.h"
#include "WinDesktopDup.h"
WinDesktopDup::~WinDesktopDup() {
Close();
}
Error WinDesktopDup::Initialize() {
// Get desktop
HDESK hDesk = OpenInputDesktop(0, FALSE, GENERIC_ALL);
if (!hDesk)
return "Failed to open desktop";
// Attach desktop to this thread (presumably for cases where this is not the main/UI thread)
bool deskAttached = SetThreadDesktop(hDesk) != 0;
CloseDesktop(hDesk);
hDesk = nullptr;
if (!deskAttached)
return "Failed to attach recording thread to desktop";
// Initialize DirectX
HRESULT hr = S_OK;
// Driver types supported
D3D_DRIVER_TYPE driverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
auto numDriverTypes = ARRAYSIZE(driverTypes);
// Feature levels supported
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1};
auto numFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL featureLevel;
// Create device
for (size_t i = 0; i < numDriverTypes; i++) {
hr = D3D11CreateDevice(nullptr, driverTypes[i], nullptr, 0, featureLevels, (UINT) numFeatureLevels,
D3D11_SDK_VERSION, &D3DDevice, &featureLevel, &D3DDeviceContext);
if (SUCCEEDED(hr))
break;
}
if (FAILED(hr))
return tsf::fmt("D3D11CreateDevice failed: %v", hr);
// Initialize the Desktop Duplication system
//m_OutputNumber = Output;
// Get DXGI device
IDXGIDevice* dxgiDevice = nullptr;
hr = D3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void**) &dxgiDevice);
if (FAILED(hr))
return tsf::fmt("D3DDevice->QueryInterface failed: %v", hr);
// Get DXGI adapter
IDXGIAdapter* dxgiAdapter = nullptr;
hr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**) &dxgiAdapter);
dxgiDevice->Release();
dxgiDevice = nullptr;
if (FAILED(hr)) {
return tsf::fmt("dxgiDevice->GetParent failed: %v", hr);
//return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors);
}
// Get output
IDXGIOutput* dxgiOutput = nullptr;
hr = dxgiAdapter->EnumOutputs(OutputNumber, &dxgiOutput);
dxgiAdapter->Release();
dxgiAdapter = nullptr;
if (FAILED(hr)) {
return tsf::fmt("dxgiAdapter->EnumOutputs failed: %v", hr);
//return ProcessFailure(m_Device, L"Failed to get specified output in DUPLICATIONMANAGER", L"Error", hr, EnumOutputsExpectedErrors);
}
dxgiOutput->GetDesc(&OutputDesc);
// QI for Output 1
IDXGIOutput1* dxgiOutput1 = nullptr;
hr = dxgiOutput->QueryInterface(__uuidof(dxgiOutput1), (void**) &dxgiOutput1);
dxgiOutput->Release();
dxgiOutput = nullptr;
if (FAILED(hr))
return tsf::fmt("dxgiOutput->QueryInterface failed: %v", hr);
// Create desktop duplication
hr = dxgiOutput1->DuplicateOutput(D3DDevice, &DeskDupl);
dxgiOutput1->Release();
dxgiOutput1 = nullptr;
if (FAILED(hr)) {
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
//MessageBoxW(nullptr, L"There is already the maximum number of applications using the Desktop Duplication API running, please close one of those applications and then try again.", L"Error", MB_OK);
return "Too many desktop recorders already active";
}
return tsf::fmt("DuplicateOutput failed: %v", hr);
//return ProcessFailure(m_Device, L"Failed to get duplicate output in DUPLICATIONMANAGER", L"Error", hr, CreateDuplicationExpectedErrors);
}
return "";
}
void WinDesktopDup::Close() {
if (DeskDupl)
DeskDupl->Release();
if (D3DDeviceContext)
D3DDeviceContext->Release();
if (D3DDevice)
D3DDevice->Release();
DeskDupl = nullptr;
D3DDeviceContext = nullptr;
D3DDevice = nullptr;
HaveFrameLock = false;
}
bool WinDesktopDup::CaptureNext() {
if (!DeskDupl)
return false;
HRESULT hr;
// according to the docs, it's best for performance if we hang onto the frame for as long as possible,
// and only release the previous frame immediately before acquiring the next one. Something about
// the OS coalescing updates, so that it doesn't have to store them as distinct things.
if (HaveFrameLock) {
HaveFrameLock = false;
hr = DeskDupl->ReleaseFrame();
// ignore response
}
IDXGIResource* deskRes = nullptr;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
hr = DeskDupl->AcquireNextFrame(0, &frameInfo, &deskRes);
if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
// nothing to see here
return false;
}
if (FAILED(hr)) {
// perhaps shutdown and reinitialize
auto msg = tsf::fmt("Acquire failed: %x\n", hr);
OutputDebugStringA(msg.c_str());
return false;
}
HaveFrameLock = true;
ID3D11Texture2D* gpuTex = nullptr;
hr = deskRes->QueryInterface(__uuidof(ID3D11Texture2D), (void**) &gpuTex);
deskRes->Release();
deskRes = nullptr;
if (FAILED(hr)) {
// not expected
return false;
}
bool ok = true;
D3D11_TEXTURE2D_DESC desc;
gpuTex->GetDesc(&desc);
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.MiscFlags = 0; // D3D11_RESOURCE_MISC_GDI_COMPATIBLE ?
ID3D11Texture2D* cpuTex = nullptr;
hr = D3DDevice->CreateTexture2D(&desc, nullptr, &cpuTex);
if (SUCCEEDED(hr)) {
D3DDeviceContext->CopyResource(cpuTex, gpuTex);
} else {
// not expected
ok = false;
}
//UINT subresource = D3D11CalcSubresource(0, 0, 0);
D3D11_MAPPED_SUBRESOURCE sr;
hr = D3DDeviceContext->Map(cpuTex, 0, D3D11_MAP_READ, 0, &sr);
if (SUCCEEDED(hr)) {
if (Latest.Width != desc.Width || Latest.Height != desc.Height) {
Latest.Width = desc.Width;
Latest.Height = desc.Height;
Latest.Buf.resize(desc.Width * desc.Height * 4);
}
for (int y = 0; y < (int) desc.Height; y++)
memcpy(Latest.Buf.data() + y * desc.Width * 4, (uint8_t*) sr.pData + sr.RowPitch * y, desc.Width * 4);
D3DDeviceContext->Unmap(cpuTex, 0);
} else {
ok = false;
}
cpuTex->Release();
gpuTex->Release();
return ok;
}