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