• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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