1 /*
2 * GStreamer
3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /*
22 * The MIT License (MIT)
23 *
24 * Copyright (c) Microsoft Corporation
25 *
26 * Permission is hereby granted, free of charge, to any person obtaining a copy
27 * of this software and associated documentation files (the "Software"), to deal
28 * in the Software without restriction, including without limitation the rights
29 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 * copies of the Software, and to permit persons to whom the Software is
31 * furnished to do so, subject to the following conditions:
32 *
33 * The above copyright notice and this permission notice shall be included in
34 * all copies or substantial portions of the Software.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
42 * THE SOFTWARE.
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include "gstd3d11screencapture.h"
50 #include "gstd3d11shader.h"
51 #include "gstd3d11pluginutils.h"
52 #include <string.h>
53
54 #include <wrl.h>
55
56 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_screen_capture_debug);
57 #define GST_CAT_DEFAULT gst_d3d11_screen_capture_debug
58
59 /* *INDENT-OFF* */
60 using namespace Microsoft::WRL;
61
62 /* List of GstD3D11ScreenCapture weakref */
63 G_LOCK_DEFINE_STATIC (dupl_list_lock);
64 static GList *dupl_list = nullptr;
65
66 /* Below implemenation were taken from Microsoft sample
67 * https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/DXGIDesktopDuplication
68 */
69 #define NUMVERTICES 6
70 #define BPP 4
71
72 /* Define our own MyFLOAT3 and MyFLOAT2 struct, since MinGW doesn't support
73 * DirectXMath.h
74 */
75 struct MyFLOAT3
76 {
77 float x;
78 float y;
79 float z;
80
81 MyFLOAT3() = default;
82
83 MyFLOAT3(const MyFLOAT3&) = default;
84 MyFLOAT3& operator=(const MyFLOAT3&) = default;
85
86 MyFLOAT3(MyFLOAT3&&) = default;
87 MyFLOAT3& operator=(MyFLOAT3&&) = default;
88
MyFLOAT3MyFLOAT389 constexpr MyFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
MyFLOAT3MyFLOAT390 explicit MyFLOAT3(const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
91 };
92
93 struct MyFLOAT2
94 {
95 float x;
96 float y;
97
98 MyFLOAT2() = default;
99
100 MyFLOAT2(const MyFLOAT2&) = default;
101 MyFLOAT2& operator=(const MyFLOAT2&) = default;
102
103 MyFLOAT2(MyFLOAT2&&) = default;
104 MyFLOAT2& operator=(MyFLOAT2&&) = default;
105
MyFLOAT2MyFLOAT2106 constexpr MyFLOAT2(float _x, float _y) : x(_x), y(_y) {}
MyFLOAT2MyFLOAT2107 explicit MyFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {}
108 };
109
110 typedef struct
111 {
112 MyFLOAT3 Pos;
113 MyFLOAT2 TexCoord;
114 } VERTEX;
115
116 /* List of expected error cases */
117 /* These are the errors we expect from general Dxgi API due to a transition */
118 HRESULT SystemTransitionsExpectedErrors[] = {
119 DXGI_ERROR_DEVICE_REMOVED,
120 DXGI_ERROR_ACCESS_LOST,
121 static_cast<HRESULT>(WAIT_ABANDONED),
122 S_OK
123 };
124
125 /* These are the errors we expect from IDXGIOutput1::DuplicateOutput
126 * due to a transition */
127 HRESULT CreateDuplicationExpectedErrors[] = {
128 DXGI_ERROR_DEVICE_REMOVED,
129 static_cast<HRESULT>(E_ACCESSDENIED),
130 DXGI_ERROR_SESSION_DISCONNECTED,
131 S_OK
132 };
133
134 /* These are the errors we expect from IDXGIOutputDuplication methods
135 * due to a transition */
136 HRESULT FrameInfoExpectedErrors[] = {
137 DXGI_ERROR_DEVICE_REMOVED,
138 DXGI_ERROR_ACCESS_LOST,
139 S_OK
140 };
141
142 /* These are the errors we expect from IDXGIAdapter::EnumOutputs methods
143 * due to outputs becoming stale during a transition */
144 HRESULT EnumOutputsExpectedErrors[] = {
145 DXGI_ERROR_NOT_FOUND,
146 S_OK
147 };
148
149 static GstFlowReturn
gst_d3d11_screen_capture_return_from_hr(ID3D11Device * device,HRESULT hr,HRESULT * expected_errors=nullptr)150 gst_d3d11_screen_capture_return_from_hr (ID3D11Device * device,
151 HRESULT hr, HRESULT * expected_errors = nullptr)
152 {
153 HRESULT translated_hr = hr;
154
155 /* On an error check if the DX device is lost */
156 if (device) {
157 HRESULT remove_reason = device->GetDeviceRemovedReason ();
158
159 switch (remove_reason) {
160 case DXGI_ERROR_DEVICE_REMOVED:
161 case DXGI_ERROR_DEVICE_RESET:
162 case static_cast<HRESULT>(E_OUTOFMEMORY):
163 /* Our device has been stopped due to an external event on the GPU so
164 * map them all to device removed and continue processing the condition
165 */
166 translated_hr = DXGI_ERROR_DEVICE_REMOVED;
167 break;
168 case S_OK:
169 /* Device is not removed so use original error */
170 break;
171 default:
172 /* Device is removed but not a error we want to remap */
173 translated_hr = remove_reason;
174 break;
175 }
176 }
177
178 /* Check if this error was expected or not */
179 if (expected_errors) {
180 HRESULT* rst = expected_errors;
181
182 while (*rst != S_OK) {
183 if (*rst == translated_hr)
184 return GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR;
185
186 rst++;
187 }
188 }
189
190 return GST_FLOW_ERROR;
191 }
192
193 class PTR_INFO
194 {
195 public:
PTR_INFO()196 PTR_INFO ()
197 : PtrShapeBuffer (nullptr)
198 , BufferSize (0)
199 {
200 LastTimeStamp.QuadPart = 0;
201 }
202
~PTR_INFO()203 ~PTR_INFO ()
204 {
205 if (PtrShapeBuffer)
206 delete[] PtrShapeBuffer;
207 }
208
209 void
MaybeReallocBuffer(UINT buffer_size)210 MaybeReallocBuffer (UINT buffer_size)
211 {
212 if (buffer_size <= BufferSize)
213 return;
214
215 if (PtrShapeBuffer)
216 delete[] PtrShapeBuffer;
217
218 PtrShapeBuffer = new BYTE[buffer_size];
219 BufferSize = buffer_size;
220 }
221
222 BYTE* PtrShapeBuffer;
223 UINT BufferSize;
224 DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
225 POINT Position;
226 bool Visible;
227 LARGE_INTEGER LastTimeStamp;
228 };
229
230 class D3D11DesktopDupObject
231 {
232 public:
D3D11DesktopDupObject()233 D3D11DesktopDupObject ()
234 : device_(nullptr)
235 , metadata_buffer_(nullptr)
236 , metadata_buffer_size_(0)
237 , vertex_buffer_(nullptr)
238 , vertex_buffer_size_(0)
239 {
240 }
241
~D3D11DesktopDupObject()242 ~D3D11DesktopDupObject ()
243 {
244 if (metadata_buffer_)
245 delete[] metadata_buffer_;
246
247 if (vertex_buffer_)
248 delete[] vertex_buffer_;
249
250 gst_clear_object (&device_);
251 }
252
253 GstFlowReturn
Init(GstD3D11Device * device,HMONITOR monitor)254 Init (GstD3D11Device * device, HMONITOR monitor)
255 {
256 GstFlowReturn ret;
257 ID3D11Device *device_handle;
258 HRESULT hr;
259 D3D11_TEXTURE2D_DESC texture_desc = { 0, };
260
261 if (!InitShader (device))
262 return GST_FLOW_ERROR;
263
264 ret = InitDupl (device, monitor);
265 if (ret != GST_FLOW_OK)
266 return ret;
267
268 GST_INFO ("Init done");
269
270 device_handle = gst_d3d11_device_get_device_handle (device);
271
272 texture_desc.Width = output_desc_.ModeDesc.Width;
273 texture_desc.Height = output_desc_.ModeDesc.Height;
274 texture_desc.MipLevels = 1;
275 texture_desc.ArraySize = 1;
276 /* FIXME: we can support DXGI_FORMAT_R10G10B10A2_UNORM */
277 texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
278 texture_desc.SampleDesc.Count = 1;
279 texture_desc.Usage = D3D11_USAGE_DEFAULT;
280 texture_desc.BindFlags =
281 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
282 texture_desc.CPUAccessFlags = 0;
283 /* source element may hold different d3d11 device object */
284 texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
285
286 hr = device_handle->CreateTexture2D (&texture_desc,
287 nullptr, &shared_texture_);
288 if (!gst_d3d11_result (hr, device)) {
289 GST_ERROR_OBJECT (device, "Couldn't create texture, hr 0x%x", (guint) hr);
290 return GST_FLOW_ERROR;
291 }
292
293 device_ = (GstD3D11Device *) gst_object_ref (device);
294
295 return GST_FLOW_OK;
296 }
297
298 GstFlowReturn
Capture()299 Capture ()
300 {
301 GstFlowReturn ret;
302 bool timeout = false;
303 ComPtr<ID3D11Texture2D> texture;
304 UINT move_count, dirty_count;
305 DXGI_OUTDUPL_FRAME_INFO frame_info;
306
307 GST_TRACE ("Capturing");
308 ret = GetFrame (&texture, &move_count, &dirty_count, &frame_info, &timeout);
309 if (ret != GST_FLOW_OK)
310 return ret;
311
312 /* Nothing updated */
313 if (timeout) {
314 GST_TRACE ("timeout");
315 return GST_FLOW_OK;
316 }
317
318 GST_TRACE ("Getting mouse pointer info");
319 ret = GetMouse (&ptr_info_, &frame_info);
320 if (ret != GST_FLOW_OK) {
321 GST_WARNING ("Couldn't get mouse pointer info");
322 dupl_->ReleaseFrame ();
323 return ret;
324 }
325
326 ret = ProcessFrame (texture.Get(), shared_texture_.Get(),
327 &output_desc_, move_count, dirty_count, &frame_info);
328
329 if (ret != GST_FLOW_OK) {
330 dupl_->ReleaseFrame ();
331 GST_WARNING ("Couldn't process frame");
332 return ret;
333 }
334
335 HRESULT hr = dupl_->ReleaseFrame ();
336 if (!gst_d3d11_result (hr, device_)) {
337 GST_WARNING ("Couldn't release frame");
338 return gst_d3d11_screen_capture_return_from_hr (nullptr, hr, FrameInfoExpectedErrors);
339 }
340
341 GST_TRACE ("Capture done");
342
343 return GST_FLOW_OK;
344 }
345
346 bool
DrawMouse(GstD3D11Device * device,ID3D11RenderTargetView * rtv,ID3D11VertexShader * vs,ID3D11PixelShader * ps,ID3D11InputLayout * layout,ID3D11SamplerState * sampler,ID3D11BlendState * blend)347 DrawMouse (GstD3D11Device * device, ID3D11RenderTargetView * rtv,
348 ID3D11VertexShader * vs, ID3D11PixelShader * ps,
349 ID3D11InputLayout * layout, ID3D11SamplerState * sampler,
350 ID3D11BlendState * blend)
351 {
352 GST_TRACE ("Drawing mouse");
353
354 if (!ptr_info_.Visible) {
355 GST_TRACE ("Mouse is invisiable");
356 return true;
357 }
358
359 ComPtr<ID3D11Texture2D> MouseTex;
360 ComPtr<ID3D11ShaderResourceView> ShaderRes;
361 ComPtr<ID3D11Buffer> VertexBufferMouse;
362 D3D11_SUBRESOURCE_DATA InitData;
363 D3D11_TEXTURE2D_DESC Desc;
364 D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
365 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
366 ID3D11DeviceContext *context_handle =
367 gst_d3d11_device_get_device_context_handle (device);
368
369 VERTEX Vertices[NUMVERTICES] =
370 {
371 {MyFLOAT3(-1.0f, -1.0f, 0), MyFLOAT2(0.0f, 1.0f)},
372 {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
373 {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
374 {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
375 {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
376 {MyFLOAT3(1.0f, 1.0f, 0), MyFLOAT2(1.0f, 0.0f)},
377 };
378
379 D3D11_TEXTURE2D_DESC FullDesc;
380 shared_texture_->GetDesc(&FullDesc);
381 INT DesktopWidth = FullDesc.Width;
382 INT DesktopHeight = FullDesc.Height;
383
384 INT CenterX = (DesktopWidth / 2);
385 INT CenterY = (DesktopHeight / 2);
386
387 INT PtrWidth = 0;
388 INT PtrHeight = 0;
389 INT PtrLeft = 0;
390 INT PtrTop = 0;
391
392 BYTE* InitBuffer = nullptr;
393
394 D3D11_BOX Box;
395 Box.front = 0;
396 Box.back = 1;
397
398 Desc.MipLevels = 1;
399 Desc.ArraySize = 1;
400 Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
401 Desc.SampleDesc.Count = 1;
402 Desc.SampleDesc.Quality = 0;
403 Desc.Usage = D3D11_USAGE_DEFAULT;
404 Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
405 Desc.CPUAccessFlags = 0;
406 Desc.MiscFlags = 0;
407
408 // Set shader resource properties
409 SDesc.Format = Desc.Format;
410 SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
411 SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1;
412 SDesc.Texture2D.MipLevels = Desc.MipLevels;
413
414 switch (ptr_info_.shape_info.Type) {
415 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
416 PtrLeft = ptr_info_.Position.x;
417 PtrTop = ptr_info_.Position.y;
418
419 PtrWidth = static_cast<INT>(ptr_info_.shape_info.Width);
420 PtrHeight = static_cast<INT>(ptr_info_.shape_info.Height);
421 break;
422 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
423 ProcessMonoMask(true, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
424 &PtrTop, &InitBuffer, &Box);
425 break;
426 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
427 ProcessMonoMask(false, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
428 &PtrTop, &InitBuffer, &Box);
429 break;
430 default:
431 break;
432 }
433
434 /* Nothing draw */
435 if (PtrWidth == 0 || PtrHeight == 0) {
436 if (InitBuffer)
437 delete[] InitBuffer;
438
439 return true;
440 }
441
442 Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
443 Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
444 Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
445 Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
446 Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
447 Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
448 Vertices[3].Pos.x = Vertices[2].Pos.x;
449 Vertices[3].Pos.y = Vertices[2].Pos.y;
450 Vertices[4].Pos.x = Vertices[1].Pos.x;
451 Vertices[4].Pos.y = Vertices[1].Pos.y;
452 Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
453 Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
454
455 Desc.Width = PtrWidth;
456 Desc.Height = PtrHeight;
457
458 InitData.pSysMem =
459 (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
460 ptr_info_.PtrShapeBuffer : InitBuffer;
461 InitData.SysMemPitch =
462 (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
463 ptr_info_.shape_info.Pitch : PtrWidth * BPP;
464 InitData.SysMemSlicePitch = 0;
465
466 // Create mouseshape as texture
467 HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex);
468 if (!gst_d3d11_result (hr, device)) {
469 GST_ERROR ("Failed to create texture for rendering mouse");
470 return false;
471 }
472
473 // Create shader resource from texture
474 hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
475 &ShaderRes);
476 if (!gst_d3d11_result (hr, device)) {
477 GST_ERROR ("Failed to create shader resource view for rendering mouse");
478 return false;
479 }
480
481 D3D11_BUFFER_DESC BDesc;
482 memset (&BDesc, 0, sizeof(D3D11_BUFFER_DESC));
483 BDesc.Usage = D3D11_USAGE_DEFAULT;
484 BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
485 BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
486 BDesc.CPUAccessFlags = 0;
487
488 memset (&InitData, 0, sizeof(D3D11_SUBRESOURCE_DATA));
489 InitData.pSysMem = Vertices;
490
491 // Create vertex buffer
492 hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
493 if (!gst_d3d11_result (hr, device)) {
494 GST_ERROR ("Failed to create vertex buffer for rendering mouse");
495 return false;
496 }
497
498 FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
499 UINT Stride = sizeof(VERTEX);
500 UINT Offset = 0;
501 ID3D11ShaderResourceView *srv = ShaderRes.Get();
502 ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
503
504 context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
505 context_handle->OMSetBlendState(blend, BlendFactor, 0xFFFFFFFF);
506 context_handle->OMSetRenderTargets(1, &rtv, nullptr);
507 context_handle->VSSetShader(vs, nullptr, 0);
508 context_handle->PSSetShader(ps, nullptr, 0);
509 context_handle->PSSetShaderResources(0, 1, &srv);
510 context_handle->PSSetSamplers(0, 1, &sampler);
511 context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
512 context_handle->IASetInputLayout(layout);
513
514 D3D11_VIEWPORT VP;
515 VP.Width = static_cast<FLOAT>(FullDesc.Width);
516 VP.Height = static_cast<FLOAT>(FullDesc.Height);
517 VP.MinDepth = 0.0f;
518 VP.MaxDepth = 1.0f;
519 VP.TopLeftX = 0.0f;
520 VP.TopLeftY = 0.0f;
521 context_handle->RSSetViewports(1, &VP);
522
523 context_handle->Draw(NUMVERTICES, 0);
524
525 /* Unbind srv and rtv from context */
526 srv = nullptr;
527 context_handle->PSSetShaderResources (0, 1, &srv);
528 context_handle->OMSetRenderTargets (0, nullptr, nullptr);
529
530 if (InitBuffer)
531 delete[] InitBuffer;
532
533 return true;
534 }
535
536 GstFlowReturn
CopyToTexture(GstD3D11Device * device,ID3D11Texture2D * texture)537 CopyToTexture (GstD3D11Device * device, ID3D11Texture2D * texture)
538 {
539 ID3D11DeviceContext *context_handle = nullptr;
540 ComPtr <ID3D11Texture2D> tex;
541 ComPtr < ID3D11Query > query;
542 HRESULT hr;
543
544 context_handle = gst_d3d11_device_get_device_context_handle (device);
545
546 if (device == device_) {
547 tex = shared_texture_;
548 } else {
549 ID3D11Device *device_handle = nullptr;
550 ComPtr < IDXGIResource > dxgi_resource;
551 D3D11_QUERY_DESC query_desc;
552 HANDLE shared_handle;
553
554 device_handle = gst_d3d11_device_get_device_handle (device);
555
556 hr = shared_texture_.As (&dxgi_resource);
557 if (!gst_d3d11_result (hr, device_))
558 return GST_FLOW_ERROR;
559
560 hr = dxgi_resource->GetSharedHandle (&shared_handle);
561 if (!gst_d3d11_result (hr, device_))
562 return GST_FLOW_ERROR;
563
564 hr = device_handle->OpenSharedResource (shared_handle,
565 IID_PPV_ARGS (&tex));
566 if (!gst_d3d11_result (hr, device))
567 return GST_FLOW_ERROR;
568
569 query_desc.Query = D3D11_QUERY_EVENT;
570 query_desc.MiscFlags = 0;
571
572 hr = device_handle->CreateQuery (&query_desc, &query);
573 if (!gst_d3d11_result (hr, device))
574 return GST_FLOW_ERROR;
575 }
576
577 context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
578 tex.Get(), 0, nullptr);
579
580 if (query) {
581 BOOL sync_done = FALSE;
582
583 do {
584 hr = context_handle->GetData (query.Get (),
585 &sync_done, sizeof (BOOL), 0);
586 } while (!sync_done && (hr == S_OK || hr == S_FALSE));
587 }
588
589 return GST_FLOW_OK;
590 }
591
592 void
GetSize(guint * width,guint * height)593 GetSize (guint * width, guint * height)
594 {
595 *width = output_desc_.ModeDesc.Width;
596 *height = output_desc_.ModeDesc.Height;
597 }
598
599 private:
600 /* This method is not expected to be failed unless un-recoverable error case */
601 bool
InitShader(GstD3D11Device * device)602 InitShader (GstD3D11Device * device)
603 {
604 static const gchar vs_str[] =
605 "struct VS_INPUT {\n"
606 " float4 Position: POSITION;\n"
607 " float2 Texture: TEXCOORD;\n"
608 "};\n"
609 "\n"
610 "struct VS_OUTPUT {\n"
611 " float4 Position: SV_POSITION;\n"
612 " float2 Texture: TEXCOORD;\n"
613 "};\n"
614 "\n"
615 "VS_OUTPUT main (VS_INPUT input)\n"
616 "{\n"
617 " return input;\n"
618 "}";
619
620 static const gchar ps_str[] =
621 "Texture2D shaderTexture;\n"
622 "SamplerState samplerState;\n"
623 "\n"
624 "struct PS_INPUT {\n"
625 " float4 Position: SV_POSITION;\n"
626 " float2 Texture: TEXCOORD;\n"
627 "};\n"
628 "\n"
629 "struct PS_OUTPUT {\n"
630 " float4 Plane: SV_Target;\n"
631 "};\n"
632 "\n"
633 "PS_OUTPUT main(PS_INPUT input)\n"
634 "{\n"
635 " PS_OUTPUT output;\n"
636 " output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
637 " return output;\n"
638 "}";
639
640 D3D11_INPUT_ELEMENT_DESC input_desc[] = {
641 {"POSITION",
642 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
643 {"TEXCOORD",
644 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
645 };
646
647 ComPtr<ID3D11VertexShader> vs;
648 ComPtr<ID3D11InputLayout> layout;
649 if (!gst_d3d11_create_vertex_shader (device,
650 vs_str, input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
651 GST_ERROR ("Failed to create vertex shader");
652 return false;
653 }
654
655 ComPtr<ID3D11PixelShader> ps;
656 if (!gst_d3d11_create_pixel_shader (device, ps_str, &ps)) {
657 GST_ERROR ("Failed to create pixel shader");
658 return false;
659 }
660
661 D3D11_SAMPLER_DESC sampler_desc;
662 memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
663 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
664 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
665 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
666 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
667 sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
668 sampler_desc.MinLOD = 0;
669 sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
670
671 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
672 ComPtr<ID3D11SamplerState> sampler;
673 HRESULT hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
674 if (!gst_d3d11_result (hr, device)) {
675 GST_ERROR ("Failed to create sampler state, hr 0x%x", (guint) hr);
676 return false;
677 }
678
679 /* Everything is prepared now */
680 vs_ = vs;
681 ps_ = ps;
682 layout_ = layout;
683 sampler_ = sampler;
684
685 return true;
686 }
687
688 /* Maybe returning expected error code depending on desktop status */
689 GstFlowReturn
InitDupl(GstD3D11Device * device,HMONITOR monitor)690 InitDupl (GstD3D11Device * device, HMONITOR monitor)
691 {
692 ComPtr<ID3D11Device> d3d11_device;
693 ComPtr<IDXGIAdapter1> adapter;
694 ComPtr<IDXGIOutput> output;
695 ComPtr<IDXGIOutput1> output1;
696
697 d3d11_device = gst_d3d11_device_get_device_handle (device);
698
699 HRESULT hr = gst_d3d11_screen_capture_find_output_for_monitor (monitor,
700 &adapter, &output);
701 if (!gst_d3d11_result (hr, device)) {
702 GST_ERROR ("Couldn't get adapter and output for monitor");
703 return GST_FLOW_ERROR;
704 }
705
706 hr = output.As (&output1);
707 if (!gst_d3d11_result (hr, device)) {
708 GST_ERROR ("Couldn't get IDXGIOutput1 interface, hr 0x%x", (guint) hr);
709 return GST_FLOW_ERROR;
710 }
711
712 HDESK hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
713 if (hdesk) {
714 if (!SetThreadDesktop (hdesk)) {
715 GST_WARNING ("SetThreadDesktop() failed, error %lu", GetLastError());
716 }
717
718 CloseDesktop (hdesk);
719 } else {
720 GST_WARNING ("OpenInputDesktop() failed, error %lu", GetLastError());
721 }
722
723 /* FIXME: Use DuplicateOutput1 to avoid potentail color conversion */
724 hr = output1->DuplicateOutput(d3d11_device.Get(), &dupl_);
725 if (!gst_d3d11_result (hr, device)) {
726 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
727 GST_ERROR ("Hit the max allowed number of Desktop Duplication session");
728 return GST_FLOW_ERROR;
729 }
730
731 /* Seems to be one limitation of Desktop Duplication API design
732 * See
733 * https://docs.microsoft.com/en-US/troubleshoot/windows-client/shell-experience/error-when-dda-capable-app-is-against-gpu
734 */
735 if (hr == DXGI_ERROR_UNSUPPORTED) {
736 GST_WARNING ("IDXGIOutput1::DuplicateOutput returned "
737 "DXGI_ERROR_UNSUPPORTED, possiblely application is run against a "
738 "discrete GPU");
739 return GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED;
740 }
741
742 return gst_d3d11_screen_capture_return_from_hr (d3d11_device.Get(), hr,
743 CreateDuplicationExpectedErrors);
744 }
745
746 dupl_->GetDesc (&output_desc_);
747
748 return GST_FLOW_OK;
749 }
750
751 GstFlowReturn
GetMouse(PTR_INFO * ptr_info,DXGI_OUTDUPL_FRAME_INFO * frame_info)752 GetMouse (PTR_INFO * ptr_info, DXGI_OUTDUPL_FRAME_INFO * frame_info)
753 {
754 /* A non-zero mouse update timestamp indicates that there is a mouse
755 * position update and optionally a shape change */
756 if (frame_info->LastMouseUpdateTime.QuadPart == 0)
757 return GST_FLOW_OK;
758
759 ptr_info->Position.x = frame_info->PointerPosition.Position.x;
760 ptr_info->Position.y = frame_info->PointerPosition.Position.y;
761 ptr_info->LastTimeStamp = frame_info->LastMouseUpdateTime;
762 ptr_info->Visible = frame_info->PointerPosition.Visible != 0;
763
764 /* No new shape */
765 if (frame_info->PointerShapeBufferSize == 0)
766 return GST_FLOW_OK;
767
768 /* Realloc buffer if needed */
769 ptr_info->MaybeReallocBuffer (frame_info->PointerShapeBufferSize);
770
771 /* Must always get shape of cursor, even if not drawn at the moment.
772 * Shape of cursor is not repeated by the AcquireNextFrame and can be
773 * requested to be drawn any time later */
774 UINT dummy;
775 HRESULT hr = dupl_->GetFramePointerShape(frame_info->PointerShapeBufferSize,
776 (void *) ptr_info->PtrShapeBuffer, &dummy, &ptr_info->shape_info);
777
778 if (!gst_d3d11_result (hr, device_)) {
779 ID3D11Device *device_handle =
780 gst_d3d11_device_get_device_handle (device_);
781
782 return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
783 FrameInfoExpectedErrors);
784 }
785
786 return GST_FLOW_OK;
787 }
788
789 void
MaybeReallocMetadataBuffer(UINT buffer_size)790 MaybeReallocMetadataBuffer (UINT buffer_size)
791 {
792 if (buffer_size <= metadata_buffer_size_)
793 return;
794
795 if (metadata_buffer_)
796 delete[] metadata_buffer_;
797
798 metadata_buffer_ = new BYTE[buffer_size];
799 metadata_buffer_size_ = buffer_size;
800 }
801
802 GstFlowReturn
GetFrame(ID3D11Texture2D ** texture,UINT * move_count,UINT * dirty_count,DXGI_OUTDUPL_FRAME_INFO * frame_info,bool * timeout)803 GetFrame (ID3D11Texture2D ** texture, UINT * move_count, UINT * dirty_count,
804 DXGI_OUTDUPL_FRAME_INFO * frame_info, bool* timeout)
805 {
806 ComPtr<IDXGIResource> resource;
807 ComPtr<ID3D11Texture2D> acquired_texture;
808 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
809
810 /* Get new frame */
811 HRESULT hr = dupl_->AcquireNextFrame(0, frame_info, &resource);
812 if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
813 GST_TRACE ("Timeout");
814
815 *timeout = true;
816 return GST_FLOW_OK;
817 }
818
819 *timeout = false;
820 *move_count = 0;
821 *dirty_count = 0;
822
823 if (!gst_d3d11_result (hr, device_)) {
824 return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
825 FrameInfoExpectedErrors);
826 }
827
828 GST_TRACE (
829 "LastPresentTime: %" G_GINT64_FORMAT
830 ", LastMouseUpdateTime: %" G_GINT64_FORMAT
831 ", AccumulatedFrames: %d"
832 ", RectsCoalesced: %d"
833 ", ProtectedContentMaskedOut: %d"
834 ", PointerPosition: (%ldx%ld, visible %d)"
835 ", TotalMetadataBufferSize: %d"
836 ", PointerShapeBufferSize: %d",
837 frame_info->LastPresentTime.QuadPart,
838 frame_info->LastMouseUpdateTime.QuadPart,
839 frame_info->AccumulatedFrames,
840 frame_info->RectsCoalesced,
841 frame_info->ProtectedContentMaskedOut,
842 frame_info->PointerPosition.Position.x,
843 frame_info->PointerPosition.Position.y,
844 frame_info->PointerPosition.Visible,
845 frame_info->TotalMetadataBufferSize,
846 frame_info->PointerShapeBufferSize);
847
848 hr = resource.As (&acquired_texture);
849 if (!gst_d3d11_result (hr, device_)) {
850 GST_ERROR ("Failed to get ID3D11Texture2D interface from IDXGIResource "
851 "hr 0x%x", (guint) hr);
852 return GST_FLOW_ERROR;
853 }
854
855 /* Get metadata */
856 if (frame_info->TotalMetadataBufferSize) {
857 UINT buf_size = frame_info->TotalMetadataBufferSize;
858
859 MaybeReallocMetadataBuffer (buf_size);
860
861 /* Get move rectangles */
862 hr = dupl_->GetFrameMoveRects(buf_size,
863 (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_, &buf_size);
864 if (!gst_d3d11_result (hr, device_)) {
865 GST_ERROR ("Couldn't get move rect, hr 0x%x", (guint) hr);
866
867 return gst_d3d11_screen_capture_return_from_hr(nullptr, hr,
868 FrameInfoExpectedErrors);
869 }
870
871 *move_count = buf_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
872
873 GST_TRACE ("MoveRects count %d", *move_count);
874 #ifndef GST_DISABLE_GST_DEBUG
875 {
876 DXGI_OUTDUPL_MOVE_RECT *rects =
877 (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_;
878 for (guint i = 0; i < *move_count; i++) {
879 GST_TRACE ("MoveRect[%d] SourcePoint: %ldx%ld, "
880 "DestinationRect (left:top:right:bottom): %ldx%ldx%ldx%ld",
881 i, rects->SourcePoint.x, rects->SourcePoint.y,
882 rects->DestinationRect.left, rects->DestinationRect.top,
883 rects->DestinationRect.right, rects->DestinationRect.bottom);
884 }
885 }
886 #endif
887
888 BYTE* dirty_rects = metadata_buffer_ + buf_size;
889 buf_size = frame_info->TotalMetadataBufferSize - buf_size;
890
891 /* Get dirty rectangles */
892 hr = dupl_->GetFrameDirtyRects(buf_size, (RECT *) dirty_rects, &buf_size);
893 if (!gst_d3d11_result (hr, device_)) {
894 GST_ERROR ("Couldn't get dirty rect, hr 0x%x", (guint) hr);
895 *move_count = 0;
896 *dirty_count = 0;
897
898 return gst_d3d11_screen_capture_return_from_hr(nullptr,
899 hr, FrameInfoExpectedErrors);
900 }
901
902 *dirty_count = buf_size / sizeof(RECT);
903
904 GST_TRACE ("DirtyRects count %d", *dirty_count);
905 #ifndef GST_DISABLE_GST_DEBUG
906 {
907 RECT *rects = (RECT *) dirty_rects;
908 for (guint i = 0; i < *dirty_count; i++) {
909 GST_TRACE ("DirtyRect[%d] left:top:right:bottom: %ldx%ldx%ldx%ld",
910 i, rects->left, rects->top, rects->right, rects->bottom);
911 }
912 }
913 #endif
914 }
915
916 *texture = acquired_texture.Detach();
917
918 return GST_FLOW_OK;
919 }
920
921 void
SetMoveRect(RECT * SrcRect,RECT * DestRect,DXGI_OUTDUPL_DESC * DeskDesc,DXGI_OUTDUPL_MOVE_RECT * MoveRect,INT TexWidth,INT TexHeight)922 SetMoveRect (RECT* SrcRect, RECT* DestRect, DXGI_OUTDUPL_DESC* DeskDesc,
923 DXGI_OUTDUPL_MOVE_RECT* MoveRect, INT TexWidth, INT TexHeight)
924 {
925 switch (DeskDesc->Rotation)
926 {
927 case DXGI_MODE_ROTATION_UNSPECIFIED:
928 case DXGI_MODE_ROTATION_IDENTITY:
929 SrcRect->left = MoveRect->SourcePoint.x;
930 SrcRect->top = MoveRect->SourcePoint.y;
931 SrcRect->right = MoveRect->SourcePoint.x +
932 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
933 SrcRect->bottom = MoveRect->SourcePoint.y +
934 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
935
936 *DestRect = MoveRect->DestinationRect;
937 break;
938 case DXGI_MODE_ROTATION_ROTATE90:
939 SrcRect->left = TexHeight - (MoveRect->SourcePoint.y +
940 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
941 SrcRect->top = MoveRect->SourcePoint.x;
942 SrcRect->right = TexHeight - MoveRect->SourcePoint.y;
943 SrcRect->bottom = MoveRect->SourcePoint.x +
944 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
945
946 DestRect->left = TexHeight - MoveRect->DestinationRect.bottom;
947 DestRect->top = MoveRect->DestinationRect.left;
948 DestRect->right = TexHeight - MoveRect->DestinationRect.top;
949 DestRect->bottom = MoveRect->DestinationRect.right;
950 break;
951 case DXGI_MODE_ROTATION_ROTATE180:
952 SrcRect->left = TexWidth - (MoveRect->SourcePoint.x +
953 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
954 SrcRect->top = TexHeight - (MoveRect->SourcePoint.y +
955 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
956 SrcRect->right = TexWidth - MoveRect->SourcePoint.x;
957 SrcRect->bottom = TexHeight - MoveRect->SourcePoint.y;
958
959 DestRect->left = TexWidth - MoveRect->DestinationRect.right;
960 DestRect->top = TexHeight - MoveRect->DestinationRect.bottom;
961 DestRect->right = TexWidth - MoveRect->DestinationRect.left;
962 DestRect->bottom = TexHeight - MoveRect->DestinationRect.top;
963 break;
964 case DXGI_MODE_ROTATION_ROTATE270:
965 SrcRect->left = MoveRect->SourcePoint.x;
966 SrcRect->top = TexWidth - (MoveRect->SourcePoint.x +
967 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
968 SrcRect->right = MoveRect->SourcePoint.y +
969 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
970 SrcRect->bottom = TexWidth - MoveRect->SourcePoint.x;
971
972 DestRect->left = MoveRect->DestinationRect.top;
973 DestRect->top = TexWidth - MoveRect->DestinationRect.right;
974 DestRect->right = MoveRect->DestinationRect.bottom;
975 DestRect->bottom = TexWidth - MoveRect->DestinationRect.left;
976 break;
977 default:
978 memset (DestRect, 0, sizeof (RECT));
979 memset (SrcRect, 0, sizeof (RECT));
980 break;
981 }
982 }
983
984 GstFlowReturn
CopyMove(ID3D11Texture2D * SharedSurf,DXGI_OUTDUPL_MOVE_RECT * MoveBuffer,UINT MoveCount,DXGI_OUTDUPL_DESC * DeskDesc)985 CopyMove (ID3D11Texture2D* SharedSurf, DXGI_OUTDUPL_MOVE_RECT* MoveBuffer,
986 UINT MoveCount, DXGI_OUTDUPL_DESC* DeskDesc)
987 {
988 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
989 ID3D11DeviceContext *device_context =
990 gst_d3d11_device_get_device_context_handle (device_);
991 D3D11_TEXTURE2D_DESC FullDesc;
992 SharedSurf->GetDesc(&FullDesc);
993
994 GST_TRACE ("Copying MoveRects (count %d)", MoveCount);
995
996 /* Make new intermediate surface to copy into for moving */
997 if (!move_texture_) {
998 D3D11_TEXTURE2D_DESC MoveDesc;
999 MoveDesc = FullDesc;
1000 MoveDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
1001 MoveDesc.MiscFlags = 0;
1002 HRESULT hr = device_handle->CreateTexture2D(&MoveDesc,
1003 nullptr, &move_texture_);
1004 if (!gst_d3d11_result (hr, device_)) {
1005 GST_ERROR ("Couldn't create intermediate texture, hr 0x%x", (guint) hr);
1006 return GST_FLOW_ERROR;
1007 }
1008 }
1009
1010 for (UINT i = 0; i < MoveCount; i++) {
1011 RECT SrcRect;
1012 RECT DestRect;
1013
1014 SetMoveRect(&SrcRect, &DestRect, DeskDesc, &MoveBuffer[i],
1015 FullDesc.Width, FullDesc.Height);
1016
1017 /* Copy rect out of shared surface */
1018 D3D11_BOX Box;
1019 Box.left = SrcRect.left;
1020 Box.top = SrcRect.top;
1021 Box.front = 0;
1022 Box.right = SrcRect.right;
1023 Box.bottom = SrcRect.bottom;
1024 Box.back = 1;
1025 device_context->CopySubresourceRegion(move_texture_.Get(),
1026 0, SrcRect.left, SrcRect.top, 0, SharedSurf, 0, &Box);
1027
1028 /* Copy back to shared surface */
1029 device_context->CopySubresourceRegion(SharedSurf,
1030 0, DestRect.left, DestRect.top, 0, move_texture_.Get(), 0, &Box);
1031 }
1032
1033 return GST_FLOW_OK;
1034 }
1035
1036 void
SetDirtyVert(VERTEX * Vertices,RECT * Dirty,DXGI_OUTDUPL_DESC * DeskDesc,D3D11_TEXTURE2D_DESC * FullDesc,D3D11_TEXTURE2D_DESC * ThisDesc)1037 SetDirtyVert (VERTEX* Vertices, RECT* Dirty,
1038 DXGI_OUTDUPL_DESC* DeskDesc, D3D11_TEXTURE2D_DESC* FullDesc,
1039 D3D11_TEXTURE2D_DESC* ThisDesc)
1040 {
1041 INT CenterX = FullDesc->Width / 2;
1042 INT CenterY = FullDesc->Height / 2;
1043
1044 INT Width = FullDesc->Width;
1045 INT Height = FullDesc->Height;
1046
1047 /* Rotation compensated destination rect */
1048 RECT DestDirty = *Dirty;
1049
1050 /* Set appropriate coordinates compensated for rotation */
1051 switch (DeskDesc->Rotation)
1052 {
1053 case DXGI_MODE_ROTATION_ROTATE90:
1054 DestDirty.left = Width - Dirty->bottom;
1055 DestDirty.top = Dirty->left;
1056 DestDirty.right = Width - Dirty->top;
1057 DestDirty.bottom = Dirty->right;
1058
1059 Vertices[0].TexCoord =
1060 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1061 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1062 Vertices[1].TexCoord =
1063 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1064 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1065 Vertices[2].TexCoord =
1066 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1067 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1068 Vertices[5].TexCoord =
1069 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1070 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1071 break;
1072 case DXGI_MODE_ROTATION_ROTATE180:
1073 DestDirty.left = Width - Dirty->right;
1074 DestDirty.top = Height - Dirty->bottom;
1075 DestDirty.right = Width - Dirty->left;
1076 DestDirty.bottom = Height - Dirty->top;
1077
1078 Vertices[0].TexCoord =
1079 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1080 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1081 Vertices[1].TexCoord =
1082 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1083 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1084 Vertices[2].TexCoord =
1085 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1086 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1087 Vertices[5].TexCoord =
1088 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1089 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1090 break;
1091 case DXGI_MODE_ROTATION_ROTATE270:
1092 DestDirty.left = Dirty->top;
1093 DestDirty.top = Height - Dirty->right;
1094 DestDirty.right = Dirty->bottom;
1095 DestDirty.bottom = Height - Dirty->left;
1096
1097 Vertices[0].TexCoord =
1098 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1099 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1100 Vertices[1].TexCoord =
1101 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1102 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1103 Vertices[2].TexCoord =
1104 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1105 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1106 Vertices[5].TexCoord =
1107 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1108 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1109 break;
1110 case DXGI_MODE_ROTATION_UNSPECIFIED:
1111 case DXGI_MODE_ROTATION_IDENTITY:
1112 default:
1113 Vertices[0].TexCoord =
1114 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1115 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1116 Vertices[1].TexCoord =
1117 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1118 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1119 Vertices[2].TexCoord =
1120 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1121 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1122 Vertices[5].TexCoord =
1123 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1124 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1125 break;
1126 }
1127
1128 /* Set positions */
1129 Vertices[0].Pos =
1130 MyFLOAT3(
1131 (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
1132 -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
1133 0.0f);
1134 Vertices[1].Pos =
1135 MyFLOAT3(
1136 (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
1137 -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
1138 0.0f);
1139 Vertices[2].Pos =
1140 MyFLOAT3(
1141 (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
1142 -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
1143 0.0f);
1144 Vertices[3].Pos = Vertices[2].Pos;
1145 Vertices[4].Pos = Vertices[1].Pos;
1146 Vertices[5].Pos =
1147 MyFLOAT3(
1148 (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
1149 -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
1150 0.0f);
1151
1152 Vertices[3].TexCoord = Vertices[2].TexCoord;
1153 Vertices[4].TexCoord = Vertices[1].TexCoord;
1154 }
1155
1156 void
MaybeReallocVertexBuffer(UINT buffer_size)1157 MaybeReallocVertexBuffer (UINT buffer_size)
1158 {
1159 if (buffer_size <= vertex_buffer_size_)
1160 return;
1161
1162 if (vertex_buffer_)
1163 delete[] vertex_buffer_;
1164
1165 vertex_buffer_ = new BYTE[buffer_size];
1166 vertex_buffer_size_ = buffer_size;
1167 }
1168
1169 GstFlowReturn
CopyDirty(ID3D11Texture2D * SrcSurface,ID3D11Texture2D * SharedSurf,RECT * DirtyBuffer,UINT DirtyCount,DXGI_OUTDUPL_DESC * DeskDesc)1170 CopyDirty (ID3D11Texture2D* SrcSurface, ID3D11Texture2D* SharedSurf,
1171 RECT* DirtyBuffer, UINT DirtyCount, DXGI_OUTDUPL_DESC* DeskDesc)
1172 {
1173 D3D11_TEXTURE2D_DESC FullDesc;
1174 D3D11_TEXTURE2D_DESC ThisDesc;
1175 ComPtr<ID3D11ShaderResourceView> ShaderResource;
1176 ComPtr<ID3D11Buffer> VertBuf;
1177 HRESULT hr;
1178 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
1179 ID3D11DeviceContext *device_context =
1180 gst_d3d11_device_get_device_context_handle (device_);
1181
1182 GST_TRACE ("Copying DiretyRects (count %d)", DirtyCount);
1183
1184 SharedSurf->GetDesc(&FullDesc);
1185 SrcSurface->GetDesc(&ThisDesc);
1186
1187 if (!rtv_) {
1188 hr = device_handle->CreateRenderTargetView(SharedSurf, nullptr, &rtv_);
1189 if (!gst_d3d11_result (hr, device_)) {
1190 GST_ERROR ("Couldn't create renter target view, hr 0x%x", (guint) hr);
1191 return GST_FLOW_ERROR;
1192 }
1193 }
1194
1195 D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
1196 ShaderDesc.Format = ThisDesc.Format;
1197 ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1198 ShaderDesc.Texture2D.MostDetailedMip = ThisDesc.MipLevels - 1;
1199 ShaderDesc.Texture2D.MipLevels = ThisDesc.MipLevels;
1200
1201 /* Create new shader resource view */
1202 hr = device_handle->CreateShaderResourceView(SrcSurface,
1203 &ShaderDesc, &ShaderResource);
1204 if (!gst_d3d11_result (hr, device_)) {
1205 return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
1206 SystemTransitionsExpectedErrors);
1207 }
1208
1209 ID3D11SamplerState *samplers = sampler_.Get();
1210 ID3D11ShaderResourceView *srv = ShaderResource.Get();
1211 ID3D11RenderTargetView *rtv = rtv_.Get();
1212 device_context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
1213 device_context->OMSetRenderTargets(1, &rtv, nullptr);
1214 device_context->VSSetShader(vs_.Get(), nullptr, 0);
1215 device_context->PSSetShader(ps_.Get(), nullptr, 0);
1216 device_context->PSSetShaderResources(0, 1, &srv);
1217 device_context->PSSetSamplers(0, 1, &samplers);
1218 device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1219 device_context->IASetInputLayout(layout_.Get());
1220
1221 /* Create space for vertices for the dirty rects if the current space isn't
1222 * large enough */
1223 UINT byte_needed = sizeof(VERTEX) * NUMVERTICES * DirtyCount;
1224 MaybeReallocVertexBuffer (byte_needed);
1225
1226 /* Fill them in */
1227 VERTEX* DirtyVertex = (VERTEX *) vertex_buffer_;
1228 for (UINT i = 0; i < DirtyCount; ++i, DirtyVertex += NUMVERTICES) {
1229 SetDirtyVert(DirtyVertex, &DirtyBuffer[i], DeskDesc,
1230 &FullDesc, &ThisDesc);
1231 }
1232
1233 /* Create vertex buffer */
1234 D3D11_BUFFER_DESC BufferDesc;
1235 memset (&BufferDesc, 0, sizeof (D3D11_BUFFER_DESC));
1236 BufferDesc.Usage = D3D11_USAGE_DEFAULT;
1237 BufferDesc.ByteWidth = byte_needed;
1238 BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1239 BufferDesc.CPUAccessFlags = 0;
1240 D3D11_SUBRESOURCE_DATA InitData;
1241 memset (&InitData, 0, sizeof (D3D11_SUBRESOURCE_DATA));
1242 InitData.pSysMem = vertex_buffer_;
1243
1244 hr = device_handle->CreateBuffer(&BufferDesc, &InitData, &VertBuf);
1245 if (!gst_d3d11_result (hr, device_)) {
1246 GST_ERROR ("Failed to create vertex buffer");
1247 return GST_FLOW_ERROR;
1248 }
1249
1250 UINT Stride = sizeof(VERTEX);
1251 UINT Offset = 0;
1252 ID3D11Buffer *vert_buf = VertBuf.Get();
1253 device_context->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
1254
1255 D3D11_VIEWPORT VP;
1256 VP.Width = static_cast<FLOAT>(FullDesc.Width);
1257 VP.Height = static_cast<FLOAT>(FullDesc.Height);
1258 VP.MinDepth = 0.0f;
1259 VP.MaxDepth = 1.0f;
1260 VP.TopLeftX = 0.0f;
1261 VP.TopLeftY = 0.0f;
1262 device_context->RSSetViewports(1, &VP);
1263
1264 device_context->Draw(NUMVERTICES * DirtyCount, 0);
1265
1266 /* Unbind srv and rtv from context */
1267 srv = nullptr;
1268 device_context->PSSetShaderResources (0, 1, &srv);
1269 device_context->OMSetRenderTargets (0, nullptr, nullptr);
1270
1271 return GST_FLOW_OK;
1272 }
1273
1274 GstFlowReturn
ProcessFrame(ID3D11Texture2D * acquired_texture,ID3D11Texture2D * SharedSurf,DXGI_OUTDUPL_DESC * DeskDesc,UINT move_count,UINT dirty_count,DXGI_OUTDUPL_FRAME_INFO * frame_info)1275 ProcessFrame(ID3D11Texture2D * acquired_texture, ID3D11Texture2D* SharedSurf,
1276 DXGI_OUTDUPL_DESC* DeskDesc, UINT move_count, UINT dirty_count,
1277 DXGI_OUTDUPL_FRAME_INFO * frame_info)
1278 {
1279 GstFlowReturn ret = GST_FLOW_OK;
1280
1281 GST_TRACE ("Processing frame");
1282
1283 /* Process dirties and moves */
1284 if (frame_info->TotalMetadataBufferSize) {
1285 if (move_count) {
1286 ret = CopyMove(SharedSurf, (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_,
1287 move_count, DeskDesc);
1288
1289 if (ret != GST_FLOW_OK)
1290 return ret;
1291 }
1292
1293 if (dirty_count) {
1294 ret = CopyDirty(acquired_texture, SharedSurf,
1295 (RECT *)(metadata_buffer_ +
1296 (move_count * sizeof(DXGI_OUTDUPL_MOVE_RECT))),
1297 dirty_count, DeskDesc);
1298 }
1299 } else {
1300 GST_TRACE ("No metadata");
1301 }
1302
1303 return ret;
1304 }
1305
1306 /* To draw mouse */
1307 bool
ProcessMonoMask(bool IsMono,PTR_INFO * PtrInfo,INT * PtrWidth,INT * PtrHeight,INT * PtrLeft,INT * PtrTop,BYTE ** InitBuffer,D3D11_BOX * Box)1308 ProcessMonoMask (bool IsMono, PTR_INFO* PtrInfo, INT* PtrWidth,
1309 INT* PtrHeight, INT* PtrLeft, INT* PtrTop, BYTE** InitBuffer,
1310 D3D11_BOX* Box)
1311 {
1312 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
1313 ID3D11DeviceContext *context_handle =
1314 gst_d3d11_device_get_device_context_handle (device_);
1315
1316 D3D11_TEXTURE2D_DESC FullDesc;
1317 shared_texture_->GetDesc(&FullDesc);
1318 INT DesktopWidth = FullDesc.Width;
1319 INT DesktopHeight = FullDesc.Height;
1320
1321 // Pointer position
1322 INT GivenLeft = PtrInfo->Position.x;
1323 INT GivenTop = PtrInfo->Position.y;
1324
1325 // Figure out if any adjustment is needed for out of bound positions
1326 if (GivenLeft < 0) {
1327 *PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width);
1328 } else if ((GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width)) > DesktopWidth) {
1329 *PtrWidth = DesktopWidth - GivenLeft;
1330 } else {
1331 *PtrWidth = static_cast<INT>(PtrInfo->shape_info.Width);
1332 }
1333
1334 if (IsMono)
1335 PtrInfo->shape_info.Height = PtrInfo->shape_info.Height / 2;
1336
1337 if (GivenTop < 0) {
1338 *PtrHeight = GivenTop + static_cast<INT>(PtrInfo->shape_info.Height);
1339 } else if ((GivenTop + static_cast<INT>(PtrInfo->shape_info.Height)) > DesktopHeight) {
1340 *PtrHeight = DesktopHeight - GivenTop;
1341 } else {
1342 *PtrHeight = static_cast<INT>(PtrInfo->shape_info.Height);
1343 }
1344
1345 if (IsMono)
1346 PtrInfo->shape_info.Height = PtrInfo->shape_info.Height * 2;
1347
1348 *PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft;
1349 *PtrTop = (GivenTop < 0) ? 0 : GivenTop;
1350
1351 D3D11_TEXTURE2D_DESC CopyBufferDesc;
1352 CopyBufferDesc.Width = *PtrWidth;
1353 CopyBufferDesc.Height = *PtrHeight;
1354 CopyBufferDesc.MipLevels = 1;
1355 CopyBufferDesc.ArraySize = 1;
1356 CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1357 CopyBufferDesc.SampleDesc.Count = 1;
1358 CopyBufferDesc.SampleDesc.Quality = 0;
1359 CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
1360 CopyBufferDesc.BindFlags = 0;
1361 CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1362 CopyBufferDesc.MiscFlags = 0;
1363
1364 ComPtr<ID3D11Texture2D> CopyBuffer;
1365 HRESULT hr = device_handle->CreateTexture2D(&CopyBufferDesc,
1366 nullptr, &CopyBuffer);
1367 if (!gst_d3d11_result (hr, device_)) {
1368 GST_ERROR ("Couldn't create texture for mouse pointer");
1369 return false;
1370 }
1371
1372 Box->left = *PtrLeft;
1373 Box->top = *PtrTop;
1374 Box->right = *PtrLeft + *PtrWidth;
1375 Box->bottom = *PtrTop + *PtrHeight;
1376 context_handle->CopySubresourceRegion(CopyBuffer.Get(),
1377 0, 0, 0, 0, shared_texture_.Get(), 0, Box);
1378
1379 ComPtr<IDXGISurface> CopySurface;
1380 hr = CopyBuffer.As (&CopySurface);
1381 if (!gst_d3d11_result (hr, device_)) {
1382 GST_ERROR ("Couldn't get DXGI resource from mouse texture");
1383 return false;
1384 }
1385
1386 DXGI_MAPPED_RECT MappedSurface;
1387 hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
1388 if (!gst_d3d11_result (hr, device_)) {
1389 GST_ERROR ("Couldn't map DXGI surface");
1390 return false;
1391 }
1392
1393 *InitBuffer = new BYTE[*PtrWidth * *PtrHeight * BPP];
1394
1395 UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer);
1396 UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits);
1397 UINT DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT);
1398
1399 // What to skip (pixel offset)
1400 UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0);
1401 UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0);
1402
1403 if (IsMono) {
1404 for (INT Row = 0; Row < *PtrHeight; Row++) {
1405 BYTE Mask = 0x80;
1406 Mask = Mask >> (SkipX % 8);
1407 for (INT Col = 0; Col < *PtrWidth; Col++) {
1408 BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
1409 ((Row + SkipY) * (PtrInfo->shape_info.Pitch))] & Mask;
1410 BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
1411 ((Row + SkipY + (PtrInfo->shape_info.Height / 2)) *
1412 (PtrInfo->shape_info.Pitch))] & Mask;
1413 UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
1414 UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
1415
1416 InitBuffer32[(Row * *PtrWidth) + Col] =
1417 (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32;
1418
1419 if (Mask == 0x01) {
1420 Mask = 0x80;
1421 } else {
1422 Mask = Mask >> 1;
1423 }
1424 }
1425 }
1426 } else {
1427 UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
1428
1429 for (INT Row = 0; Row < *PtrHeight; Row++) {
1430 for (INT Col = 0; Col < *PtrWidth; ++Col) {
1431 // Set up mask
1432 UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) *
1433 (PtrInfo->shape_info.Pitch / sizeof(UINT)))];
1434 if (MaskVal) {
1435 // Mask was 0xFF
1436 InitBuffer32[(Row * *PtrWidth) + Col] =
1437 (Desktop32[(Row * DesktopPitchInPixels) + Col] ^
1438 Buffer32[(Col + SkipX) + ((Row + SkipY) *
1439 (PtrInfo->shape_info.Pitch / sizeof(UINT)))]) | 0xFF000000;
1440 } else {
1441 // Mask was 0x00
1442 InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) +
1443 ((Row + SkipY) * (PtrInfo->shape_info.Pitch / sizeof(UINT)))] | 0xFF000000;
1444 }
1445 }
1446 }
1447 }
1448
1449 // Done with resource
1450 hr = CopySurface->Unmap();
1451 if (!gst_d3d11_result (hr, device_)) {
1452 GST_ERROR ("Failed to unmap DXGI surface");
1453 return false;
1454 }
1455
1456 return true;
1457 }
1458
1459 private:
1460 PTR_INFO ptr_info_;
1461 DXGI_OUTDUPL_DESC output_desc_;
1462 GstD3D11Device * device_;
1463
1464 ComPtr<ID3D11Texture2D> shared_texture_;
1465 ComPtr<ID3D11RenderTargetView> rtv_;
1466 ComPtr<ID3D11Texture2D> move_texture_;
1467 ComPtr<ID3D11VertexShader> vs_;
1468 ComPtr<ID3D11PixelShader> ps_;
1469 ComPtr<ID3D11InputLayout> layout_;
1470 ComPtr<ID3D11SamplerState> sampler_;
1471 ComPtr<IDXGIOutputDuplication> dupl_;
1472
1473 /* frame metadata */
1474 BYTE *metadata_buffer_;
1475 UINT metadata_buffer_size_;
1476
1477 /* vertex buffers */
1478 BYTE *vertex_buffer_;
1479 UINT vertex_buffer_size_;
1480 };
1481 /* *INDENT-ON* */
1482
1483 enum
1484 {
1485 PROP_0,
1486 PROP_D3D11_DEVICE,
1487 PROP_MONITOR_HANDLE,
1488 };
1489
1490 #define DEFAULT_MONITOR_INDEX -1
1491
1492 struct _GstD3D11ScreenCapture
1493 {
1494 GstObject parent;
1495
1496 GstD3D11Device *device;
1497 guint cached_width;
1498 guint cached_height;
1499
1500 D3D11DesktopDupObject *dupl_obj;
1501
1502 HMONITOR monitor_handle;
1503 RECT desktop_coordinates;
1504 gboolean prepared;
1505 gint64 adapter_luid;
1506
1507 GRecMutex lock;
1508 };
1509
1510 static void gst_d3d11_screen_capture_constructed (GObject * object);
1511 static void gst_d3d11_screen_capture_dispose (GObject * object);
1512 static void gst_d3d11_screen_capture_finalize (GObject * object);
1513 static void gst_d3d11_screen_capture_set_property (GObject * object,
1514 guint prop_id, const GValue * value, GParamSpec * pspec);
1515
1516 #define gst_d3d11_screen_capture_parent_class parent_class
1517 G_DEFINE_TYPE (GstD3D11ScreenCapture, gst_d3d11_screen_capture,
1518 GST_TYPE_OBJECT);
1519
1520 static void
gst_d3d11_screen_capture_class_init(GstD3D11ScreenCaptureClass * klass)1521 gst_d3d11_screen_capture_class_init (GstD3D11ScreenCaptureClass * klass)
1522 {
1523 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1524
1525 gobject_class->constructed = gst_d3d11_screen_capture_constructed;
1526 gobject_class->dispose = gst_d3d11_screen_capture_dispose;
1527 gobject_class->finalize = gst_d3d11_screen_capture_finalize;
1528 gobject_class->set_property = gst_d3d11_screen_capture_set_property;
1529
1530 g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
1531 g_param_spec_object ("d3d11device", "D3D11 Device",
1532 "GstD3D11Device object for operating",
1533 GST_TYPE_D3D11_DEVICE, (GParamFlags)
1534 (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
1535 G_PARAM_STATIC_STRINGS)));
1536 g_object_class_install_property (gobject_class, PROP_MONITOR_HANDLE,
1537 g_param_spec_pointer ("monitor-handle", "Monitor Handle",
1538 "A HMONITOR handle of monitor to capture", (GParamFlags)
1539 (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
1540 G_PARAM_STATIC_STRINGS)));
1541 }
1542
1543 static void
gst_d3d11_screen_capture_init(GstD3D11ScreenCapture * self)1544 gst_d3d11_screen_capture_init (GstD3D11ScreenCapture * self)
1545 {
1546 g_rec_mutex_init (&self->lock);
1547
1548 memset (&self->desktop_coordinates, 0, sizeof (RECT));
1549 }
1550
1551 static void
gst_d3d11_screen_capture_constructed(GObject * object)1552 gst_d3d11_screen_capture_constructed (GObject * object)
1553 {
1554 GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
1555 /* *INDENT-OFF* */
1556 ComPtr<IDXGIDevice> dxgi_device;
1557 ComPtr<IDXGIAdapter1> adapter;
1558 ComPtr<IDXGIOutput> output;
1559 ComPtr<IDXGIOutput1> output1;
1560 /* *INDENT-ON* */
1561 HRESULT hr;
1562 gboolean ret = FALSE;
1563 DXGI_OUTPUT_DESC output_desc;
1564 DXGI_ADAPTER_DESC adapter_desc;
1565 gint64 luid, device_luid;
1566
1567 if (!self->device) {
1568 GST_WARNING_OBJECT (self, "D3D11 device is unavailable");
1569 goto out;
1570 }
1571
1572 if (!self->monitor_handle) {
1573 GST_WARNING_OBJECT (self, "Null monitor handle");
1574 goto out;
1575 }
1576
1577 hr = gst_d3d11_screen_capture_find_output_for_monitor (self->monitor_handle,
1578 &adapter, &output);
1579 if (!gst_d3d11_result (hr, self->device)) {
1580 GST_WARNING_OBJECT (self,
1581 "Failed to find associated adapter for monitor %p",
1582 self->monitor_handle);
1583 goto out;
1584 }
1585
1586 hr = output.As (&output1);
1587 if (!gst_d3d11_result (hr, self->device)) {
1588 GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
1589 goto out;
1590 }
1591
1592 hr = adapter->GetDesc (&adapter_desc);
1593 if (!gst_d3d11_result (hr, self->device)) {
1594 GST_WARNING_OBJECT (self, "Failed to get adapter desc");
1595 goto out;
1596 }
1597
1598 luid = gst_d3d11_luid_to_int64 (&adapter_desc.AdapterLuid);
1599 g_object_get (self->device, "adapter-luid", &device_luid, nullptr);
1600 if (luid != device_luid) {
1601 GST_WARNING_OBJECT (self, "Incompatible d3d11 device");
1602 goto out;
1603 }
1604
1605 hr = output->GetDesc (&output_desc);
1606 if (!gst_d3d11_result (hr, self->device)) {
1607 GST_WARNING_OBJECT (self, "Failed to get output desc");
1608 goto out;
1609 }
1610
1611 /* DesktopCoordinates will not report actual texture size in case that
1612 * application is running without dpi-awareness. To get actual monitor size,
1613 * we need to use Win32 API... */
1614 MONITORINFOEXW monitor_info;
1615 DEVMODEW dev_mode;
1616
1617 monitor_info.cbSize = sizeof (MONITORINFOEXW);
1618 if (!GetMonitorInfoW (output_desc.Monitor, (LPMONITORINFO) & monitor_info)) {
1619 GST_WARNING_OBJECT (self, "Couldn't get monitor info");
1620 goto out;
1621 }
1622
1623 dev_mode.dmSize = sizeof (DEVMODEW);
1624 dev_mode.dmDriverExtra = sizeof (POINTL);
1625 dev_mode.dmFields = DM_POSITION;
1626 if (!EnumDisplaySettingsW
1627 (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
1628 GST_WARNING_OBJECT (self, "Couldn't enumerate display settings");
1629 goto out;
1630 }
1631
1632 self->desktop_coordinates.left = dev_mode.dmPosition.x;
1633 self->desktop_coordinates.top = dev_mode.dmPosition.y;
1634 self->desktop_coordinates.right =
1635 dev_mode.dmPosition.x + dev_mode.dmPelsWidth;
1636 self->desktop_coordinates.bottom =
1637 dev_mode.dmPosition.y + dev_mode.dmPelsHeight;
1638
1639 self->cached_width =
1640 self->desktop_coordinates.right - self->desktop_coordinates.left;
1641 self->cached_height =
1642 self->desktop_coordinates.bottom - self->desktop_coordinates.top;
1643
1644 GST_DEBUG_OBJECT (self,
1645 "Desktop coordinates left:top:right:bottom = %ld:%ld:%ld:%ld (%dx%d)",
1646 self->desktop_coordinates.left, self->desktop_coordinates.top,
1647 self->desktop_coordinates.right, self->desktop_coordinates.bottom,
1648 self->cached_width, self->cached_height);
1649
1650 g_object_get (self->device, "adapter-luid", &self->adapter_luid, nullptr);
1651
1652 ret = TRUE;
1653
1654 out:
1655 if (!ret)
1656 gst_clear_object (&self->device);
1657
1658 G_OBJECT_CLASS (parent_class)->constructed (object);
1659 }
1660
1661 static void
gst_d3d11_screen_capture_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1662 gst_d3d11_screen_capture_set_property (GObject * object, guint prop_id,
1663 const GValue * value, GParamSpec * pspec)
1664 {
1665 GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
1666
1667 switch (prop_id) {
1668 case PROP_D3D11_DEVICE:
1669 self->device = (GstD3D11Device *) g_value_dup_object (value);
1670 break;
1671 case PROP_MONITOR_HANDLE:
1672 self->monitor_handle = (HMONITOR) g_value_get_pointer (value);
1673 break;
1674 default:
1675 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1676 break;
1677 }
1678 }
1679
1680 static void
gst_d3d11_screen_capture_dispose(GObject * object)1681 gst_d3d11_screen_capture_dispose (GObject * object)
1682 {
1683 GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
1684
1685 if (self->dupl_obj) {
1686 delete self->dupl_obj;
1687 self->dupl_obj = nullptr;
1688 }
1689
1690 gst_clear_object (&self->device);
1691
1692 G_OBJECT_CLASS (parent_class)->dispose (object);
1693 }
1694
1695 static void
gst_d3d11_screen_capture_finalize(GObject * object)1696 gst_d3d11_screen_capture_finalize (GObject * object)
1697 {
1698 GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
1699
1700 g_rec_mutex_clear (&self->lock);
1701
1702 G_OBJECT_CLASS (parent_class)->finalize (object);
1703 }
1704
1705 static void
gst_d3d11_screen_capture_weak_ref_notify(gpointer data,GstD3D11ScreenCapture * dupl)1706 gst_d3d11_screen_capture_weak_ref_notify (gpointer data,
1707 GstD3D11ScreenCapture * dupl)
1708 {
1709 G_LOCK (dupl_list_lock);
1710 dupl_list = g_list_remove (dupl_list, dupl);
1711 G_UNLOCK (dupl_list_lock);
1712 }
1713
1714 GstD3D11ScreenCapture *
gst_d3d11_screen_capture_new(GstD3D11Device * device,HMONITOR monitor_handle)1715 gst_d3d11_screen_capture_new (GstD3D11Device * device, HMONITOR monitor_handle)
1716 {
1717 GstD3D11ScreenCapture *self = nullptr;
1718 GList *iter;
1719
1720 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
1721
1722 /* Check if we have dup object corresponding to monitor_handle,
1723 * and if there is already configured capture object, reuse it.
1724 * This is because of the limitation of desktop duplication API
1725 * (i.e., in a process, only one duplication object can exist).
1726 * See also
1727 * https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutput1-duplicateoutput#remarks
1728 */
1729 G_LOCK (dupl_list_lock);
1730 for (iter = dupl_list; iter; iter = g_list_next (iter)) {
1731 GstD3D11ScreenCapture *dupl = (GstD3D11ScreenCapture *) iter->data;
1732
1733 if (dupl->monitor_handle == monitor_handle) {
1734 GST_DEBUG ("Found configured desktop dup object for monitor handle %p",
1735 monitor_handle);
1736 self = (GstD3D11ScreenCapture *) gst_object_ref (dupl);
1737 break;
1738 }
1739 }
1740
1741 if (self) {
1742 G_UNLOCK (dupl_list_lock);
1743 return self;
1744 }
1745
1746 self = (GstD3D11ScreenCapture *) g_object_new (GST_TYPE_D3D11_SCREEN_CAPTURE,
1747 "d3d11device", device, "monitor-handle", monitor_handle, nullptr);
1748
1749 if (!self->device) {
1750 GST_WARNING_OBJECT (self, "Couldn't configure desktop dup object");
1751 gst_object_unref (self);
1752 G_UNLOCK (dupl_list_lock);
1753
1754 return nullptr;
1755 }
1756
1757 g_object_weak_ref (G_OBJECT (self),
1758 (GWeakNotify) gst_d3d11_screen_capture_weak_ref_notify, nullptr);
1759 dupl_list = g_list_append (dupl_list, self);
1760
1761 G_UNLOCK (dupl_list_lock);
1762
1763 return self;
1764 }
1765
1766 GstFlowReturn
gst_d3d11_screen_capture_prepare(GstD3D11ScreenCapture * capture)1767 gst_d3d11_screen_capture_prepare (GstD3D11ScreenCapture * capture)
1768 {
1769 GstFlowReturn ret;
1770
1771 g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR);
1772 g_return_val_if_fail (capture->device != nullptr, GST_FLOW_ERROR);
1773
1774 g_rec_mutex_lock (&capture->lock);
1775 if (capture->prepared) {
1776 GST_DEBUG_OBJECT (capture, "Already prepared");
1777 g_rec_mutex_unlock (&capture->lock);
1778 return GST_FLOW_OK;
1779 }
1780
1781 capture->dupl_obj = new D3D11DesktopDupObject ();
1782 ret = capture->dupl_obj->Init (capture->device, capture->monitor_handle);
1783 if (ret != GST_FLOW_OK) {
1784 GST_WARNING_OBJECT (capture,
1785 "Couldn't prepare capturing, %sexpected failure",
1786 ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR ? "" : "un");
1787
1788 delete capture->dupl_obj;
1789 capture->dupl_obj = nullptr;
1790 g_rec_mutex_unlock (&capture->lock);
1791
1792 return ret;
1793 }
1794
1795 capture->prepared = TRUE;
1796 g_rec_mutex_unlock (&capture->lock);
1797
1798 return GST_FLOW_OK;
1799 }
1800
1801 gboolean
gst_d3d11_screen_capture_get_size(GstD3D11ScreenCapture * capture,guint * width,guint * height)1802 gst_d3d11_screen_capture_get_size (GstD3D11ScreenCapture * capture,
1803 guint * width, guint * height)
1804 {
1805 g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), FALSE);
1806 g_return_val_if_fail (width != nullptr, FALSE);
1807 g_return_val_if_fail (height != nullptr, FALSE);
1808
1809 g_rec_mutex_lock (&capture->lock);
1810 *width = 0;
1811 *height = 0;
1812
1813 if (capture->dupl_obj) {
1814 capture->dupl_obj->GetSize (&capture->cached_width,
1815 &capture->cached_height);
1816 }
1817
1818 *width = capture->cached_width;
1819 *height = capture->cached_height;
1820 g_rec_mutex_unlock (&capture->lock);
1821
1822 return TRUE;
1823 }
1824
1825 GstFlowReturn
gst_d3d11_screen_capture_do_capture(GstD3D11ScreenCapture * capture,GstD3D11Device * device,ID3D11Texture2D * texture,ID3D11RenderTargetView * rtv,ID3D11VertexShader * vs,ID3D11PixelShader * ps,ID3D11InputLayout * layout,ID3D11SamplerState * sampler,ID3D11BlendState * blend,gboolean draw_mouse)1826 gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
1827 GstD3D11Device * device, ID3D11Texture2D * texture,
1828 ID3D11RenderTargetView * rtv, ID3D11VertexShader * vs,
1829 ID3D11PixelShader * ps, ID3D11InputLayout * layout,
1830 ID3D11SamplerState * sampler, ID3D11BlendState * blend, gboolean draw_mouse)
1831 {
1832 GstFlowReturn ret = GST_FLOW_OK;
1833 D3D11_TEXTURE2D_DESC desc;
1834 gboolean shared_device = FALSE;
1835 guint width, height;
1836
1837 g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR);
1838 g_return_val_if_fail (texture != nullptr, GST_FLOW_ERROR);
1839
1840 if (device != capture->device) {
1841 gint64 luid;
1842
1843 g_object_get (device, "adapter-luid", &luid, nullptr);
1844 /* source element must hold d3d11 device for the same GPU already
1845 * by DXGI duplication API design */
1846 if (luid != capture->adapter_luid) {
1847 GST_ERROR_OBJECT (capture, "Trying to capture from different device");
1848 return GST_FLOW_ERROR;
1849 }
1850
1851 shared_device = TRUE;
1852 }
1853
1854 g_rec_mutex_lock (&capture->lock);
1855 if (!capture->prepared)
1856 ret = gst_d3d11_screen_capture_prepare (capture);
1857
1858 if (ret != GST_FLOW_OK) {
1859 GST_WARNING_OBJECT (capture, "We are not prepared");
1860 g_rec_mutex_unlock (&capture->lock);
1861 return ret;
1862 }
1863
1864 gst_d3d11_screen_capture_get_size (capture, &width, &height);
1865
1866 texture->GetDesc (&desc);
1867 if (desc.Width != width || desc.Height != height) {
1868 GST_INFO_OBJECT (capture,
1869 "Different texture size, ours: %dx%d, external: %dx%d",
1870 width, height, desc.Width, desc.Height);
1871 g_rec_mutex_unlock (&capture->lock);
1872
1873 return GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED;
1874 }
1875
1876 gst_d3d11_device_lock (capture->device);
1877 ret = capture->dupl_obj->Capture ();
1878 if (ret != GST_FLOW_OK) {
1879 gst_d3d11_device_unlock (capture->device);
1880
1881 delete capture->dupl_obj;
1882 capture->dupl_obj = nullptr;
1883 capture->prepared = FALSE;
1884
1885 if (ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR) {
1886 GST_WARNING_OBJECT (capture,
1887 "Couldn't capture frame, but expected failure");
1888 } else {
1889 GST_ERROR_OBJECT (capture, "Unexpected failure during capture");
1890 }
1891
1892 g_rec_mutex_unlock (&capture->lock);
1893 return ret;
1894 }
1895
1896 GST_LOG_OBJECT (capture, "Capture done");
1897 if (shared_device)
1898 gst_d3d11_device_lock (device);
1899
1900 ret = capture->dupl_obj->CopyToTexture (device, texture);
1901 if (ret != GST_FLOW_OK)
1902 goto out;
1903
1904 if (draw_mouse)
1905 capture->dupl_obj->DrawMouse (device, rtv, vs, ps, layout, sampler, blend);
1906
1907 out:
1908 if (shared_device)
1909 gst_d3d11_device_unlock (device);
1910
1911 gst_d3d11_device_unlock (capture->device);
1912 g_rec_mutex_unlock (&capture->lock);
1913
1914 return ret;
1915 }
1916
1917 HRESULT
gst_d3d11_screen_capture_find_output_for_monitor(HMONITOR monitor,IDXGIAdapter1 ** adapter,IDXGIOutput ** output)1918 gst_d3d11_screen_capture_find_output_for_monitor (HMONITOR monitor,
1919 IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
1920 {
1921 ComPtr < IDXGIFactory1 > factory;
1922 HRESULT hr = S_OK;
1923
1924 g_return_val_if_fail (monitor != nullptr, E_INVALIDARG);
1925
1926 hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
1927 if (FAILED (hr))
1928 return hr;
1929
1930 for (UINT adapter_idx = 0;; adapter_idx++) {
1931 ComPtr < IDXGIAdapter1 > adapter_tmp;
1932
1933 hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
1934 if (FAILED (hr))
1935 break;
1936
1937 for (UINT output_idx = 0;; output_idx++) {
1938 ComPtr < IDXGIOutput > output_tmp;
1939 DXGI_OUTPUT_DESC desc;
1940
1941 hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
1942 if (FAILED (hr))
1943 break;
1944
1945 hr = output_tmp->GetDesc (&desc);
1946 if (FAILED (hr))
1947 continue;
1948
1949 if (desc.Monitor == monitor) {
1950 if (adapter)
1951 *adapter = adapter_tmp.Detach ();
1952 if (output)
1953 *output = output_tmp.Detach ();
1954
1955 return S_OK;
1956 }
1957 }
1958 }
1959
1960 return E_FAIL;
1961 }
1962
1963 HRESULT
gst_d3d11_screen_capture_find_primary_monitor(HMONITOR * monitor,IDXGIAdapter1 ** adapter,IDXGIOutput ** output)1964 gst_d3d11_screen_capture_find_primary_monitor (HMONITOR * monitor,
1965 IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
1966 {
1967 ComPtr < IDXGIFactory1 > factory;
1968 HRESULT hr = S_OK;
1969
1970 hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
1971 if (FAILED (hr))
1972 return hr;
1973
1974 for (UINT adapter_idx = 0;; adapter_idx++) {
1975 ComPtr < IDXGIAdapter1 > adapter_tmp;
1976
1977 hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
1978 if (FAILED (hr))
1979 break;
1980
1981 for (UINT output_idx = 0;; output_idx++) {
1982 ComPtr < IDXGIOutput > output_tmp;
1983 DXGI_OUTPUT_DESC desc;
1984 MONITORINFOEXW minfo;
1985
1986 hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
1987 if (FAILED (hr))
1988 break;
1989
1990 hr = output_tmp->GetDesc (&desc);
1991 if (FAILED (hr))
1992 continue;
1993
1994 minfo.cbSize = sizeof (MONITORINFOEXW);
1995 if (!GetMonitorInfoW (desc.Monitor, &minfo))
1996 continue;
1997
1998 if ((minfo.dwFlags & MONITORINFOF_PRIMARY) != 0) {
1999 if (monitor)
2000 *monitor = desc.Monitor;
2001 if (adapter)
2002 *adapter = adapter_tmp.Detach ();
2003 if (output)
2004 *output = output_tmp.Detach ();
2005
2006 return S_OK;
2007 }
2008 }
2009 }
2010
2011 return E_FAIL;
2012 }
2013
2014 HRESULT
gst_d3d11_screen_capture_find_nth_monitor(guint index,HMONITOR * monitor,IDXGIAdapter1 ** adapter,IDXGIOutput ** output)2015 gst_d3d11_screen_capture_find_nth_monitor (guint index, HMONITOR * monitor,
2016 IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
2017 {
2018 ComPtr < IDXGIFactory1 > factory;
2019 HRESULT hr = S_OK;
2020 guint num_found = 0;
2021
2022 hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
2023 if (FAILED (hr))
2024 return hr;
2025
2026 for (UINT adapter_idx = 0;; adapter_idx++) {
2027 ComPtr < IDXGIAdapter1 > adapter_tmp;
2028
2029 hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
2030 if (FAILED (hr))
2031 break;
2032
2033 for (UINT output_idx = 0;; output_idx++) {
2034 ComPtr < IDXGIOutput > output_tmp;
2035 DXGI_OUTPUT_DESC desc;
2036 MONITORINFOEXW minfo;
2037
2038 hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
2039 if (FAILED (hr))
2040 break;
2041
2042 hr = output_tmp->GetDesc (&desc);
2043 if (FAILED (hr))
2044 continue;
2045
2046 minfo.cbSize = sizeof (MONITORINFOEXW);
2047 if (!GetMonitorInfoW (desc.Monitor, &minfo))
2048 continue;
2049
2050 if (num_found == index) {
2051 if (monitor)
2052 *monitor = desc.Monitor;
2053 if (adapter)
2054 *adapter = adapter_tmp.Detach ();
2055 if (output)
2056 *output = output_tmp.Detach ();
2057
2058 return S_OK;
2059 }
2060
2061 num_found++;
2062 }
2063 }
2064
2065 return E_FAIL;
2066 }
2067