d3d11screencapture: Subclassing capture implementation
authorSeungha Yang <seungha@centricular.com>
Thu, 6 Oct 2022 14:54:14 +0000 (23:54 +0900)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 21 Oct 2022 14:21:28 +0000 (14:21 +0000)
Preparation to use WinRT capture API

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3144>

subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11screencapture.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11screencapture.h
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11screencapturesrc.cpp
subprojects/gst-plugins-bad/sys/d3d11/meson.build

diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.cpp
new file mode 100644 (file)
index 0000000..fbdf8f6
--- /dev/null
@@ -0,0 +1,1983 @@
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) Microsoft Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to deal
+ *  in the Software without restriction, including without limitation the rights
+ *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *  copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ *  all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *  THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstd3d11dxgicapture.h"
+#include "gstd3d11pluginutils.h"
+#include <string.h>
+
+#include <wrl.h>
+
+GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_screen_capture_debug);
+#define GST_CAT_DEFAULT gst_d3d11_screen_capture_debug
+
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+
+/* List of GstD3D11DxgiCapture weakref */
+G_LOCK_DEFINE_STATIC (dupl_list_lock);
+static GList *dupl_list = nullptr;
+
+/* Below implemenation were taken from Microsoft sample
+ * https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/DXGIDesktopDuplication
+ */
+#define NUMVERTICES 6
+#define BPP 4
+
+/* Define our own MyFLOAT3 and MyFLOAT2 struct, since MinGW doesn't support
+ * DirectXMath.h
+ */
+struct MyFLOAT3
+{
+  float x;
+  float y;
+  float z;
+
+  MyFLOAT3() = default;
+
+  MyFLOAT3(const MyFLOAT3&) = default;
+  MyFLOAT3& operator=(const MyFLOAT3&) = default;
+
+  MyFLOAT3(MyFLOAT3&&) = default;
+  MyFLOAT3& operator=(MyFLOAT3&&) = default;
+
+  constexpr MyFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
+  explicit MyFLOAT3(const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
+};
+
+struct MyFLOAT2
+{
+  float x;
+  float y;
+
+  MyFLOAT2() = default;
+
+  MyFLOAT2(const MyFLOAT2&) = default;
+  MyFLOAT2& operator=(const MyFLOAT2&) = default;
+
+  MyFLOAT2(MyFLOAT2&&) = default;
+  MyFLOAT2& operator=(MyFLOAT2&&) = default;
+
+  constexpr MyFLOAT2(float _x, float _y) : x(_x), y(_y) {}
+  explicit MyFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {}
+};
+
+typedef struct
+{
+  MyFLOAT3 Pos;
+  MyFLOAT2 TexCoord;
+} VERTEX;
+
+/* List of expected error cases */
+/* These are the errors we expect from general Dxgi API due to a transition */
+HRESULT SystemTransitionsExpectedErrors[] = {
+  DXGI_ERROR_DEVICE_REMOVED,
+  DXGI_ERROR_ACCESS_LOST,
+  static_cast<HRESULT>(WAIT_ABANDONED),
+  S_OK
+};
+
+/* These are the errors we expect from IDXGIOutput1::DuplicateOutput
+ * due to a transition */
+HRESULT CreateDuplicationExpectedErrors[] = {
+  DXGI_ERROR_DEVICE_REMOVED,
+  static_cast<HRESULT>(E_ACCESSDENIED),
+  DXGI_ERROR_SESSION_DISCONNECTED,
+  S_OK
+};
+
+/* These are the errors we expect from IDXGIOutputDuplication methods
+ * due to a transition */
+HRESULT FrameInfoExpectedErrors[] = {
+  DXGI_ERROR_DEVICE_REMOVED,
+  DXGI_ERROR_ACCESS_LOST,
+  S_OK
+};
+
+/* These are the errors we expect from IDXGIAdapter::EnumOutputs methods
+ * due to outputs becoming stale during a transition */
+HRESULT EnumOutputsExpectedErrors[] = {
+  DXGI_ERROR_NOT_FOUND,
+  S_OK
+};
+
+static GstFlowReturn
+gst_d3d11_dxgi_capture_return_from_hr (ID3D11Device * device,
+    HRESULT hr, HRESULT * expected_errors = nullptr)
+{
+  HRESULT translated_hr = hr;
+
+  /* On an error check if the DX device is lost */
+  if (device) {
+    HRESULT remove_reason = device->GetDeviceRemovedReason ();
+
+    switch (remove_reason) {
+      case DXGI_ERROR_DEVICE_REMOVED:
+      case DXGI_ERROR_DEVICE_RESET:
+      case static_cast<HRESULT>(E_OUTOFMEMORY):
+        /* Our device has been stopped due to an external event on the GPU so
+         * map them all to device removed and continue processing the condition
+         */
+        translated_hr = DXGI_ERROR_DEVICE_REMOVED;
+        break;
+      case S_OK:
+        /* Device is not removed so use original error */
+        break;
+      default:
+        /* Device is removed but not a error we want to remap */
+        translated_hr = remove_reason;
+        break;
+    }
+  }
+
+  /* Check if this error was expected or not */
+  if (expected_errors) {
+    HRESULT* rst = expected_errors;
+
+    while (*rst != S_OK) {
+      if (*rst == translated_hr)
+        return GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR;
+
+      rst++;
+    }
+  }
+
+  return GST_FLOW_ERROR;
+}
+
+class PTR_INFO
+{
+public:
+  PTR_INFO ()
+    : PtrShapeBuffer (nullptr)
+    , BufferSize (0)
+  {
+    LastTimeStamp.QuadPart = 0;
+  }
+
+  ~PTR_INFO ()
+  {
+    if (PtrShapeBuffer)
+      delete[] PtrShapeBuffer;
+  }
+
+  void
+  MaybeReallocBuffer (UINT buffer_size)
+  {
+    if (buffer_size <= BufferSize)
+      return;
+
+    if (PtrShapeBuffer)
+      delete[] PtrShapeBuffer;
+
+    PtrShapeBuffer = new BYTE[buffer_size];
+    BufferSize = buffer_size;
+  }
+
+  BYTE* PtrShapeBuffer;
+  UINT BufferSize;
+  DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
+  POINT Position;
+  bool Visible;
+  LARGE_INTEGER LastTimeStamp;
+};
+
+class D3D11DesktopDupObject
+{
+public:
+  D3D11DesktopDupObject ()
+    : device_(nullptr)
+    , fence_(nullptr)
+    , metadata_buffer_(nullptr)
+    , metadata_buffer_size_(0)
+    , vertex_buffer_(nullptr)
+    , vertex_buffer_size_(0)
+  {
+  }
+
+  ~D3D11DesktopDupObject ()
+  {
+    if (metadata_buffer_)
+      delete[] metadata_buffer_;
+
+    if (vertex_buffer_)
+      delete[] vertex_buffer_;
+
+    gst_clear_d3d11_fence (&fence_);
+    gst_clear_object (&device_);
+  }
+
+  GstFlowReturn
+  Init (GstD3D11Device * device, HMONITOR monitor)
+  {
+    GstFlowReturn ret;
+    ID3D11Device *device_handle;
+    HRESULT hr;
+    D3D11_TEXTURE2D_DESC texture_desc = { 0, };
+
+    if (!InitShader (device))
+      return GST_FLOW_ERROR;
+
+    ret = InitDupl (device, monitor);
+    if (ret != GST_FLOW_OK)
+      return ret;
+
+    GST_INFO ("Init done");
+
+    device_handle = gst_d3d11_device_get_device_handle (device);
+
+    texture_desc.Width = output_desc_.ModeDesc.Width;
+    texture_desc.Height = output_desc_.ModeDesc.Height;
+    texture_desc.MipLevels = 1;
+    texture_desc.ArraySize = 1;
+    /* FIXME: we can support DXGI_FORMAT_R10G10B10A2_UNORM */
+    texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    texture_desc.SampleDesc.Count = 1;
+    texture_desc.Usage = D3D11_USAGE_DEFAULT;
+    texture_desc.BindFlags =
+        D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
+    texture_desc.CPUAccessFlags = 0;
+    /* source element may hold different d3d11 device object */
+    texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+
+    hr = device_handle->CreateTexture2D (&texture_desc,
+        nullptr, &shared_texture_);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR_OBJECT (device, "Couldn't create texture, hr 0x%x", (guint) hr);
+      return GST_FLOW_ERROR;
+    }
+
+    device_ = (GstD3D11Device *) gst_object_ref (device);
+
+    return GST_FLOW_OK;
+  }
+
+  GstFlowReturn
+  Capture ()
+  {
+    GstFlowReturn ret;
+    bool timeout = false;
+    ComPtr<ID3D11Texture2D> texture;
+    UINT move_count, dirty_count;
+    DXGI_OUTDUPL_FRAME_INFO frame_info;
+
+    GST_TRACE ("Capturing");
+    ret = GetFrame (&texture, &move_count, &dirty_count, &frame_info, &timeout);
+    if (ret != GST_FLOW_OK)
+      return ret;
+
+    /* Nothing updated */
+    if (timeout) {
+      GST_TRACE ("timeout");
+      return GST_FLOW_OK;
+    }
+
+    GST_TRACE ("Getting mouse pointer info");
+    ret = GetMouse (&ptr_info_, &frame_info);
+    if (ret != GST_FLOW_OK) {
+      GST_WARNING ("Couldn't get mouse pointer info");
+      dupl_->ReleaseFrame ();
+      return ret;
+    }
+
+    ret = ProcessFrame (texture.Get(), shared_texture_.Get(),
+        &output_desc_, move_count, dirty_count, &frame_info);
+
+    if (ret != GST_FLOW_OK) {
+      dupl_->ReleaseFrame ();
+      GST_WARNING ("Couldn't process frame");
+      return ret;
+    }
+
+    HRESULT hr = dupl_->ReleaseFrame ();
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_WARNING ("Couldn't release frame");
+      return gst_d3d11_dxgi_capture_return_from_hr (nullptr, hr, FrameInfoExpectedErrors);
+    }
+
+    GST_TRACE ("Capture done");
+
+    return GST_FLOW_OK;
+  }
+
+  bool
+  DrawMouse (GstD3D11Device * device, ID3D11RenderTargetView * rtv,
+      ID3D11VertexShader * vs, ID3D11PixelShader * ps,
+      ID3D11InputLayout * layout, ID3D11SamplerState * sampler,
+      ID3D11BlendState * blend, D3D11_BOX * cropBox)
+  {
+    GST_TRACE ("Drawing mouse");
+
+    if (!ptr_info_.Visible) {
+      GST_TRACE ("Mouse is invisiable");
+      return true;
+    }
+
+    ComPtr<ID3D11Texture2D> MouseTex;
+    ComPtr<ID3D11ShaderResourceView> ShaderRes;
+    ComPtr<ID3D11Buffer> VertexBufferMouse;
+    D3D11_SUBRESOURCE_DATA InitData;
+    D3D11_TEXTURE2D_DESC Desc;
+    D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
+    ID3D11DeviceContext *context_handle =
+        gst_d3d11_device_get_device_context_handle (device);
+
+    VERTEX Vertices[NUMVERTICES] =
+    {
+      {MyFLOAT3(-1.0f, -1.0f, 0), MyFLOAT2(0.0f, 1.0f)},
+      {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
+      {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
+      {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
+      {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
+      {MyFLOAT3(1.0f, 1.0f, 0), MyFLOAT2(1.0f, 0.0f)},
+    };
+
+    D3D11_TEXTURE2D_DESC FullDesc;
+    shared_texture_->GetDesc(&FullDesc);
+    INT DesktopWidth = FullDesc.Width;
+    INT DesktopHeight = FullDesc.Height;
+
+    INT CenterX = (DesktopWidth / 2);
+    INT CenterY = (DesktopHeight / 2);
+
+    INT PtrWidth = 0;
+    INT PtrHeight = 0;
+    INT PtrLeft = 0;
+    INT PtrTop = 0;
+
+    BYTE* InitBuffer = nullptr;
+
+    D3D11_BOX Box;
+    Box.front = 0;
+    Box.back = 1;
+
+    Desc.MipLevels = 1;
+    Desc.ArraySize = 1;
+    Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    Desc.SampleDesc.Count = 1;
+    Desc.SampleDesc.Quality = 0;
+    Desc.Usage = D3D11_USAGE_DEFAULT;
+    Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    Desc.CPUAccessFlags = 0;
+    Desc.MiscFlags = 0;
+
+    // Set shader resource properties
+    SDesc.Format = Desc.Format;
+    SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1;
+    SDesc.Texture2D.MipLevels = Desc.MipLevels;
+
+    switch (ptr_info_.shape_info.Type) {
+      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
+        PtrLeft = ptr_info_.Position.x;
+        PtrTop = ptr_info_.Position.y;
+
+        PtrWidth = static_cast<INT>(ptr_info_.shape_info.Width);
+        PtrHeight = static_cast<INT>(ptr_info_.shape_info.Height);
+        break;
+      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
+        ProcessMonoMask(true, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
+            &PtrTop, &InitBuffer, &Box);
+        break;
+      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
+        ProcessMonoMask(false, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
+            &PtrTop, &InitBuffer, &Box);
+        break;
+      default:
+        break;
+    }
+
+    /* Nothing to draw */
+    if (PtrWidth == 0 || PtrHeight == 0 ||
+        (PtrLeft + PtrWidth) < static_cast<INT>(cropBox->left) ||
+        PtrLeft > static_cast<INT>(cropBox->right) ||
+        (PtrTop + PtrHeight) < static_cast<INT>(cropBox->top) ||
+        PtrTop > static_cast<INT>(cropBox->bottom)) {
+      if (InitBuffer)
+        delete[] InitBuffer;
+
+      return true;
+    }
+
+    PtrLeft -= cropBox->left;
+    PtrTop -= cropBox->top;
+
+    Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
+    Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
+    Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
+    Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
+    Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
+    Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
+    Vertices[3].Pos.x = Vertices[2].Pos.x;
+    Vertices[3].Pos.y = Vertices[2].Pos.y;
+    Vertices[4].Pos.x = Vertices[1].Pos.x;
+    Vertices[4].Pos.y = Vertices[1].Pos.y;
+    Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
+    Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
+
+    Desc.Width = PtrWidth;
+    Desc.Height = PtrHeight;
+
+    InitData.pSysMem =
+        (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
+         ptr_info_.PtrShapeBuffer : InitBuffer;
+    InitData.SysMemPitch =
+        (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
+        ptr_info_.shape_info.Pitch : PtrWidth * BPP;
+    InitData.SysMemSlicePitch = 0;
+
+    // Create mouseshape as texture
+    HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create texture for rendering mouse");
+      return false;
+    }
+
+    // Create shader resource from texture
+    hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
+        &ShaderRes);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create shader resource view for rendering mouse");
+      return false;
+    }
+
+    D3D11_BUFFER_DESC BDesc;
+    memset (&BDesc, 0, sizeof(D3D11_BUFFER_DESC));
+    BDesc.Usage = D3D11_USAGE_DEFAULT;
+    BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
+    BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    BDesc.CPUAccessFlags = 0;
+
+    memset (&InitData, 0, sizeof(D3D11_SUBRESOURCE_DATA));
+    InitData.pSysMem = Vertices;
+
+    // Create vertex buffer
+    hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create vertex buffer for rendering mouse");
+      return false;
+    }
+
+    FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
+    UINT Stride = sizeof(VERTEX);
+    UINT Offset = 0;
+    ID3D11ShaderResourceView *srv = ShaderRes.Get();
+    ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
+
+    context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
+    context_handle->OMSetBlendState(blend, BlendFactor, 0xFFFFFFFF);
+    context_handle->OMSetRenderTargets(1, &rtv, nullptr);
+    context_handle->VSSetShader(vs, nullptr, 0);
+    context_handle->PSSetShader(ps, nullptr, 0);
+    context_handle->PSSetShaderResources(0, 1, &srv);
+    context_handle->PSSetSamplers(0, 1, &sampler);
+    context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    context_handle->IASetInputLayout(layout);
+
+    D3D11_VIEWPORT VP;
+    VP.Width = static_cast<FLOAT>(FullDesc.Width);
+    VP.Height = static_cast<FLOAT>(FullDesc.Height);
+    VP.MinDepth = 0.0f;
+    VP.MaxDepth = 1.0f;
+    VP.TopLeftX = 0.0f;
+    VP.TopLeftY = 0.0f;
+    context_handle->RSSetViewports(1, &VP);
+
+    context_handle->Draw(NUMVERTICES, 0);
+
+    /* Unbind srv and rtv from context */
+    srv = nullptr;
+    context_handle->PSSetShaderResources (0, 1, &srv);
+    context_handle->OMSetRenderTargets (0, nullptr, nullptr);
+
+    if (InitBuffer)
+      delete[] InitBuffer;
+
+    return true;
+  }
+
+  GstFlowReturn
+  CopyToTexture (GstD3D11Device * device, ID3D11Texture2D * texture,
+      D3D11_BOX * cropBox)
+  {
+    ID3D11DeviceContext *context_handle = nullptr;
+    ComPtr <ID3D11Texture2D> tex;
+    HRESULT hr;
+    gboolean is_shared = FALSE;
+
+    context_handle = gst_d3d11_device_get_device_context_handle (device);
+
+    if (device == device_) {
+      tex = shared_texture_;
+    } else {
+      ID3D11Device *device_handle = nullptr;
+      ComPtr < IDXGIResource > dxgi_resource;
+      HANDLE shared_handle;
+
+      device_handle = gst_d3d11_device_get_device_handle (device);
+
+      hr = shared_texture_.As (&dxgi_resource);
+      if (!gst_d3d11_result (hr, device_))
+        return GST_FLOW_ERROR;
+
+      hr = dxgi_resource->GetSharedHandle (&shared_handle);
+      if (!gst_d3d11_result (hr, device_))
+        return GST_FLOW_ERROR;
+
+      hr = device_handle->OpenSharedResource (shared_handle,
+          IID_PPV_ARGS (&tex));
+      if (!gst_d3d11_result (hr, device))
+        return GST_FLOW_ERROR;
+
+      if (fence_ && fence_->device != device)
+        gst_clear_d3d11_fence (&fence_);
+
+      if (!fence_)
+        fence_ = gst_d3d11_device_create_fence (device);
+
+      if (!fence_)
+        return GST_FLOW_ERROR;
+
+      is_shared = TRUE;
+    }
+
+    context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
+        tex.Get(), 0, cropBox);
+
+    if (is_shared) {
+      if (!gst_d3d11_fence_signal (fence_) || !gst_d3d11_fence_wait (fence_))
+        return GST_FLOW_ERROR;
+    }
+
+    return GST_FLOW_OK;
+  }
+
+  void
+  GetSize (guint * width, guint * height)
+  {
+    *width = output_desc_.ModeDesc.Width;
+    *height = output_desc_.ModeDesc.Height;
+  }
+
+private:
+  /* This method is not expected to be failed unless un-recoverable error case */
+  bool
+  InitShader (GstD3D11Device * device)
+  {
+    static const gchar vs_str[] =
+        "struct VS_INPUT {\n"
+        "  float4 Position: POSITION;\n"
+        "  float2 Texture: TEXCOORD;\n"
+        "};\n"
+        "\n"
+        "struct VS_OUTPUT {\n"
+        "  float4 Position: SV_POSITION;\n"
+        "  float2 Texture: TEXCOORD;\n"
+        "};\n"
+        "\n"
+        "VS_OUTPUT main (VS_INPUT input)\n"
+        "{\n"
+        "  return input;\n"
+        "}";
+
+    static const gchar ps_str[] =
+        "Texture2D shaderTexture;\n"
+        "SamplerState samplerState;\n"
+        "\n"
+        "struct PS_INPUT {\n"
+        "  float4 Position: SV_POSITION;\n"
+        "  float2 Texture: TEXCOORD;\n"
+        "};\n"
+        "\n"
+        "struct PS_OUTPUT {\n"
+        "  float4 Plane: SV_Target;\n"
+        "};\n"
+        "\n"
+        "PS_OUTPUT main(PS_INPUT input)\n"
+        "{\n"
+        "  PS_OUTPUT output;\n"
+        "  output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
+        "  return output;\n"
+        "}";
+
+    D3D11_INPUT_ELEMENT_DESC input_desc[] = {
+      {"POSITION",
+          0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
+      {"TEXCOORD",
+          0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
+    };
+
+    ComPtr<ID3D11VertexShader> vs;
+    ComPtr<ID3D11InputLayout> layout;
+    HRESULT hr;
+
+    hr = gst_d3d11_create_vertex_shader_simple (device,
+        vs_str, "main", input_desc, G_N_ELEMENTS (input_desc), &vs, &layout);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create vertex shader");
+      return false;
+    }
+
+    ComPtr<ID3D11PixelShader> ps;
+    hr = gst_d3d11_create_pixel_shader_simple (device, ps_str, "main", &ps);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create pixel shader");
+      return false;
+    }
+
+    D3D11_SAMPLER_DESC sampler_desc;
+    memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
+    sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+    sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+    sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+    sampler_desc.MinLOD = 0;
+    sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
+
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
+    ComPtr<ID3D11SamplerState> sampler;
+    hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Failed to create sampler state, hr 0x%x", (guint) hr);
+      return false;
+    }
+
+    /* Everything is prepared now */
+    vs_ = vs;
+    ps_ = ps;
+    layout_ = layout;
+    sampler_ = sampler;
+
+    return true;
+  }
+
+  /* Maybe returning expected error code depending on desktop status */
+  GstFlowReturn
+  InitDupl (GstD3D11Device * device, HMONITOR monitor)
+  {
+    ComPtr<ID3D11Device> d3d11_device;
+    ComPtr<IDXGIAdapter1> adapter;
+    ComPtr<IDXGIOutput> output;
+    ComPtr<IDXGIOutput1> output1;
+
+    d3d11_device = gst_d3d11_device_get_device_handle (device);
+
+    HRESULT hr = gst_d3d11_screen_capture_find_output_for_monitor (monitor,
+        &adapter, &output);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Couldn't get adapter and output for monitor");
+      return GST_FLOW_ERROR;
+    }
+
+    hr = output.As (&output1);
+    if (!gst_d3d11_result (hr, device)) {
+      GST_ERROR ("Couldn't get IDXGIOutput1 interface, hr 0x%x", (guint) hr);
+      return GST_FLOW_ERROR;
+    }
+
+    HDESK hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
+    if (hdesk) {
+      if (!SetThreadDesktop (hdesk)) {
+        GST_WARNING ("SetThreadDesktop() failed, error %lu", GetLastError());
+      }
+
+      CloseDesktop (hdesk);
+    } else {
+      GST_WARNING ("OpenInputDesktop() failed, error %lu", GetLastError());
+    }
+
+    /* FIXME: Use DuplicateOutput1 to avoid potentail color conversion */
+    hr = output1->DuplicateOutput(d3d11_device.Get(), &dupl_);
+    if (!gst_d3d11_result (hr, device)) {
+      if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
+        GST_ERROR ("Hit the max allowed number of Desktop Duplication session");
+        return GST_FLOW_ERROR;
+      }
+
+      /* Seems to be one limitation of Desktop Duplication API design
+       * See
+       * https://docs.microsoft.com/en-US/troubleshoot/windows-client/shell-experience/error-when-dda-capable-app-is-against-gpu
+       */
+      if (hr == DXGI_ERROR_UNSUPPORTED) {
+        GST_WARNING ("IDXGIOutput1::DuplicateOutput returned "
+            "DXGI_ERROR_UNSUPPORTED, possiblely application is run against a "
+            "discrete GPU");
+        return GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED;
+      }
+
+      return gst_d3d11_dxgi_capture_return_from_hr (d3d11_device.Get(), hr,
+          CreateDuplicationExpectedErrors);
+    }
+
+    dupl_->GetDesc (&output_desc_);
+
+    return GST_FLOW_OK;
+  }
+
+  GstFlowReturn
+  GetMouse (PTR_INFO * ptr_info, DXGI_OUTDUPL_FRAME_INFO * frame_info)
+  {
+    /* A non-zero mouse update timestamp indicates that there is a mouse
+     * position update and optionally a shape change */
+    if (frame_info->LastMouseUpdateTime.QuadPart == 0)
+      return GST_FLOW_OK;
+
+    ptr_info->Position.x = frame_info->PointerPosition.Position.x;
+    ptr_info->Position.y = frame_info->PointerPosition.Position.y;
+    ptr_info->LastTimeStamp = frame_info->LastMouseUpdateTime;
+    ptr_info->Visible = frame_info->PointerPosition.Visible != 0;
+
+    /* No new shape */
+    if (frame_info->PointerShapeBufferSize == 0)
+      return GST_FLOW_OK;
+
+    /* Realloc buffer if needed */
+    ptr_info->MaybeReallocBuffer (frame_info->PointerShapeBufferSize);
+
+    /* Must always get shape of cursor, even if not drawn at the moment.
+     * Shape of cursor is not repeated by the AcquireNextFrame and can be
+     * requested to be drawn any time later */
+    UINT dummy;
+    HRESULT hr = dupl_->GetFramePointerShape(frame_info->PointerShapeBufferSize,
+        (void *) ptr_info->PtrShapeBuffer, &dummy, &ptr_info->shape_info);
+
+    if (!gst_d3d11_result (hr, device_)) {
+      ID3D11Device *device_handle =
+          gst_d3d11_device_get_device_handle (device_);
+
+      return gst_d3d11_dxgi_capture_return_from_hr(device_handle, hr,
+          FrameInfoExpectedErrors);
+    }
+
+    return GST_FLOW_OK;
+  }
+
+  void
+  MaybeReallocMetadataBuffer (UINT buffer_size)
+  {
+    if (buffer_size <= metadata_buffer_size_)
+      return;
+
+    if (metadata_buffer_)
+      delete[] metadata_buffer_;
+
+    metadata_buffer_ = new BYTE[buffer_size];
+    metadata_buffer_size_ = buffer_size;
+  }
+
+  GstFlowReturn
+  GetFrame (ID3D11Texture2D ** texture, UINT * move_count, UINT * dirty_count,
+      DXGI_OUTDUPL_FRAME_INFO * frame_info, bool* timeout)
+  {
+    ComPtr<IDXGIResource> resource;
+    ComPtr<ID3D11Texture2D> acquired_texture;
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
+
+    /* Get new frame */
+    HRESULT hr = dupl_->AcquireNextFrame(0, frame_info, &resource);
+    if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
+      GST_TRACE ("Timeout");
+
+      *timeout = true;
+      return GST_FLOW_OK;
+    }
+
+    *timeout = false;
+    *move_count = 0;
+    *dirty_count = 0;
+
+    if (!gst_d3d11_result (hr, device_)) {
+      return gst_d3d11_dxgi_capture_return_from_hr(device_handle, hr,
+          FrameInfoExpectedErrors);
+    }
+
+    GST_TRACE (
+        "LastPresentTime: %" G_GINT64_FORMAT
+        ", LastMouseUpdateTime: %" G_GINT64_FORMAT
+        ", AccumulatedFrames: %d"
+        ", RectsCoalesced: %d"
+        ", ProtectedContentMaskedOut: %d"
+        ", PointerPosition: (%ldx%ld, visible %d)"
+        ", TotalMetadataBufferSize: %d"
+        ", PointerShapeBufferSize: %d",
+        frame_info->LastPresentTime.QuadPart,
+        frame_info->LastMouseUpdateTime.QuadPart,
+        frame_info->AccumulatedFrames,
+        frame_info->RectsCoalesced,
+        frame_info->ProtectedContentMaskedOut,
+        frame_info->PointerPosition.Position.x,
+        frame_info->PointerPosition.Position.y,
+        frame_info->PointerPosition.Visible,
+        frame_info->TotalMetadataBufferSize,
+        frame_info->PointerShapeBufferSize);
+
+    hr = resource.As (&acquired_texture);
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Failed to get ID3D11Texture2D interface from IDXGIResource "
+          "hr 0x%x", (guint) hr);
+      return GST_FLOW_ERROR;
+    }
+
+    /* Get metadata */
+    if (frame_info->TotalMetadataBufferSize) {
+      UINT buf_size = frame_info->TotalMetadataBufferSize;
+
+      MaybeReallocMetadataBuffer (buf_size);
+
+      /* Get move rectangles */
+      hr = dupl_->GetFrameMoveRects(buf_size,
+          (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_, &buf_size);
+      if (!gst_d3d11_result (hr, device_)) {
+        GST_ERROR ("Couldn't get move rect, hr 0x%x", (guint) hr);
+
+        return gst_d3d11_dxgi_capture_return_from_hr(nullptr, hr,
+            FrameInfoExpectedErrors);
+      }
+
+      *move_count = buf_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
+
+      GST_TRACE ("MoveRects count %d", *move_count);
+#ifndef GST_DISABLE_GST_DEBUG
+      {
+        DXGI_OUTDUPL_MOVE_RECT *rects =
+            (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_;
+        for (guint i = 0; i < *move_count; i++) {
+          GST_TRACE ("MoveRect[%d] SourcePoint: %ldx%ld, "
+            "DestinationRect (left:top:right:bottom): %ldx%ldx%ldx%ld",
+            i, rects->SourcePoint.x, rects->SourcePoint.y,
+            rects->DestinationRect.left, rects->DestinationRect.top,
+            rects->DestinationRect.right, rects->DestinationRect.bottom);
+        }
+      }
+#endif
+
+      BYTE* dirty_rects = metadata_buffer_ + buf_size;
+      buf_size = frame_info->TotalMetadataBufferSize - buf_size;
+
+      /* Get dirty rectangles */
+      hr = dupl_->GetFrameDirtyRects(buf_size, (RECT *) dirty_rects, &buf_size);
+      if (!gst_d3d11_result (hr, device_)) {
+        GST_ERROR ("Couldn't get dirty rect, hr 0x%x", (guint) hr);
+        *move_count = 0;
+        *dirty_count = 0;
+
+        return gst_d3d11_dxgi_capture_return_from_hr(nullptr,
+            hr, FrameInfoExpectedErrors);
+      }
+
+      *dirty_count = buf_size / sizeof(RECT);
+
+      GST_TRACE ("DirtyRects count %d", *dirty_count);
+#ifndef GST_DISABLE_GST_DEBUG
+      {
+        RECT *rects = (RECT *) dirty_rects;
+        for (guint i = 0; i < *dirty_count; i++) {
+          GST_TRACE ("DirtyRect[%d] left:top:right:bottom: %ldx%ldx%ldx%ld",
+            i, rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
+        }
+      }
+#endif
+    }
+
+    *texture = acquired_texture.Detach();
+
+    return GST_FLOW_OK;
+  }
+
+  void
+  SetMoveRect (RECT* SrcRect, RECT* DestRect, DXGI_OUTDUPL_DESC* DeskDesc,
+      DXGI_OUTDUPL_MOVE_RECT* MoveRect, INT TexWidth, INT TexHeight)
+  {
+    switch (DeskDesc->Rotation)
+    {
+      case DXGI_MODE_ROTATION_UNSPECIFIED:
+      case DXGI_MODE_ROTATION_IDENTITY:
+        SrcRect->left = MoveRect->SourcePoint.x;
+        SrcRect->top = MoveRect->SourcePoint.y;
+        SrcRect->right = MoveRect->SourcePoint.x +
+            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
+        SrcRect->bottom = MoveRect->SourcePoint.y +
+            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
+
+        *DestRect = MoveRect->DestinationRect;
+        break;
+      case DXGI_MODE_ROTATION_ROTATE90:
+        SrcRect->left = TexHeight - (MoveRect->SourcePoint.y +
+            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
+        SrcRect->top = MoveRect->SourcePoint.x;
+        SrcRect->right = TexHeight - MoveRect->SourcePoint.y;
+        SrcRect->bottom = MoveRect->SourcePoint.x +
+            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
+
+        DestRect->left = TexHeight - MoveRect->DestinationRect.bottom;
+        DestRect->top = MoveRect->DestinationRect.left;
+        DestRect->right = TexHeight - MoveRect->DestinationRect.top;
+        DestRect->bottom = MoveRect->DestinationRect.right;
+        break;
+      case DXGI_MODE_ROTATION_ROTATE180:
+        SrcRect->left = TexWidth - (MoveRect->SourcePoint.x +
+            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
+        SrcRect->top = TexHeight - (MoveRect->SourcePoint.y +
+            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
+        SrcRect->right = TexWidth - MoveRect->SourcePoint.x;
+        SrcRect->bottom = TexHeight - MoveRect->SourcePoint.y;
+
+        DestRect->left = TexWidth - MoveRect->DestinationRect.right;
+        DestRect->top = TexHeight - MoveRect->DestinationRect.bottom;
+        DestRect->right = TexWidth - MoveRect->DestinationRect.left;
+        DestRect->bottom =  TexHeight - MoveRect->DestinationRect.top;
+        break;
+      case DXGI_MODE_ROTATION_ROTATE270:
+        SrcRect->left = MoveRect->SourcePoint.x;
+        SrcRect->top = TexWidth - (MoveRect->SourcePoint.x +
+            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
+        SrcRect->right = MoveRect->SourcePoint.y +
+            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
+        SrcRect->bottom = TexWidth - MoveRect->SourcePoint.x;
+
+        DestRect->left = MoveRect->DestinationRect.top;
+        DestRect->top = TexWidth - MoveRect->DestinationRect.right;
+        DestRect->right = MoveRect->DestinationRect.bottom;
+        DestRect->bottom =  TexWidth - MoveRect->DestinationRect.left;
+        break;
+      default:
+        memset (DestRect, 0, sizeof (RECT));
+        memset (SrcRect, 0, sizeof (RECT));
+        break;
+    }
+  }
+
+  GstFlowReturn
+  CopyMove (ID3D11Texture2D* SharedSurf, DXGI_OUTDUPL_MOVE_RECT* MoveBuffer,
+      UINT MoveCount, DXGI_OUTDUPL_DESC* DeskDesc)
+  {
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
+    ID3D11DeviceContext *device_context =
+       gst_d3d11_device_get_device_context_handle (device_);
+    D3D11_TEXTURE2D_DESC FullDesc;
+    SharedSurf->GetDesc(&FullDesc);
+
+    GST_TRACE ("Copying MoveRects (count %d)", MoveCount);
+
+    /* Make new intermediate surface to copy into for moving */
+    if (!move_texture_) {
+      D3D11_TEXTURE2D_DESC MoveDesc;
+      MoveDesc = FullDesc;
+      MoveDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
+      MoveDesc.MiscFlags = 0;
+      HRESULT hr = device_handle->CreateTexture2D(&MoveDesc,
+          nullptr, &move_texture_);
+      if (!gst_d3d11_result (hr, device_)) {
+        GST_ERROR ("Couldn't create intermediate texture, hr 0x%x", (guint) hr);
+        return GST_FLOW_ERROR;
+      }
+    }
+
+    for (UINT i = 0; i < MoveCount; i++) {
+      RECT SrcRect;
+      RECT DestRect;
+
+      SetMoveRect(&SrcRect, &DestRect, DeskDesc, &MoveBuffer[i],
+          FullDesc.Width, FullDesc.Height);
+
+      /* Copy rect out of shared surface */
+      D3D11_BOX Box;
+      Box.left = SrcRect.left;
+      Box.top = SrcRect.top;
+      Box.front = 0;
+      Box.right = SrcRect.right;
+      Box.bottom = SrcRect.bottom;
+      Box.back = 1;
+      device_context->CopySubresourceRegion(move_texture_.Get(),
+          0, SrcRect.left, SrcRect.top, 0, SharedSurf, 0, &Box);
+
+      /* Copy back to shared surface */
+      device_context->CopySubresourceRegion(SharedSurf,
+          0, DestRect.left, DestRect.top, 0, move_texture_.Get(), 0, &Box);
+    }
+
+    return GST_FLOW_OK;
+  }
+
+  void
+  SetDirtyVert (VERTEX* Vertices, RECT* Dirty,
+      DXGI_OUTDUPL_DESC* DeskDesc, D3D11_TEXTURE2D_DESC* FullDesc,
+      D3D11_TEXTURE2D_DESC* ThisDesc)
+  {
+    INT CenterX = FullDesc->Width / 2;
+    INT CenterY = FullDesc->Height / 2;
+
+    INT Width = FullDesc->Width;
+    INT Height = FullDesc->Height;
+
+    /* Rotation compensated destination rect */
+    RECT DestDirty = *Dirty;
+
+    /* Set appropriate coordinates compensated for rotation */
+    switch (DeskDesc->Rotation)
+    {
+      case DXGI_MODE_ROTATION_ROTATE90:
+        DestDirty.left = Width - Dirty->bottom;
+        DestDirty.top = Dirty->left;
+        DestDirty.right = Width - Dirty->top;
+        DestDirty.bottom = Dirty->right;
+
+        Vertices[0].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[1].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[2].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[5].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        break;
+      case DXGI_MODE_ROTATION_ROTATE180:
+        DestDirty.left = Width - Dirty->right;
+        DestDirty.top = Height - Dirty->bottom;
+        DestDirty.right = Width - Dirty->left;
+        DestDirty.bottom = Height - Dirty->top;
+
+        Vertices[0].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[1].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[2].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[5].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        break;
+      case DXGI_MODE_ROTATION_ROTATE270:
+        DestDirty.left = Dirty->top;
+        DestDirty.top = Height - Dirty->right;
+        DestDirty.right = Dirty->bottom;
+        DestDirty.bottom = Height - Dirty->left;
+
+        Vertices[0].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[1].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[2].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[5].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        break;
+      case DXGI_MODE_ROTATION_UNSPECIFIED:
+      case DXGI_MODE_ROTATION_IDENTITY:
+      default:
+        Vertices[0].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[1].TexCoord =
+            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[2].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
+        Vertices[5].TexCoord =
+            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
+                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
+        break;
+    }
+
+    /* Set positions */
+    Vertices[0].Pos =
+        MyFLOAT3(
+          (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
+          -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
+          0.0f);
+    Vertices[1].Pos =
+        MyFLOAT3(
+          (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
+          -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
+          0.0f);
+    Vertices[2].Pos =
+        MyFLOAT3(
+          (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
+          -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
+          0.0f);
+    Vertices[3].Pos = Vertices[2].Pos;
+    Vertices[4].Pos = Vertices[1].Pos;
+    Vertices[5].Pos =
+        MyFLOAT3(
+          (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
+          -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
+          0.0f);
+
+    Vertices[3].TexCoord = Vertices[2].TexCoord;
+    Vertices[4].TexCoord = Vertices[1].TexCoord;
+  }
+
+  void
+  MaybeReallocVertexBuffer (UINT buffer_size)
+  {
+    if (buffer_size <= vertex_buffer_size_)
+      return;
+
+    if (vertex_buffer_)
+      delete[] vertex_buffer_;
+
+    vertex_buffer_ = new BYTE[buffer_size];
+    vertex_buffer_size_ = buffer_size;
+  }
+
+  GstFlowReturn
+  CopyDirty (ID3D11Texture2D* SrcSurface, ID3D11Texture2D* SharedSurf,
+      RECT* DirtyBuffer, UINT DirtyCount, DXGI_OUTDUPL_DESC* DeskDesc)
+  {
+    D3D11_TEXTURE2D_DESC FullDesc;
+    D3D11_TEXTURE2D_DESC ThisDesc;
+    ComPtr<ID3D11ShaderResourceView> ShaderResource;
+    ComPtr<ID3D11Buffer> VertBuf;
+    HRESULT hr;
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
+    ID3D11DeviceContext *device_context =
+       gst_d3d11_device_get_device_context_handle (device_);
+
+    GST_TRACE ("Copying DirtyRects (count %d)", DirtyCount);
+
+    SharedSurf->GetDesc(&FullDesc);
+    SrcSurface->GetDesc(&ThisDesc);
+
+    if (!rtv_) {
+      hr = device_handle->CreateRenderTargetView(SharedSurf, nullptr, &rtv_);
+      if (!gst_d3d11_result (hr, device_)) {
+        GST_ERROR ("Couldn't create render target view, hr 0x%x", (guint) hr);
+        return GST_FLOW_ERROR;
+      }
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
+    ShaderDesc.Format = ThisDesc.Format;
+    ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    ShaderDesc.Texture2D.MostDetailedMip = ThisDesc.MipLevels - 1;
+    ShaderDesc.Texture2D.MipLevels = ThisDesc.MipLevels;
+
+    /* Create new shader resource view */
+    hr = device_handle->CreateShaderResourceView(SrcSurface,
+        &ShaderDesc, &ShaderResource);
+    if (!gst_d3d11_result (hr, device_)) {
+      return gst_d3d11_dxgi_capture_return_from_hr(device_handle, hr,
+          SystemTransitionsExpectedErrors);
+    }
+
+    ID3D11SamplerState *samplers = sampler_.Get();
+    ID3D11ShaderResourceView *srv = ShaderResource.Get();
+    ID3D11RenderTargetView *rtv = rtv_.Get();
+    device_context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
+    device_context->OMSetRenderTargets(1, &rtv, nullptr);
+    device_context->VSSetShader(vs_.Get(), nullptr, 0);
+    device_context->PSSetShader(ps_.Get(), nullptr, 0);
+    device_context->PSSetShaderResources(0, 1, &srv);
+    device_context->PSSetSamplers(0, 1, &samplers);
+    device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+    device_context->IASetInputLayout(layout_.Get());
+
+    /* Create space for vertices for the dirty rects if the current space isn't
+     * large enough */
+    UINT byte_needed = sizeof(VERTEX) * NUMVERTICES * DirtyCount;
+    MaybeReallocVertexBuffer (byte_needed);
+
+    /* Fill them in */
+    VERTEX* DirtyVertex = (VERTEX *) vertex_buffer_;
+    for (UINT i = 0; i < DirtyCount; ++i, DirtyVertex += NUMVERTICES) {
+      SetDirtyVert(DirtyVertex, &DirtyBuffer[i], DeskDesc,
+          &FullDesc, &ThisDesc);
+    }
+
+    /* Create vertex buffer */
+    D3D11_BUFFER_DESC BufferDesc;
+    memset (&BufferDesc, 0, sizeof (D3D11_BUFFER_DESC));
+    BufferDesc.Usage = D3D11_USAGE_DEFAULT;
+    BufferDesc.ByteWidth = byte_needed;
+    BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    BufferDesc.CPUAccessFlags = 0;
+    D3D11_SUBRESOURCE_DATA InitData;
+    memset (&InitData, 0, sizeof (D3D11_SUBRESOURCE_DATA));
+    InitData.pSysMem = vertex_buffer_;
+
+    hr = device_handle->CreateBuffer(&BufferDesc, &InitData, &VertBuf);
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Failed to create vertex buffer");
+      return GST_FLOW_ERROR;
+    }
+
+    UINT Stride = sizeof(VERTEX);
+    UINT Offset = 0;
+    ID3D11Buffer *vert_buf = VertBuf.Get();
+    device_context->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
+
+    D3D11_VIEWPORT VP;
+    VP.Width = static_cast<FLOAT>(FullDesc.Width);
+    VP.Height = static_cast<FLOAT>(FullDesc.Height);
+    VP.MinDepth = 0.0f;
+    VP.MaxDepth = 1.0f;
+    VP.TopLeftX = 0.0f;
+    VP.TopLeftY = 0.0f;
+    device_context->RSSetViewports(1, &VP);
+
+    device_context->Draw(NUMVERTICES * DirtyCount, 0);
+
+    /* Unbind srv and rtv from context */
+    srv = nullptr;
+    device_context->PSSetShaderResources (0, 1, &srv);
+    device_context->OMSetRenderTargets (0, nullptr, nullptr);
+
+    return GST_FLOW_OK;
+  }
+
+  GstFlowReturn
+  ProcessFrame(ID3D11Texture2D * acquired_texture, ID3D11Texture2D* SharedSurf,
+      DXGI_OUTDUPL_DESC* DeskDesc, UINT move_count, UINT dirty_count,
+      DXGI_OUTDUPL_FRAME_INFO * frame_info)
+  {
+    GstFlowReturn ret = GST_FLOW_OK;
+
+    GST_TRACE ("Processing frame");
+
+    /* Process dirties and moves */
+    if (frame_info->TotalMetadataBufferSize) {
+      if (move_count) {
+        ret = CopyMove(SharedSurf, (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_,
+            move_count, DeskDesc);
+
+        if (ret != GST_FLOW_OK)
+          return ret;
+      }
+
+      if (dirty_count) {
+        ret = CopyDirty(acquired_texture, SharedSurf,
+            (RECT *)(metadata_buffer_ +
+                (move_count * sizeof(DXGI_OUTDUPL_MOVE_RECT))),
+            dirty_count, DeskDesc);
+      }
+    } else {
+      GST_TRACE ("No metadata");
+    }
+
+    return ret;
+  }
+
+  /* To draw mouse */
+  bool
+  ProcessMonoMask (bool IsMono, PTR_INFO* PtrInfo, INT* PtrWidth,
+      INT* PtrHeight, INT* PtrLeft, INT* PtrTop, BYTE** InitBuffer,
+      D3D11_BOX* Box)
+  {
+    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
+    ID3D11DeviceContext *context_handle =
+        gst_d3d11_device_get_device_context_handle (device_);
+
+    D3D11_TEXTURE2D_DESC FullDesc;
+    shared_texture_->GetDesc(&FullDesc);
+    INT DesktopWidth = FullDesc.Width;
+    INT DesktopHeight = FullDesc.Height;
+
+    // Pointer position
+    INT GivenLeft = PtrInfo->Position.x;
+    INT GivenTop = PtrInfo->Position.y;
+
+    // Figure out if any adjustment is needed for out of bound positions
+    if (GivenLeft < 0) {
+      *PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width);
+    } else if ((GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width)) > DesktopWidth) {
+      *PtrWidth = DesktopWidth - GivenLeft;
+    } else {
+      *PtrWidth = static_cast<INT>(PtrInfo->shape_info.Width);
+    }
+
+    if (IsMono)
+      PtrInfo->shape_info.Height = PtrInfo->shape_info.Height / 2;
+
+    if (GivenTop < 0) {
+      *PtrHeight = GivenTop + static_cast<INT>(PtrInfo->shape_info.Height);
+    } else if ((GivenTop + static_cast<INT>(PtrInfo->shape_info.Height)) > DesktopHeight) {
+      *PtrHeight = DesktopHeight - GivenTop;
+    } else {
+      *PtrHeight = static_cast<INT>(PtrInfo->shape_info.Height);
+    }
+
+    if (IsMono)
+      PtrInfo->shape_info.Height = PtrInfo->shape_info.Height * 2;
+
+    *PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft;
+    *PtrTop = (GivenTop < 0) ? 0 : GivenTop;
+
+    D3D11_TEXTURE2D_DESC CopyBufferDesc;
+    CopyBufferDesc.Width = *PtrWidth;
+    CopyBufferDesc.Height = *PtrHeight;
+    CopyBufferDesc.MipLevels = 1;
+    CopyBufferDesc.ArraySize = 1;
+    CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    CopyBufferDesc.SampleDesc.Count = 1;
+    CopyBufferDesc.SampleDesc.Quality = 0;
+    CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
+    CopyBufferDesc.BindFlags = 0;
+    CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+    CopyBufferDesc.MiscFlags = 0;
+
+    ComPtr<ID3D11Texture2D> CopyBuffer;
+    HRESULT hr = device_handle->CreateTexture2D(&CopyBufferDesc,
+        nullptr, &CopyBuffer);
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Couldn't create texture for mouse pointer");
+      return false;
+    }
+
+    Box->left = *PtrLeft;
+    Box->top = *PtrTop;
+    Box->right = *PtrLeft + *PtrWidth;
+    Box->bottom = *PtrTop + *PtrHeight;
+    context_handle->CopySubresourceRegion(CopyBuffer.Get(),
+        0, 0, 0, 0, shared_texture_.Get(), 0, Box);
+
+    ComPtr<IDXGISurface> CopySurface;
+    hr = CopyBuffer.As (&CopySurface);
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Couldn't get DXGI resource from mouse texture");
+      return false;
+    }
+
+    DXGI_MAPPED_RECT MappedSurface;
+    hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Couldn't map DXGI surface");
+      return false;
+    }
+
+    *InitBuffer = new BYTE[*PtrWidth * *PtrHeight * BPP];
+
+    UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer);
+    UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits);
+    UINT  DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT);
+
+    // What to skip (pixel offset)
+    UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0);
+    UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0);
+
+    if (IsMono) {
+      for (INT Row = 0; Row < *PtrHeight; Row++) {
+        BYTE Mask = 0x80;
+        Mask = Mask >> (SkipX % 8);
+        for (INT Col = 0; Col < *PtrWidth; Col++) {
+          BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
+              ((Row + SkipY) * (PtrInfo->shape_info.Pitch))] & Mask;
+          BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
+              ((Row + SkipY + (PtrInfo->shape_info.Height / 2)) *
+                  (PtrInfo->shape_info.Pitch))] & Mask;
+          UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
+          UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
+
+          InitBuffer32[(Row * *PtrWidth) + Col] =
+              (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32;
+
+          if (Mask == 0x01) {
+              Mask = 0x80;
+          } else {
+              Mask = Mask >> 1;
+          }
+        }
+      }
+    } else {
+      UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
+
+      for (INT Row = 0; Row < *PtrHeight; Row++) {
+        for (INT Col = 0; Col < *PtrWidth; ++Col) {
+          // Set up mask
+          UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) *
+              (PtrInfo->shape_info.Pitch / sizeof(UINT)))];
+          if (MaskVal) {
+            // Mask was 0xFF
+            InitBuffer32[(Row * *PtrWidth) + Col] =
+                (Desktop32[(Row * DesktopPitchInPixels) + Col] ^
+                    Buffer32[(Col + SkipX) + ((Row + SkipY) *
+                    (PtrInfo->shape_info.Pitch / sizeof(UINT)))]) | 0xFF000000;
+          } else {
+            // Mask was 0x00
+            InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) +
+                ((Row + SkipY) * (PtrInfo->shape_info.Pitch / sizeof(UINT)))] | 0xFF000000;
+          }
+        }
+      }
+    }
+
+    // Done with resource
+    hr = CopySurface->Unmap();
+    if (!gst_d3d11_result (hr, device_)) {
+      GST_ERROR ("Failed to unmap DXGI surface");
+      return false;
+    }
+
+    return true;
+  }
+
+private:
+  PTR_INFO ptr_info_;
+  DXGI_OUTDUPL_DESC output_desc_;
+  GstD3D11Device * device_;
+  GstD3D11Fence * fence_;
+
+  ComPtr<ID3D11Texture2D> shared_texture_;
+  ComPtr<ID3D11RenderTargetView> rtv_;
+  ComPtr<ID3D11Texture2D> move_texture_;
+  ComPtr<ID3D11VertexShader> vs_;
+  ComPtr<ID3D11PixelShader> ps_;
+  ComPtr<ID3D11InputLayout> layout_;
+  ComPtr<ID3D11SamplerState> sampler_;
+  ComPtr<IDXGIOutputDuplication> dupl_;
+
+  /* frame metadata */
+  BYTE *metadata_buffer_;
+  UINT metadata_buffer_size_;
+
+  /* vertex buffers */
+  BYTE *vertex_buffer_;
+  UINT vertex_buffer_size_;
+};
+/* *INDENT-ON* */
+
+enum
+{
+  PROP_0,
+  PROP_D3D11_DEVICE,
+  PROP_MONITOR_HANDLE,
+};
+
+struct _GstD3D11DxgiCapture
+{
+  GstD3D11ScreenCapture parent;
+
+  GstD3D11Device *device;
+  guint cached_width;
+  guint cached_height;
+
+  D3D11DesktopDupObject *dupl_obj;
+  IDXGIOutput *output;
+
+  HMONITOR monitor_handle;
+  RECT desktop_coordinates;
+  gboolean prepared;
+  gint64 adapter_luid;
+
+  CRITICAL_SECTION lock;
+};
+
+static void gst_d3d11_dxgi_capture_constructed (GObject * object);
+static void gst_d3d11_dxgi_capture_dispose (GObject * object);
+static void gst_d3d11_dxgi_capture_finalize (GObject * object);
+static void gst_d3d11_dxgi_capture_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static GstFlowReturn
+gst_d3d11_dxgi_capture_prepare (GstD3D11ScreenCapture * capture);
+static gboolean
+gst_d3d11_dxgi_capture_get_size (GstD3D11ScreenCapture * capture,
+    guint * width, guint * height);
+static gboolean
+gst_d3d11_dxgi_capture_get_colorimetry (GstD3D11ScreenCapture * capture,
+    GstVideoColorimetry * colorimetry);
+static GstFlowReturn
+gst_d3d11_dxgi_capture_do_capture (GstD3D11ScreenCapture * capture,
+    GstD3D11Device * device, ID3D11Texture2D * texture,
+    ID3D11RenderTargetView * rtv, ID3D11VertexShader * vs,
+    ID3D11PixelShader * ps, ID3D11InputLayout * layout,
+    ID3D11SamplerState * sampler, ID3D11BlendState * blend,
+    D3D11_BOX * crop_box, gboolean draw_mouse);
+
+#define gst_d3d11_dxgi_capture_parent_class parent_class
+G_DEFINE_TYPE (GstD3D11DxgiCapture, gst_d3d11_dxgi_capture,
+    GST_TYPE_D3D11_SCREEN_CAPTURE);
+
+static void
+gst_d3d11_dxgi_capture_class_init (GstD3D11DxgiCaptureClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstD3D11ScreenCaptureClass *capture_class =
+      GST_D3D11_SCREEN_CAPTURE_CLASS (klass);
+
+  gobject_class->constructed = gst_d3d11_dxgi_capture_constructed;
+  gobject_class->dispose = gst_d3d11_dxgi_capture_dispose;
+  gobject_class->finalize = gst_d3d11_dxgi_capture_finalize;
+  gobject_class->set_property = gst_d3d11_dxgi_capture_set_property;
+
+  g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
+      g_param_spec_object ("d3d11device", "D3D11 Device",
+          "GstD3D11Device object for operating",
+          GST_TYPE_D3D11_DEVICE, (GParamFlags)
+          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+              G_PARAM_STATIC_STRINGS)));
+  g_object_class_install_property (gobject_class, PROP_MONITOR_HANDLE,
+      g_param_spec_pointer ("monitor-handle", "Monitor Handle",
+          "A HMONITOR handle of monitor to capture", (GParamFlags)
+          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+              G_PARAM_STATIC_STRINGS)));
+
+  capture_class->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_dxgi_capture_prepare);
+  capture_class->get_size = GST_DEBUG_FUNCPTR (gst_d3d11_dxgi_capture_get_size);
+  capture_class->get_colorimetry =
+      GST_DEBUG_FUNCPTR (gst_d3d11_dxgi_capture_get_colorimetry);
+  capture_class->do_capture =
+      GST_DEBUG_FUNCPTR (gst_d3d11_dxgi_capture_do_capture);
+}
+
+static void
+gst_d3d11_dxgi_capture_init (GstD3D11DxgiCapture * self)
+{
+  InitializeCriticalSection (&self->lock);
+
+  memset (&self->desktop_coordinates, 0, sizeof (RECT));
+}
+
+static void
+gst_d3d11_dxgi_capture_constructed (GObject * object)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (object);
+  /* *INDENT-OFF* */
+  ComPtr<IDXGIDevice> dxgi_device;
+  ComPtr<IDXGIAdapter1> adapter;
+  ComPtr<IDXGIOutput> output;
+  ComPtr<IDXGIOutput1> output1;
+  /* *INDENT-ON* */
+  HRESULT hr;
+  gboolean ret = FALSE;
+  DXGI_OUTPUT_DESC output_desc;
+  DXGI_ADAPTER_DESC adapter_desc;
+  gint64 luid, device_luid;
+
+  if (!self->device) {
+    GST_WARNING_OBJECT (self, "D3D11 device is unavailable");
+    goto out;
+  }
+
+  if (!self->monitor_handle) {
+    GST_WARNING_OBJECT (self, "Null monitor handle");
+    goto out;
+  }
+
+  hr = gst_d3d11_screen_capture_find_output_for_monitor (self->monitor_handle,
+      &adapter, &output);
+  if (!gst_d3d11_result (hr, self->device)) {
+    GST_WARNING_OBJECT (self,
+        "Failed to find associated adapter for monitor %p",
+        self->monitor_handle);
+    goto out;
+  }
+
+  hr = output.As (&output1);
+  if (!gst_d3d11_result (hr, self->device)) {
+    GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
+    goto out;
+  }
+
+  hr = adapter->GetDesc (&adapter_desc);
+  if (!gst_d3d11_result (hr, self->device)) {
+    GST_WARNING_OBJECT (self, "Failed to get adapter desc");
+    goto out;
+  }
+
+  luid = gst_d3d11_luid_to_int64 (&adapter_desc.AdapterLuid);
+  g_object_get (self->device, "adapter-luid", &device_luid, nullptr);
+  if (luid != device_luid) {
+    GST_WARNING_OBJECT (self, "Incompatible d3d11 device");
+    goto out;
+  }
+
+  hr = output->GetDesc (&output_desc);
+  if (!gst_d3d11_result (hr, self->device)) {
+    GST_WARNING_OBJECT (self, "Failed to get output desc");
+    goto out;
+  }
+
+  /* DesktopCoordinates will not report actual texture size in case that
+   * application is running without dpi-awareness. To get actual monitor size,
+   * we need to use Win32 API... */
+  MONITORINFOEXW monitor_info;
+  DEVMODEW dev_mode;
+
+  monitor_info.cbSize = sizeof (MONITORINFOEXW);
+  if (!GetMonitorInfoW (output_desc.Monitor, (LPMONITORINFO) & monitor_info)) {
+    GST_WARNING_OBJECT (self, "Couldn't get monitor info");
+    goto out;
+  }
+
+  dev_mode.dmSize = sizeof (DEVMODEW);
+  dev_mode.dmDriverExtra = sizeof (POINTL);
+  dev_mode.dmFields = DM_POSITION;
+  if (!EnumDisplaySettingsW
+      (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
+    GST_WARNING_OBJECT (self, "Couldn't enumerate display settings");
+    goto out;
+  }
+
+  self->desktop_coordinates.left = dev_mode.dmPosition.x;
+  self->desktop_coordinates.top = dev_mode.dmPosition.y;
+  self->desktop_coordinates.right =
+      dev_mode.dmPosition.x + dev_mode.dmPelsWidth;
+  self->desktop_coordinates.bottom =
+      dev_mode.dmPosition.y + dev_mode.dmPelsHeight;
+
+  self->cached_width =
+      self->desktop_coordinates.right - self->desktop_coordinates.left;
+  self->cached_height =
+      self->desktop_coordinates.bottom - self->desktop_coordinates.top;
+
+  GST_DEBUG_OBJECT (self,
+      "Desktop coordinates left:top:right:bottom = %ld:%ld:%ld:%ld (%dx%d)",
+      self->desktop_coordinates.left, self->desktop_coordinates.top,
+      self->desktop_coordinates.right, self->desktop_coordinates.bottom,
+      self->cached_width, self->cached_height);
+
+  g_object_get (self->device, "adapter-luid", &self->adapter_luid, nullptr);
+
+  self->output = output.Detach ();
+
+  ret = TRUE;
+
+out:
+  if (!ret)
+    gst_clear_object (&self->device);
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static void
+gst_d3d11_dxgi_capture_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (object);
+
+  switch (prop_id) {
+    case PROP_D3D11_DEVICE:
+      self->device = (GstD3D11Device *) g_value_dup_object (value);
+      break;
+    case PROP_MONITOR_HANDLE:
+      self->monitor_handle = (HMONITOR) g_value_get_pointer (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_d3d11_dxgi_capture_dispose (GObject * object)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (object);
+
+  GST_D3D11_CLEAR_COM (self->output);
+
+  if (self->dupl_obj) {
+    delete self->dupl_obj;
+    self->dupl_obj = nullptr;
+  }
+
+  gst_clear_object (&self->device);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_d3d11_dxgi_capture_finalize (GObject * object)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (object);
+
+  DeleteCriticalSection (&self->lock);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_d3d11_dxgi_capture_weak_ref_notify (gpointer data,
+    GstD3D11DxgiCapture * dupl)
+{
+  G_LOCK (dupl_list_lock);
+  dupl_list = g_list_remove (dupl_list, dupl);
+  G_UNLOCK (dupl_list_lock);
+}
+
+GstD3D11ScreenCapture *
+gst_d3d11_dxgi_capture_new (GstD3D11Device * device, HMONITOR monitor_handle)
+{
+  GstD3D11DxgiCapture *self = nullptr;
+  GList *iter;
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
+
+  /* Check if we have dup object corresponding to monitor_handle,
+   * and if there is already configured capture object, reuse it.
+   * This is because of the limitation of desktop duplication API
+   * (i.e., in a process, only one duplication object can exist).
+   * See also
+   * https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutput1-duplicateoutput#remarks
+   */
+  G_LOCK (dupl_list_lock);
+  for (iter = dupl_list; iter; iter = g_list_next (iter)) {
+    GstD3D11DxgiCapture *dupl = (GstD3D11DxgiCapture *) iter->data;
+
+    if (dupl->monitor_handle == monitor_handle) {
+      GST_DEBUG ("Found configured desktop dup object for monitor handle %p",
+          monitor_handle);
+      self = (GstD3D11DxgiCapture *) gst_object_ref (dupl);
+      break;
+    }
+  }
+
+  if (self) {
+    G_UNLOCK (dupl_list_lock);
+    return GST_D3D11_SCREEN_CAPTURE_CAST (self);
+  }
+
+  self = (GstD3D11DxgiCapture *) g_object_new (GST_TYPE_D3D11_DXGI_CAPTURE,
+      "d3d11device", device, "monitor-handle", monitor_handle, nullptr);
+
+  if (!self->device) {
+    GST_WARNING_OBJECT (self, "Couldn't configure desktop dup object");
+    gst_object_unref (self);
+    G_UNLOCK (dupl_list_lock);
+
+    return nullptr;
+  }
+
+  g_object_weak_ref (G_OBJECT (self),
+      (GWeakNotify) gst_d3d11_dxgi_capture_weak_ref_notify, nullptr);
+  dupl_list = g_list_append (dupl_list, self);
+
+  G_UNLOCK (dupl_list_lock);
+
+  return GST_D3D11_SCREEN_CAPTURE_CAST (self);
+}
+
+static GstFlowReturn
+gst_d3d11_dxgi_capture_prepare (GstD3D11ScreenCapture * capture)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (capture);
+  GstFlowReturn ret;
+
+  GstD3D11CSLockGuard lk (&self->lock);
+
+  if (self->prepared) {
+    GST_DEBUG_OBJECT (self, "Already prepared");
+    return GST_FLOW_OK;
+  }
+
+  self->dupl_obj = new D3D11DesktopDupObject ();
+  ret = self->dupl_obj->Init (self->device, self->monitor_handle);
+  if (ret != GST_FLOW_OK) {
+    GST_WARNING_OBJECT (capture,
+        "Couldn't prepare capturing, %sexpected failure",
+        ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR ? "" : "un");
+
+    delete self->dupl_obj;
+    self->dupl_obj = nullptr;
+
+    return ret;
+  }
+
+  self->prepared = TRUE;
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_d3d11_dxgi_capture_get_size_unlocked (GstD3D11DxgiCapture * self,
+    guint * width, guint * height)
+{
+  *width = 0;
+  *height = 0;
+
+  if (self->dupl_obj) {
+    self->dupl_obj->GetSize (&self->cached_width, &self->cached_height);
+  }
+
+  *width = self->cached_width;
+  *height = self->cached_height;
+
+  return TRUE;
+}
+
+
+static gboolean
+gst_d3d11_dxgi_capture_get_size (GstD3D11ScreenCapture * capture,
+    guint * width, guint * height)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (capture);
+  GstD3D11CSLockGuard lk (&self->lock);
+
+  return gst_d3d11_dxgi_capture_get_size_unlocked (self, width, height);
+}
+
+static gboolean
+gst_d3d11_dxgi_capture_get_colorimetry (GstD3D11ScreenCapture * capture,
+    GstVideoColorimetry * colorimetry)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (capture);
+  DXGI_COLOR_SPACE_TYPE dxgi_cs;
+  GstVideoInfo info;
+
+  dxgi_cs = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
+
+  if (self->output) {
+    ComPtr < IDXGIOutput6 > output;
+    HRESULT hr;
+    DXGI_OUTPUT_DESC1 desc;
+
+    hr = self->output->QueryInterface (IID_PPV_ARGS (&output));
+    if (SUCCEEDED (hr))
+      hr = output->GetDesc1 (&desc);
+
+    if (SUCCEEDED (hr))
+      dxgi_cs = desc.ColorSpace;
+  }
+
+  gst_video_info_set_format (&info, GST_VIDEO_FORMAT_BGRA, 16, 16);
+  if (gst_video_info_apply_dxgi_color_space (dxgi_cs, &info)) {
+    *colorimetry = info.colorimetry;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static GstFlowReturn
+gst_d3d11_dxgi_capture_do_capture (GstD3D11ScreenCapture * capture,
+    GstD3D11Device * device, ID3D11Texture2D * texture,
+    ID3D11RenderTargetView * rtv, ID3D11VertexShader * vs,
+    ID3D11PixelShader * ps, ID3D11InputLayout * layout,
+    ID3D11SamplerState * sampler, ID3D11BlendState * blend,
+    D3D11_BOX * crop_box, gboolean draw_mouse)
+{
+  GstD3D11DxgiCapture *self = GST_D3D11_DXGI_CAPTURE (capture);
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean shared_device = FALSE;
+  guint width, height;
+
+  if (device != self->device) {
+    gint64 luid;
+
+    g_object_get (device, "adapter-luid", &luid, nullptr);
+    /* source element must hold d3d11 device for the same GPU already
+     * by DXGI duplication API design */
+    if (luid != self->adapter_luid) {
+      GST_ERROR_OBJECT (self, "Trying to capture from different device");
+      return GST_FLOW_ERROR;
+    }
+
+    shared_device = TRUE;
+  }
+
+  GstD3D11CSLockGuard lk (&self->lock);
+  if (!self->prepared)
+    ret = gst_d3d11_dxgi_capture_prepare (capture);
+
+  if (ret != GST_FLOW_OK) {
+    GST_WARNING_OBJECT (self, "We are not prepared");
+    return ret;
+  }
+
+  gst_d3d11_dxgi_capture_get_size_unlocked (self, &width, &height);
+
+  if (crop_box->left > width || crop_box->right > width ||
+      crop_box->top > height || crop_box->bottom > height) {
+    GST_INFO_OBJECT (self,
+        "Capture area (%u, %u, %u, %u) doesn't fit into screen size %ux%u",
+        crop_box->left, crop_box->right, crop_box->top,
+        crop_box->bottom, width, height);
+
+    return GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED;
+  }
+
+  gst_d3d11_device_lock (self->device);
+  ret = self->dupl_obj->Capture ();
+  if (ret != GST_FLOW_OK) {
+    gst_d3d11_device_unlock (self->device);
+
+    delete self->dupl_obj;
+    self->dupl_obj = nullptr;
+    self->prepared = FALSE;
+
+    if (ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR) {
+      GST_WARNING_OBJECT (self, "Couldn't capture frame, but expected failure");
+    } else {
+      GST_ERROR_OBJECT (self, "Unexpected failure during capture");
+    }
+
+    return ret;
+  }
+
+  GST_LOG_OBJECT (self, "Capture done");
+  if (shared_device)
+    gst_d3d11_device_lock (device);
+
+  ret = self->dupl_obj->CopyToTexture (device, texture, crop_box);
+  if (ret != GST_FLOW_OK)
+    goto out;
+
+  if (draw_mouse) {
+    self->dupl_obj->DrawMouse (device,
+        rtv, vs, ps, layout, sampler, blend, crop_box);
+  }
+
+out:
+  if (shared_device)
+    gst_d3d11_device_unlock (device);
+
+  gst_d3d11_device_unlock (self->device);
+
+  return ret;
+}
diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.h b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11dxgicapture.h
new file mode 100644 (file)
index 0000000..a4064c0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * GStreamer
+ * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/d3d11/gstd3d11.h>
+#include "gstd3d11screencapture.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_DXGI_CAPTURE (gst_d3d11_dxgi_capture_get_type())
+G_DECLARE_FINAL_TYPE (GstD3D11DxgiCapture, gst_d3d11_dxgi_capture,
+    GST, D3D11_DXGI_CAPTURE, GstD3D11ScreenCapture);
+
+GstD3D11ScreenCapture * gst_d3d11_dxgi_capture_new (GstD3D11Device * device,
+                                                    HMONITOR monitor_handle);
+
+G_END_DECLS
+
index 15616c9..1324b26 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GStreamer
- * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02110-1301, USA.
  */
 
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) Microsoft Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- *  of this software and associated documentation files (the "Software"), to deal
- *  in the Software without restriction, including without limitation the rights
- *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- *  copies of the Software, and to permit persons to whom the Software is
- *  furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- *  all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- *  THE SOFTWARE.
- */
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -57,1818 +33,64 @@ GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_screen_capture_debug);
 
 /* *INDENT-OFF* */
 using namespace Microsoft::WRL;
-
-/* List of GstD3D11ScreenCapture weakref */
-G_LOCK_DEFINE_STATIC (dupl_list_lock);
-static GList *dupl_list = nullptr;
-
-/* Below implemenation were taken from Microsoft sample
- * https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/DXGIDesktopDuplication
- */
-#define NUMVERTICES 6
-#define BPP 4
-
-/* Define our own MyFLOAT3 and MyFLOAT2 struct, since MinGW doesn't support
- * DirectXMath.h
- */
-struct MyFLOAT3
-{
-  float x;
-  float y;
-  float z;
-
-  MyFLOAT3() = default;
-
-  MyFLOAT3(const MyFLOAT3&) = default;
-  MyFLOAT3& operator=(const MyFLOAT3&) = default;
-
-  MyFLOAT3(MyFLOAT3&&) = default;
-  MyFLOAT3& operator=(MyFLOAT3&&) = default;
-
-  constexpr MyFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
-  explicit MyFLOAT3(const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
-};
-
-struct MyFLOAT2
-{
-  float x;
-  float y;
-
-  MyFLOAT2() = default;
-
-  MyFLOAT2(const MyFLOAT2&) = default;
-  MyFLOAT2& operator=(const MyFLOAT2&) = default;
-
-  MyFLOAT2(MyFLOAT2&&) = default;
-  MyFLOAT2& operator=(MyFLOAT2&&) = default;
-
-  constexpr MyFLOAT2(float _x, float _y) : x(_x), y(_y) {}
-  explicit MyFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {}
-};
-
-typedef struct
-{
-  MyFLOAT3 Pos;
-  MyFLOAT2 TexCoord;
-} VERTEX;
-
-/* List of expected error cases */
-/* These are the errors we expect from general Dxgi API due to a transition */
-HRESULT SystemTransitionsExpectedErrors[] = {
-  DXGI_ERROR_DEVICE_REMOVED,
-  DXGI_ERROR_ACCESS_LOST,
-  static_cast<HRESULT>(WAIT_ABANDONED),
-  S_OK
-};
-
-/* These are the errors we expect from IDXGIOutput1::DuplicateOutput
- * due to a transition */
-HRESULT CreateDuplicationExpectedErrors[] = {
-  DXGI_ERROR_DEVICE_REMOVED,
-  static_cast<HRESULT>(E_ACCESSDENIED),
-  DXGI_ERROR_SESSION_DISCONNECTED,
-  S_OK
-};
-
-/* These are the errors we expect from IDXGIOutputDuplication methods
- * due to a transition */
-HRESULT FrameInfoExpectedErrors[] = {
-  DXGI_ERROR_DEVICE_REMOVED,
-  DXGI_ERROR_ACCESS_LOST,
-  S_OK
-};
-
-/* These are the errors we expect from IDXGIAdapter::EnumOutputs methods
- * due to outputs becoming stale during a transition */
-HRESULT EnumOutputsExpectedErrors[] = {
-  DXGI_ERROR_NOT_FOUND,
-  S_OK
-};
-
-static GstFlowReturn
-gst_d3d11_screen_capture_return_from_hr (ID3D11Device * device,
-    HRESULT hr, HRESULT * expected_errors = nullptr)
-{
-  HRESULT translated_hr = hr;
-
-  /* On an error check if the DX device is lost */
-  if (device) {
-    HRESULT remove_reason = device->GetDeviceRemovedReason ();
-
-    switch (remove_reason) {
-      case DXGI_ERROR_DEVICE_REMOVED:
-      case DXGI_ERROR_DEVICE_RESET:
-      case static_cast<HRESULT>(E_OUTOFMEMORY):
-        /* Our device has been stopped due to an external event on the GPU so
-         * map them all to device removed and continue processing the condition
-         */
-        translated_hr = DXGI_ERROR_DEVICE_REMOVED;
-        break;
-      case S_OK:
-        /* Device is not removed so use original error */
-        break;
-      default:
-        /* Device is removed but not a error we want to remap */
-        translated_hr = remove_reason;
-        break;
-    }
-  }
-
-  /* Check if this error was expected or not */
-  if (expected_errors) {
-    HRESULT* rst = expected_errors;
-
-    while (*rst != S_OK) {
-      if (*rst == translated_hr)
-        return GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR;
-
-      rst++;
-    }
-  }
-
-  return GST_FLOW_ERROR;
-}
-
-class PTR_INFO
-{
-public:
-  PTR_INFO ()
-    : PtrShapeBuffer (nullptr)
-    , BufferSize (0)
-  {
-    LastTimeStamp.QuadPart = 0;
-  }
-
-  ~PTR_INFO ()
-  {
-    if (PtrShapeBuffer)
-      delete[] PtrShapeBuffer;
-  }
-
-  void
-  MaybeReallocBuffer (UINT buffer_size)
-  {
-    if (buffer_size <= BufferSize)
-      return;
-
-    if (PtrShapeBuffer)
-      delete[] PtrShapeBuffer;
-
-    PtrShapeBuffer = new BYTE[buffer_size];
-    BufferSize = buffer_size;
-  }
-
-  BYTE* PtrShapeBuffer;
-  UINT BufferSize;
-  DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
-  POINT Position;
-  bool Visible;
-  LARGE_INTEGER LastTimeStamp;
-};
-
-class D3D11DesktopDupObject
-{
-public:
-  D3D11DesktopDupObject ()
-    : device_(nullptr)
-    , fence_(nullptr)
-    , metadata_buffer_(nullptr)
-    , metadata_buffer_size_(0)
-    , vertex_buffer_(nullptr)
-    , vertex_buffer_size_(0)
-  {
-  }
-
-  ~D3D11DesktopDupObject ()
-  {
-    if (metadata_buffer_)
-      delete[] metadata_buffer_;
-
-    if (vertex_buffer_)
-      delete[] vertex_buffer_;
-
-    gst_clear_d3d11_fence (&fence_);
-    gst_clear_object (&device_);
-  }
-
-  GstFlowReturn
-  Init (GstD3D11Device * device, HMONITOR monitor)
-  {
-    GstFlowReturn ret;
-    ID3D11Device *device_handle;
-    HRESULT hr;
-    D3D11_TEXTURE2D_DESC texture_desc = { 0, };
-
-    if (!InitShader (device))
-      return GST_FLOW_ERROR;
-
-    ret = InitDupl (device, monitor);
-    if (ret != GST_FLOW_OK)
-      return ret;
-
-    GST_INFO ("Init done");
-
-    device_handle = gst_d3d11_device_get_device_handle (device);
-
-    texture_desc.Width = output_desc_.ModeDesc.Width;
-    texture_desc.Height = output_desc_.ModeDesc.Height;
-    texture_desc.MipLevels = 1;
-    texture_desc.ArraySize = 1;
-    /* FIXME: we can support DXGI_FORMAT_R10G10B10A2_UNORM */
-    texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
-    texture_desc.SampleDesc.Count = 1;
-    texture_desc.Usage = D3D11_USAGE_DEFAULT;
-    texture_desc.BindFlags =
-        D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
-    texture_desc.CPUAccessFlags = 0;
-    /* source element may hold different d3d11 device object */
-    texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
-
-    hr = device_handle->CreateTexture2D (&texture_desc,
-        nullptr, &shared_texture_);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR_OBJECT (device, "Couldn't create texture, hr 0x%x", (guint) hr);
-      return GST_FLOW_ERROR;
-    }
-
-    device_ = (GstD3D11Device *) gst_object_ref (device);
-
-    return GST_FLOW_OK;
-  }
-
-  GstFlowReturn
-  Capture ()
-  {
-    GstFlowReturn ret;
-    bool timeout = false;
-    ComPtr<ID3D11Texture2D> texture;
-    UINT move_count, dirty_count;
-    DXGI_OUTDUPL_FRAME_INFO frame_info;
-
-    GST_TRACE ("Capturing");
-    ret = GetFrame (&texture, &move_count, &dirty_count, &frame_info, &timeout);
-    if (ret != GST_FLOW_OK)
-      return ret;
-
-    /* Nothing updated */
-    if (timeout) {
-      GST_TRACE ("timeout");
-      return GST_FLOW_OK;
-    }
-
-    GST_TRACE ("Getting mouse pointer info");
-    ret = GetMouse (&ptr_info_, &frame_info);
-    if (ret != GST_FLOW_OK) {
-      GST_WARNING ("Couldn't get mouse pointer info");
-      dupl_->ReleaseFrame ();
-      return ret;
-    }
-
-    ret = ProcessFrame (texture.Get(), shared_texture_.Get(),
-        &output_desc_, move_count, dirty_count, &frame_info);
-
-    if (ret != GST_FLOW_OK) {
-      dupl_->ReleaseFrame ();
-      GST_WARNING ("Couldn't process frame");
-      return ret;
-    }
-
-    HRESULT hr = dupl_->ReleaseFrame ();
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_WARNING ("Couldn't release frame");
-      return gst_d3d11_screen_capture_return_from_hr (nullptr, hr, FrameInfoExpectedErrors);
-    }
-
-    GST_TRACE ("Capture done");
-
-    return GST_FLOW_OK;
-  }
-
-  bool
-  DrawMouse (GstD3D11Device * device, ID3D11RenderTargetView * rtv,
-      ID3D11VertexShader * vs, ID3D11PixelShader * ps,
-      ID3D11InputLayout * layout, ID3D11SamplerState * sampler,
-      ID3D11BlendState * blend, D3D11_BOX * cropBox)
-  {
-    GST_TRACE ("Drawing mouse");
-
-    if (!ptr_info_.Visible) {
-      GST_TRACE ("Mouse is invisiable");
-      return true;
-    }
-
-    ComPtr<ID3D11Texture2D> MouseTex;
-    ComPtr<ID3D11ShaderResourceView> ShaderRes;
-    ComPtr<ID3D11Buffer> VertexBufferMouse;
-    D3D11_SUBRESOURCE_DATA InitData;
-    D3D11_TEXTURE2D_DESC Desc;
-    D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
-    ID3D11DeviceContext *context_handle =
-        gst_d3d11_device_get_device_context_handle (device);
-
-    VERTEX Vertices[NUMVERTICES] =
-    {
-      {MyFLOAT3(-1.0f, -1.0f, 0), MyFLOAT2(0.0f, 1.0f)},
-      {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
-      {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
-      {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
-      {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
-      {MyFLOAT3(1.0f, 1.0f, 0), MyFLOAT2(1.0f, 0.0f)},
-    };
-
-    D3D11_TEXTURE2D_DESC FullDesc;
-    shared_texture_->GetDesc(&FullDesc);
-    INT DesktopWidth = FullDesc.Width;
-    INT DesktopHeight = FullDesc.Height;
-
-    INT CenterX = (DesktopWidth / 2);
-    INT CenterY = (DesktopHeight / 2);
-
-    INT PtrWidth = 0;
-    INT PtrHeight = 0;
-    INT PtrLeft = 0;
-    INT PtrTop = 0;
-
-    BYTE* InitBuffer = nullptr;
-
-    D3D11_BOX Box;
-    Box.front = 0;
-    Box.back = 1;
-
-    Desc.MipLevels = 1;
-    Desc.ArraySize = 1;
-    Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
-    Desc.SampleDesc.Count = 1;
-    Desc.SampleDesc.Quality = 0;
-    Desc.Usage = D3D11_USAGE_DEFAULT;
-    Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
-    Desc.CPUAccessFlags = 0;
-    Desc.MiscFlags = 0;
-
-    // Set shader resource properties
-    SDesc.Format = Desc.Format;
-    SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
-    SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1;
-    SDesc.Texture2D.MipLevels = Desc.MipLevels;
-
-    switch (ptr_info_.shape_info.Type) {
-      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
-        PtrLeft = ptr_info_.Position.x;
-        PtrTop = ptr_info_.Position.y;
-
-        PtrWidth = static_cast<INT>(ptr_info_.shape_info.Width);
-        PtrHeight = static_cast<INT>(ptr_info_.shape_info.Height);
-        break;
-      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
-        ProcessMonoMask(true, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
-            &PtrTop, &InitBuffer, &Box);
-        break;
-      case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
-        ProcessMonoMask(false, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
-            &PtrTop, &InitBuffer, &Box);
-        break;
-      default:
-        break;
-    }
-
-    /* Nothing to draw */
-    if (PtrWidth == 0 || PtrHeight == 0 ||
-        (PtrLeft + PtrWidth) < static_cast<INT>(cropBox->left) ||
-        PtrLeft > static_cast<INT>(cropBox->right) ||
-        (PtrTop + PtrHeight) < static_cast<INT>(cropBox->top) ||
-        PtrTop > static_cast<INT>(cropBox->bottom)) {
-      if (InitBuffer)
-        delete[] InitBuffer;
-
-      return true;
-    }
-
-    PtrLeft -= cropBox->left;
-    PtrTop -= cropBox->top;
-
-    Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
-    Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
-    Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
-    Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
-    Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
-    Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
-    Vertices[3].Pos.x = Vertices[2].Pos.x;
-    Vertices[3].Pos.y = Vertices[2].Pos.y;
-    Vertices[4].Pos.x = Vertices[1].Pos.x;
-    Vertices[4].Pos.y = Vertices[1].Pos.y;
-    Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
-    Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
-
-    Desc.Width = PtrWidth;
-    Desc.Height = PtrHeight;
-
-    InitData.pSysMem =
-        (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
-         ptr_info_.PtrShapeBuffer : InitBuffer;
-    InitData.SysMemPitch =
-        (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
-        ptr_info_.shape_info.Pitch : PtrWidth * BPP;
-    InitData.SysMemSlicePitch = 0;
-
-    // Create mouseshape as texture
-    HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create texture for rendering mouse");
-      return false;
-    }
-
-    // Create shader resource from texture
-    hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
-        &ShaderRes);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create shader resource view for rendering mouse");
-      return false;
-    }
-
-    D3D11_BUFFER_DESC BDesc;
-    memset (&BDesc, 0, sizeof(D3D11_BUFFER_DESC));
-    BDesc.Usage = D3D11_USAGE_DEFAULT;
-    BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
-    BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
-    BDesc.CPUAccessFlags = 0;
-
-    memset (&InitData, 0, sizeof(D3D11_SUBRESOURCE_DATA));
-    InitData.pSysMem = Vertices;
-
-    // Create vertex buffer
-    hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create vertex buffer for rendering mouse");
-      return false;
-    }
-
-    FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
-    UINT Stride = sizeof(VERTEX);
-    UINT Offset = 0;
-    ID3D11ShaderResourceView *srv = ShaderRes.Get();
-    ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
-
-    context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
-    context_handle->OMSetBlendState(blend, BlendFactor, 0xFFFFFFFF);
-    context_handle->OMSetRenderTargets(1, &rtv, nullptr);
-    context_handle->VSSetShader(vs, nullptr, 0);
-    context_handle->PSSetShader(ps, nullptr, 0);
-    context_handle->PSSetShaderResources(0, 1, &srv);
-    context_handle->PSSetSamplers(0, 1, &sampler);
-    context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-    context_handle->IASetInputLayout(layout);
-
-    D3D11_VIEWPORT VP;
-    VP.Width = static_cast<FLOAT>(FullDesc.Width);
-    VP.Height = static_cast<FLOAT>(FullDesc.Height);
-    VP.MinDepth = 0.0f;
-    VP.MaxDepth = 1.0f;
-    VP.TopLeftX = 0.0f;
-    VP.TopLeftY = 0.0f;
-    context_handle->RSSetViewports(1, &VP);
-
-    context_handle->Draw(NUMVERTICES, 0);
-
-    /* Unbind srv and rtv from context */
-    srv = nullptr;
-    context_handle->PSSetShaderResources (0, 1, &srv);
-    context_handle->OMSetRenderTargets (0, nullptr, nullptr);
-
-    if (InitBuffer)
-      delete[] InitBuffer;
-
-    return true;
-  }
-
-  GstFlowReturn
-  CopyToTexture (GstD3D11Device * device, ID3D11Texture2D * texture,
-      D3D11_BOX * cropBox)
-  {
-    ID3D11DeviceContext *context_handle = nullptr;
-    ComPtr <ID3D11Texture2D> tex;
-    HRESULT hr;
-    gboolean is_shared = FALSE;
-
-    context_handle = gst_d3d11_device_get_device_context_handle (device);
-
-    if (device == device_) {
-      tex = shared_texture_;
-    } else {
-      ID3D11Device *device_handle = nullptr;
-      ComPtr < IDXGIResource > dxgi_resource;
-      HANDLE shared_handle;
-
-      device_handle = gst_d3d11_device_get_device_handle (device);
-
-      hr = shared_texture_.As (&dxgi_resource);
-      if (!gst_d3d11_result (hr, device_))
-        return GST_FLOW_ERROR;
-
-      hr = dxgi_resource->GetSharedHandle (&shared_handle);
-      if (!gst_d3d11_result (hr, device_))
-        return GST_FLOW_ERROR;
-
-      hr = device_handle->OpenSharedResource (shared_handle,
-          IID_PPV_ARGS (&tex));
-      if (!gst_d3d11_result (hr, device))
-        return GST_FLOW_ERROR;
-
-      if (fence_ && fence_->device != device)
-        gst_clear_d3d11_fence (&fence_);
-
-      if (!fence_)
-        fence_ = gst_d3d11_device_create_fence (device);
-
-      if (!fence_)
-        return GST_FLOW_ERROR;
-
-      is_shared = TRUE;
-    }
-
-    context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
-        tex.Get(), 0, cropBox);
-
-    if (is_shared) {
-      if (!gst_d3d11_fence_signal (fence_) || !gst_d3d11_fence_wait (fence_))
-        return GST_FLOW_ERROR;
-    }
-
-    return GST_FLOW_OK;
-  }
-
-  void
-  GetSize (guint * width, guint * height)
-  {
-    *width = output_desc_.ModeDesc.Width;
-    *height = output_desc_.ModeDesc.Height;
-  }
-
-private:
-  /* This method is not expected to be failed unless un-recoverable error case */
-  bool
-  InitShader (GstD3D11Device * device)
-  {
-    static const gchar vs_str[] =
-        "struct VS_INPUT {\n"
-        "  float4 Position: POSITION;\n"
-        "  float2 Texture: TEXCOORD;\n"
-        "};\n"
-        "\n"
-        "struct VS_OUTPUT {\n"
-        "  float4 Position: SV_POSITION;\n"
-        "  float2 Texture: TEXCOORD;\n"
-        "};\n"
-        "\n"
-        "VS_OUTPUT main (VS_INPUT input)\n"
-        "{\n"
-        "  return input;\n"
-        "}";
-
-    static const gchar ps_str[] =
-        "Texture2D shaderTexture;\n"
-        "SamplerState samplerState;\n"
-        "\n"
-        "struct PS_INPUT {\n"
-        "  float4 Position: SV_POSITION;\n"
-        "  float2 Texture: TEXCOORD;\n"
-        "};\n"
-        "\n"
-        "struct PS_OUTPUT {\n"
-        "  float4 Plane: SV_Target;\n"
-        "};\n"
-        "\n"
-        "PS_OUTPUT main(PS_INPUT input)\n"
-        "{\n"
-        "  PS_OUTPUT output;\n"
-        "  output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
-        "  return output;\n"
-        "}";
-
-    D3D11_INPUT_ELEMENT_DESC input_desc[] = {
-      {"POSITION",
-          0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
-      {"TEXCOORD",
-          0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
-    };
-
-    ComPtr<ID3D11VertexShader> vs;
-    ComPtr<ID3D11InputLayout> layout;
-    HRESULT hr;
-
-    hr = gst_d3d11_create_vertex_shader_simple (device,
-        vs_str, "main", input_desc, G_N_ELEMENTS (input_desc), &vs, &layout);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create vertex shader");
-      return false;
-    }
-
-    ComPtr<ID3D11PixelShader> ps;
-    hr = gst_d3d11_create_pixel_shader_simple (device, ps_str, "main", &ps);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create pixel shader");
-      return false;
-    }
-
-    D3D11_SAMPLER_DESC sampler_desc;
-    memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
-    sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
-    sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
-    sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
-    sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
-    sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
-    sampler_desc.MinLOD = 0;
-    sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
-
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
-    ComPtr<ID3D11SamplerState> sampler;
-    hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Failed to create sampler state, hr 0x%x", (guint) hr);
-      return false;
-    }
-
-    /* Everything is prepared now */
-    vs_ = vs;
-    ps_ = ps;
-    layout_ = layout;
-    sampler_ = sampler;
-
-    return true;
-  }
-
-  /* Maybe returning expected error code depending on desktop status */
-  GstFlowReturn
-  InitDupl (GstD3D11Device * device, HMONITOR monitor)
-  {
-    ComPtr<ID3D11Device> d3d11_device;
-    ComPtr<IDXGIAdapter1> adapter;
-    ComPtr<IDXGIOutput> output;
-    ComPtr<IDXGIOutput1> output1;
-
-    d3d11_device = gst_d3d11_device_get_device_handle (device);
-
-    HRESULT hr = gst_d3d11_screen_capture_find_output_for_monitor (monitor,
-        &adapter, &output);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Couldn't get adapter and output for monitor");
-      return GST_FLOW_ERROR;
-    }
-
-    hr = output.As (&output1);
-    if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Couldn't get IDXGIOutput1 interface, hr 0x%x", (guint) hr);
-      return GST_FLOW_ERROR;
-    }
-
-    HDESK hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
-    if (hdesk) {
-      if (!SetThreadDesktop (hdesk)) {
-        GST_WARNING ("SetThreadDesktop() failed, error %lu", GetLastError());
-      }
-
-      CloseDesktop (hdesk);
-    } else {
-      GST_WARNING ("OpenInputDesktop() failed, error %lu", GetLastError());
-    }
-
-    /* FIXME: Use DuplicateOutput1 to avoid potentail color conversion */
-    hr = output1->DuplicateOutput(d3d11_device.Get(), &dupl_);
-    if (!gst_d3d11_result (hr, device)) {
-      if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
-        GST_ERROR ("Hit the max allowed number of Desktop Duplication session");
-        return GST_FLOW_ERROR;
-      }
-
-      /* Seems to be one limitation of Desktop Duplication API design
-       * See
-       * https://docs.microsoft.com/en-US/troubleshoot/windows-client/shell-experience/error-when-dda-capable-app-is-against-gpu
-       */
-      if (hr == DXGI_ERROR_UNSUPPORTED) {
-        GST_WARNING ("IDXGIOutput1::DuplicateOutput returned "
-            "DXGI_ERROR_UNSUPPORTED, possiblely application is run against a "
-            "discrete GPU");
-        return GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED;
-      }
-
-      return gst_d3d11_screen_capture_return_from_hr (d3d11_device.Get(), hr,
-          CreateDuplicationExpectedErrors);
-    }
-
-    dupl_->GetDesc (&output_desc_);
-
-    return GST_FLOW_OK;
-  }
-
-  GstFlowReturn
-  GetMouse (PTR_INFO * ptr_info, DXGI_OUTDUPL_FRAME_INFO * frame_info)
-  {
-    /* A non-zero mouse update timestamp indicates that there is a mouse
-     * position update and optionally a shape change */
-    if (frame_info->LastMouseUpdateTime.QuadPart == 0)
-      return GST_FLOW_OK;
-
-    ptr_info->Position.x = frame_info->PointerPosition.Position.x;
-    ptr_info->Position.y = frame_info->PointerPosition.Position.y;
-    ptr_info->LastTimeStamp = frame_info->LastMouseUpdateTime;
-    ptr_info->Visible = frame_info->PointerPosition.Visible != 0;
-
-    /* No new shape */
-    if (frame_info->PointerShapeBufferSize == 0)
-      return GST_FLOW_OK;
-
-    /* Realloc buffer if needed */
-    ptr_info->MaybeReallocBuffer (frame_info->PointerShapeBufferSize);
-
-    /* Must always get shape of cursor, even if not drawn at the moment.
-     * Shape of cursor is not repeated by the AcquireNextFrame and can be
-     * requested to be drawn any time later */
-    UINT dummy;
-    HRESULT hr = dupl_->GetFramePointerShape(frame_info->PointerShapeBufferSize,
-        (void *) ptr_info->PtrShapeBuffer, &dummy, &ptr_info->shape_info);
-
-    if (!gst_d3d11_result (hr, device_)) {
-      ID3D11Device *device_handle =
-          gst_d3d11_device_get_device_handle (device_);
-
-      return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
-          FrameInfoExpectedErrors);
-    }
-
-    return GST_FLOW_OK;
-  }
-
-  void
-  MaybeReallocMetadataBuffer (UINT buffer_size)
-  {
-    if (buffer_size <= metadata_buffer_size_)
-      return;
-
-    if (metadata_buffer_)
-      delete[] metadata_buffer_;
-
-    metadata_buffer_ = new BYTE[buffer_size];
-    metadata_buffer_size_ = buffer_size;
-  }
-
-  GstFlowReturn
-  GetFrame (ID3D11Texture2D ** texture, UINT * move_count, UINT * dirty_count,
-      DXGI_OUTDUPL_FRAME_INFO * frame_info, bool* timeout)
-  {
-    ComPtr<IDXGIResource> resource;
-    ComPtr<ID3D11Texture2D> acquired_texture;
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
-
-    /* Get new frame */
-    HRESULT hr = dupl_->AcquireNextFrame(0, frame_info, &resource);
-    if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
-      GST_TRACE ("Timeout");
-
-      *timeout = true;
-      return GST_FLOW_OK;
-    }
-
-    *timeout = false;
-    *move_count = 0;
-    *dirty_count = 0;
-
-    if (!gst_d3d11_result (hr, device_)) {
-      return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
-          FrameInfoExpectedErrors);
-    }
-
-    GST_TRACE (
-        "LastPresentTime: %" G_GINT64_FORMAT
-        ", LastMouseUpdateTime: %" G_GINT64_FORMAT
-        ", AccumulatedFrames: %d"
-        ", RectsCoalesced: %d"
-        ", ProtectedContentMaskedOut: %d"
-        ", PointerPosition: (%ldx%ld, visible %d)"
-        ", TotalMetadataBufferSize: %d"
-        ", PointerShapeBufferSize: %d",
-        frame_info->LastPresentTime.QuadPart,
-        frame_info->LastMouseUpdateTime.QuadPart,
-        frame_info->AccumulatedFrames,
-        frame_info->RectsCoalesced,
-        frame_info->ProtectedContentMaskedOut,
-        frame_info->PointerPosition.Position.x,
-        frame_info->PointerPosition.Position.y,
-        frame_info->PointerPosition.Visible,
-        frame_info->TotalMetadataBufferSize,
-        frame_info->PointerShapeBufferSize);
-
-    hr = resource.As (&acquired_texture);
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Failed to get ID3D11Texture2D interface from IDXGIResource "
-          "hr 0x%x", (guint) hr);
-      return GST_FLOW_ERROR;
-    }
-
-    /* Get metadata */
-    if (frame_info->TotalMetadataBufferSize) {
-      UINT buf_size = frame_info->TotalMetadataBufferSize;
-
-      MaybeReallocMetadataBuffer (buf_size);
-
-      /* Get move rectangles */
-      hr = dupl_->GetFrameMoveRects(buf_size,
-          (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_, &buf_size);
-      if (!gst_d3d11_result (hr, device_)) {
-        GST_ERROR ("Couldn't get move rect, hr 0x%x", (guint) hr);
-
-        return gst_d3d11_screen_capture_return_from_hr(nullptr, hr,
-            FrameInfoExpectedErrors);
-      }
-
-      *move_count = buf_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
-
-      GST_TRACE ("MoveRects count %d", *move_count);
-#ifndef GST_DISABLE_GST_DEBUG
-      {
-        DXGI_OUTDUPL_MOVE_RECT *rects =
-            (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_;
-        for (guint i = 0; i < *move_count; i++) {
-          GST_TRACE ("MoveRect[%d] SourcePoint: %ldx%ld, "
-            "DestinationRect (left:top:right:bottom): %ldx%ldx%ldx%ld",
-            i, rects->SourcePoint.x, rects->SourcePoint.y,
-            rects->DestinationRect.left, rects->DestinationRect.top,
-            rects->DestinationRect.right, rects->DestinationRect.bottom);
-        }
-      }
-#endif
-
-      BYTE* dirty_rects = metadata_buffer_ + buf_size;
-      buf_size = frame_info->TotalMetadataBufferSize - buf_size;
-
-      /* Get dirty rectangles */
-      hr = dupl_->GetFrameDirtyRects(buf_size, (RECT *) dirty_rects, &buf_size);
-      if (!gst_d3d11_result (hr, device_)) {
-        GST_ERROR ("Couldn't get dirty rect, hr 0x%x", (guint) hr);
-        *move_count = 0;
-        *dirty_count = 0;
-
-        return gst_d3d11_screen_capture_return_from_hr(nullptr,
-            hr, FrameInfoExpectedErrors);
-      }
-
-      *dirty_count = buf_size / sizeof(RECT);
-
-      GST_TRACE ("DirtyRects count %d", *dirty_count);
-#ifndef GST_DISABLE_GST_DEBUG
-      {
-        RECT *rects = (RECT *) dirty_rects;
-        for (guint i = 0; i < *dirty_count; i++) {
-          GST_TRACE ("DirtyRect[%d] left:top:right:bottom: %ldx%ldx%ldx%ld",
-            i, rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
-        }
-      }
-#endif
-    }
-
-    *texture = acquired_texture.Detach();
-
-    return GST_FLOW_OK;
-  }
-
-  void
-  SetMoveRect (RECT* SrcRect, RECT* DestRect, DXGI_OUTDUPL_DESC* DeskDesc,
-      DXGI_OUTDUPL_MOVE_RECT* MoveRect, INT TexWidth, INT TexHeight)
-  {
-    switch (DeskDesc->Rotation)
-    {
-      case DXGI_MODE_ROTATION_UNSPECIFIED:
-      case DXGI_MODE_ROTATION_IDENTITY:
-        SrcRect->left = MoveRect->SourcePoint.x;
-        SrcRect->top = MoveRect->SourcePoint.y;
-        SrcRect->right = MoveRect->SourcePoint.x +
-            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
-        SrcRect->bottom = MoveRect->SourcePoint.y +
-            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
-
-        *DestRect = MoveRect->DestinationRect;
-        break;
-      case DXGI_MODE_ROTATION_ROTATE90:
-        SrcRect->left = TexHeight - (MoveRect->SourcePoint.y +
-            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
-        SrcRect->top = MoveRect->SourcePoint.x;
-        SrcRect->right = TexHeight - MoveRect->SourcePoint.y;
-        SrcRect->bottom = MoveRect->SourcePoint.x +
-            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
-
-        DestRect->left = TexHeight - MoveRect->DestinationRect.bottom;
-        DestRect->top = MoveRect->DestinationRect.left;
-        DestRect->right = TexHeight - MoveRect->DestinationRect.top;
-        DestRect->bottom = MoveRect->DestinationRect.right;
-        break;
-      case DXGI_MODE_ROTATION_ROTATE180:
-        SrcRect->left = TexWidth - (MoveRect->SourcePoint.x +
-            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
-        SrcRect->top = TexHeight - (MoveRect->SourcePoint.y +
-            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
-        SrcRect->right = TexWidth - MoveRect->SourcePoint.x;
-        SrcRect->bottom = TexHeight - MoveRect->SourcePoint.y;
-
-        DestRect->left = TexWidth - MoveRect->DestinationRect.right;
-        DestRect->top = TexHeight - MoveRect->DestinationRect.bottom;
-        DestRect->right = TexWidth - MoveRect->DestinationRect.left;
-        DestRect->bottom =  TexHeight - MoveRect->DestinationRect.top;
-        break;
-      case DXGI_MODE_ROTATION_ROTATE270:
-        SrcRect->left = MoveRect->SourcePoint.x;
-        SrcRect->top = TexWidth - (MoveRect->SourcePoint.x +
-            MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
-        SrcRect->right = MoveRect->SourcePoint.y +
-            MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
-        SrcRect->bottom = TexWidth - MoveRect->SourcePoint.x;
-
-        DestRect->left = MoveRect->DestinationRect.top;
-        DestRect->top = TexWidth - MoveRect->DestinationRect.right;
-        DestRect->right = MoveRect->DestinationRect.bottom;
-        DestRect->bottom =  TexWidth - MoveRect->DestinationRect.left;
-        break;
-      default:
-        memset (DestRect, 0, sizeof (RECT));
-        memset (SrcRect, 0, sizeof (RECT));
-        break;
-    }
-  }
-
-  GstFlowReturn
-  CopyMove (ID3D11Texture2D* SharedSurf, DXGI_OUTDUPL_MOVE_RECT* MoveBuffer,
-      UINT MoveCount, DXGI_OUTDUPL_DESC* DeskDesc)
-  {
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
-    ID3D11DeviceContext *device_context =
-       gst_d3d11_device_get_device_context_handle (device_);
-    D3D11_TEXTURE2D_DESC FullDesc;
-    SharedSurf->GetDesc(&FullDesc);
-
-    GST_TRACE ("Copying MoveRects (count %d)", MoveCount);
-
-    /* Make new intermediate surface to copy into for moving */
-    if (!move_texture_) {
-      D3D11_TEXTURE2D_DESC MoveDesc;
-      MoveDesc = FullDesc;
-      MoveDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
-      MoveDesc.MiscFlags = 0;
-      HRESULT hr = device_handle->CreateTexture2D(&MoveDesc,
-          nullptr, &move_texture_);
-      if (!gst_d3d11_result (hr, device_)) {
-        GST_ERROR ("Couldn't create intermediate texture, hr 0x%x", (guint) hr);
-        return GST_FLOW_ERROR;
-      }
-    }
-
-    for (UINT i = 0; i < MoveCount; i++) {
-      RECT SrcRect;
-      RECT DestRect;
-
-      SetMoveRect(&SrcRect, &DestRect, DeskDesc, &MoveBuffer[i],
-          FullDesc.Width, FullDesc.Height);
-
-      /* Copy rect out of shared surface */
-      D3D11_BOX Box;
-      Box.left = SrcRect.left;
-      Box.top = SrcRect.top;
-      Box.front = 0;
-      Box.right = SrcRect.right;
-      Box.bottom = SrcRect.bottom;
-      Box.back = 1;
-      device_context->CopySubresourceRegion(move_texture_.Get(),
-          0, SrcRect.left, SrcRect.top, 0, SharedSurf, 0, &Box);
-
-      /* Copy back to shared surface */
-      device_context->CopySubresourceRegion(SharedSurf,
-          0, DestRect.left, DestRect.top, 0, move_texture_.Get(), 0, &Box);
-    }
-
-    return GST_FLOW_OK;
-  }
-
-  void
-  SetDirtyVert (VERTEX* Vertices, RECT* Dirty,
-      DXGI_OUTDUPL_DESC* DeskDesc, D3D11_TEXTURE2D_DESC* FullDesc,
-      D3D11_TEXTURE2D_DESC* ThisDesc)
-  {
-    INT CenterX = FullDesc->Width / 2;
-    INT CenterY = FullDesc->Height / 2;
-
-    INT Width = FullDesc->Width;
-    INT Height = FullDesc->Height;
-
-    /* Rotation compensated destination rect */
-    RECT DestDirty = *Dirty;
-
-    /* Set appropriate coordinates compensated for rotation */
-    switch (DeskDesc->Rotation)
-    {
-      case DXGI_MODE_ROTATION_ROTATE90:
-        DestDirty.left = Width - Dirty->bottom;
-        DestDirty.top = Dirty->left;
-        DestDirty.right = Width - Dirty->top;
-        DestDirty.bottom = Dirty->right;
-
-        Vertices[0].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[1].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[2].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[5].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        break;
-      case DXGI_MODE_ROTATION_ROTATE180:
-        DestDirty.left = Width - Dirty->right;
-        DestDirty.top = Height - Dirty->bottom;
-        DestDirty.right = Width - Dirty->left;
-        DestDirty.bottom = Height - Dirty->top;
-
-        Vertices[0].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[1].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[2].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[5].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        break;
-      case DXGI_MODE_ROTATION_ROTATE270:
-        DestDirty.left = Dirty->top;
-        DestDirty.top = Height - Dirty->right;
-        DestDirty.right = Dirty->bottom;
-        DestDirty.bottom = Height - Dirty->left;
-
-        Vertices[0].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[1].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[2].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[5].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        break;
-      case DXGI_MODE_ROTATION_UNSPECIFIED:
-      case DXGI_MODE_ROTATION_IDENTITY:
-      default:
-        Vertices[0].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[1].TexCoord =
-            MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[2].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
-        Vertices[5].TexCoord =
-            MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
-                     Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
-        break;
-    }
-
-    /* Set positions */
-    Vertices[0].Pos =
-        MyFLOAT3(
-          (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
-          -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
-          0.0f);
-    Vertices[1].Pos =
-        MyFLOAT3(
-          (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
-          -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
-          0.0f);
-    Vertices[2].Pos =
-        MyFLOAT3(
-          (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
-          -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
-          0.0f);
-    Vertices[3].Pos = Vertices[2].Pos;
-    Vertices[4].Pos = Vertices[1].Pos;
-    Vertices[5].Pos =
-        MyFLOAT3(
-          (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
-          -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
-          0.0f);
-
-    Vertices[3].TexCoord = Vertices[2].TexCoord;
-    Vertices[4].TexCoord = Vertices[1].TexCoord;
-  }
-
-  void
-  MaybeReallocVertexBuffer (UINT buffer_size)
-  {
-    if (buffer_size <= vertex_buffer_size_)
-      return;
-
-    if (vertex_buffer_)
-      delete[] vertex_buffer_;
-
-    vertex_buffer_ = new BYTE[buffer_size];
-    vertex_buffer_size_ = buffer_size;
-  }
-
-  GstFlowReturn
-  CopyDirty (ID3D11Texture2D* SrcSurface, ID3D11Texture2D* SharedSurf,
-      RECT* DirtyBuffer, UINT DirtyCount, DXGI_OUTDUPL_DESC* DeskDesc)
-  {
-    D3D11_TEXTURE2D_DESC FullDesc;
-    D3D11_TEXTURE2D_DESC ThisDesc;
-    ComPtr<ID3D11ShaderResourceView> ShaderResource;
-    ComPtr<ID3D11Buffer> VertBuf;
-    HRESULT hr;
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
-    ID3D11DeviceContext *device_context =
-       gst_d3d11_device_get_device_context_handle (device_);
-
-    GST_TRACE ("Copying DirtyRects (count %d)", DirtyCount);
-
-    SharedSurf->GetDesc(&FullDesc);
-    SrcSurface->GetDesc(&ThisDesc);
-
-    if (!rtv_) {
-      hr = device_handle->CreateRenderTargetView(SharedSurf, nullptr, &rtv_);
-      if (!gst_d3d11_result (hr, device_)) {
-        GST_ERROR ("Couldn't create render target view, hr 0x%x", (guint) hr);
-        return GST_FLOW_ERROR;
-      }
-    }
-
-    D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
-    ShaderDesc.Format = ThisDesc.Format;
-    ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
-    ShaderDesc.Texture2D.MostDetailedMip = ThisDesc.MipLevels - 1;
-    ShaderDesc.Texture2D.MipLevels = ThisDesc.MipLevels;
-
-    /* Create new shader resource view */
-    hr = device_handle->CreateShaderResourceView(SrcSurface,
-        &ShaderDesc, &ShaderResource);
-    if (!gst_d3d11_result (hr, device_)) {
-      return gst_d3d11_screen_capture_return_from_hr(device_handle, hr,
-          SystemTransitionsExpectedErrors);
-    }
-
-    ID3D11SamplerState *samplers = sampler_.Get();
-    ID3D11ShaderResourceView *srv = ShaderResource.Get();
-    ID3D11RenderTargetView *rtv = rtv_.Get();
-    device_context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
-    device_context->OMSetRenderTargets(1, &rtv, nullptr);
-    device_context->VSSetShader(vs_.Get(), nullptr, 0);
-    device_context->PSSetShader(ps_.Get(), nullptr, 0);
-    device_context->PSSetShaderResources(0, 1, &srv);
-    device_context->PSSetSamplers(0, 1, &samplers);
-    device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
-    device_context->IASetInputLayout(layout_.Get());
-
-    /* Create space for vertices for the dirty rects if the current space isn't
-     * large enough */
-    UINT byte_needed = sizeof(VERTEX) * NUMVERTICES * DirtyCount;
-    MaybeReallocVertexBuffer (byte_needed);
-
-    /* Fill them in */
-    VERTEX* DirtyVertex = (VERTEX *) vertex_buffer_;
-    for (UINT i = 0; i < DirtyCount; ++i, DirtyVertex += NUMVERTICES) {
-      SetDirtyVert(DirtyVertex, &DirtyBuffer[i], DeskDesc,
-          &FullDesc, &ThisDesc);
-    }
-
-    /* Create vertex buffer */
-    D3D11_BUFFER_DESC BufferDesc;
-    memset (&BufferDesc, 0, sizeof (D3D11_BUFFER_DESC));
-    BufferDesc.Usage = D3D11_USAGE_DEFAULT;
-    BufferDesc.ByteWidth = byte_needed;
-    BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
-    BufferDesc.CPUAccessFlags = 0;
-    D3D11_SUBRESOURCE_DATA InitData;
-    memset (&InitData, 0, sizeof (D3D11_SUBRESOURCE_DATA));
-    InitData.pSysMem = vertex_buffer_;
-
-    hr = device_handle->CreateBuffer(&BufferDesc, &InitData, &VertBuf);
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Failed to create vertex buffer");
-      return GST_FLOW_ERROR;
-    }
-
-    UINT Stride = sizeof(VERTEX);
-    UINT Offset = 0;
-    ID3D11Buffer *vert_buf = VertBuf.Get();
-    device_context->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
-
-    D3D11_VIEWPORT VP;
-    VP.Width = static_cast<FLOAT>(FullDesc.Width);
-    VP.Height = static_cast<FLOAT>(FullDesc.Height);
-    VP.MinDepth = 0.0f;
-    VP.MaxDepth = 1.0f;
-    VP.TopLeftX = 0.0f;
-    VP.TopLeftY = 0.0f;
-    device_context->RSSetViewports(1, &VP);
-
-    device_context->Draw(NUMVERTICES * DirtyCount, 0);
-
-    /* Unbind srv and rtv from context */
-    srv = nullptr;
-    device_context->PSSetShaderResources (0, 1, &srv);
-    device_context->OMSetRenderTargets (0, nullptr, nullptr);
-
-    return GST_FLOW_OK;
-  }
-
-  GstFlowReturn
-  ProcessFrame(ID3D11Texture2D * acquired_texture, ID3D11Texture2D* SharedSurf,
-      DXGI_OUTDUPL_DESC* DeskDesc, UINT move_count, UINT dirty_count,
-      DXGI_OUTDUPL_FRAME_INFO * frame_info)
-  {
-    GstFlowReturn ret = GST_FLOW_OK;
-
-    GST_TRACE ("Processing frame");
-
-    /* Process dirties and moves */
-    if (frame_info->TotalMetadataBufferSize) {
-      if (move_count) {
-        ret = CopyMove(SharedSurf, (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_,
-            move_count, DeskDesc);
-
-        if (ret != GST_FLOW_OK)
-          return ret;
-      }
-
-      if (dirty_count) {
-        ret = CopyDirty(acquired_texture, SharedSurf,
-            (RECT *)(metadata_buffer_ +
-                (move_count * sizeof(DXGI_OUTDUPL_MOVE_RECT))),
-            dirty_count, DeskDesc);
-      }
-    } else {
-      GST_TRACE ("No metadata");
-    }
-
-    return ret;
-  }
-
-  /* To draw mouse */
-  bool
-  ProcessMonoMask (bool IsMono, PTR_INFO* PtrInfo, INT* PtrWidth,
-      INT* PtrHeight, INT* PtrLeft, INT* PtrTop, BYTE** InitBuffer,
-      D3D11_BOX* Box)
-  {
-    ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
-    ID3D11DeviceContext *context_handle =
-        gst_d3d11_device_get_device_context_handle (device_);
-
-    D3D11_TEXTURE2D_DESC FullDesc;
-    shared_texture_->GetDesc(&FullDesc);
-    INT DesktopWidth = FullDesc.Width;
-    INT DesktopHeight = FullDesc.Height;
-
-    // Pointer position
-    INT GivenLeft = PtrInfo->Position.x;
-    INT GivenTop = PtrInfo->Position.y;
-
-    // Figure out if any adjustment is needed for out of bound positions
-    if (GivenLeft < 0) {
-      *PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width);
-    } else if ((GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width)) > DesktopWidth) {
-      *PtrWidth = DesktopWidth - GivenLeft;
-    } else {
-      *PtrWidth = static_cast<INT>(PtrInfo->shape_info.Width);
-    }
-
-    if (IsMono)
-      PtrInfo->shape_info.Height = PtrInfo->shape_info.Height / 2;
-
-    if (GivenTop < 0) {
-      *PtrHeight = GivenTop + static_cast<INT>(PtrInfo->shape_info.Height);
-    } else if ((GivenTop + static_cast<INT>(PtrInfo->shape_info.Height)) > DesktopHeight) {
-      *PtrHeight = DesktopHeight - GivenTop;
-    } else {
-      *PtrHeight = static_cast<INT>(PtrInfo->shape_info.Height);
-    }
-
-    if (IsMono)
-      PtrInfo->shape_info.Height = PtrInfo->shape_info.Height * 2;
-
-    *PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft;
-    *PtrTop = (GivenTop < 0) ? 0 : GivenTop;
-
-    D3D11_TEXTURE2D_DESC CopyBufferDesc;
-    CopyBufferDesc.Width = *PtrWidth;
-    CopyBufferDesc.Height = *PtrHeight;
-    CopyBufferDesc.MipLevels = 1;
-    CopyBufferDesc.ArraySize = 1;
-    CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
-    CopyBufferDesc.SampleDesc.Count = 1;
-    CopyBufferDesc.SampleDesc.Quality = 0;
-    CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
-    CopyBufferDesc.BindFlags = 0;
-    CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
-    CopyBufferDesc.MiscFlags = 0;
-
-    ComPtr<ID3D11Texture2D> CopyBuffer;
-    HRESULT hr = device_handle->CreateTexture2D(&CopyBufferDesc,
-        nullptr, &CopyBuffer);
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Couldn't create texture for mouse pointer");
-      return false;
-    }
-
-    Box->left = *PtrLeft;
-    Box->top = *PtrTop;
-    Box->right = *PtrLeft + *PtrWidth;
-    Box->bottom = *PtrTop + *PtrHeight;
-    context_handle->CopySubresourceRegion(CopyBuffer.Get(),
-        0, 0, 0, 0, shared_texture_.Get(), 0, Box);
-
-    ComPtr<IDXGISurface> CopySurface;
-    hr = CopyBuffer.As (&CopySurface);
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Couldn't get DXGI resource from mouse texture");
-      return false;
-    }
-
-    DXGI_MAPPED_RECT MappedSurface;
-    hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Couldn't map DXGI surface");
-      return false;
-    }
-
-    *InitBuffer = new BYTE[*PtrWidth * *PtrHeight * BPP];
-
-    UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer);
-    UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits);
-    UINT  DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT);
-
-    // What to skip (pixel offset)
-    UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0);
-    UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0);
-
-    if (IsMono) {
-      for (INT Row = 0; Row < *PtrHeight; Row++) {
-        BYTE Mask = 0x80;
-        Mask = Mask >> (SkipX % 8);
-        for (INT Col = 0; Col < *PtrWidth; Col++) {
-          BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
-              ((Row + SkipY) * (PtrInfo->shape_info.Pitch))] & Mask;
-          BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
-              ((Row + SkipY + (PtrInfo->shape_info.Height / 2)) *
-                  (PtrInfo->shape_info.Pitch))] & Mask;
-          UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
-          UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
-
-          InitBuffer32[(Row * *PtrWidth) + Col] =
-              (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32;
-
-          if (Mask == 0x01) {
-              Mask = 0x80;
-          } else {
-              Mask = Mask >> 1;
-          }
-        }
-      }
-    } else {
-      UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
-
-      for (INT Row = 0; Row < *PtrHeight; Row++) {
-        for (INT Col = 0; Col < *PtrWidth; ++Col) {
-          // Set up mask
-          UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) *
-              (PtrInfo->shape_info.Pitch / sizeof(UINT)))];
-          if (MaskVal) {
-            // Mask was 0xFF
-            InitBuffer32[(Row * *PtrWidth) + Col] =
-                (Desktop32[(Row * DesktopPitchInPixels) + Col] ^
-                    Buffer32[(Col + SkipX) + ((Row + SkipY) *
-                    (PtrInfo->shape_info.Pitch / sizeof(UINT)))]) | 0xFF000000;
-          } else {
-            // Mask was 0x00
-            InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) +
-                ((Row + SkipY) * (PtrInfo->shape_info.Pitch / sizeof(UINT)))] | 0xFF000000;
-          }
-        }
-      }
-    }
-
-    // Done with resource
-    hr = CopySurface->Unmap();
-    if (!gst_d3d11_result (hr, device_)) {
-      GST_ERROR ("Failed to unmap DXGI surface");
-      return false;
-    }
-
-    return true;
-  }
-
-private:
-  PTR_INFO ptr_info_;
-  DXGI_OUTDUPL_DESC output_desc_;
-  GstD3D11Device * device_;
-  GstD3D11Fence * fence_;
-
-  ComPtr<ID3D11Texture2D> shared_texture_;
-  ComPtr<ID3D11RenderTargetView> rtv_;
-  ComPtr<ID3D11Texture2D> move_texture_;
-  ComPtr<ID3D11VertexShader> vs_;
-  ComPtr<ID3D11PixelShader> ps_;
-  ComPtr<ID3D11InputLayout> layout_;
-  ComPtr<ID3D11SamplerState> sampler_;
-  ComPtr<IDXGIOutputDuplication> dupl_;
-
-  /* frame metadata */
-  BYTE *metadata_buffer_;
-  UINT metadata_buffer_size_;
-
-  /* vertex buffers */
-  BYTE *vertex_buffer_;
-  UINT vertex_buffer_size_;
-};
 /* *INDENT-ON* */
 
-enum
-{
-  PROP_0,
-  PROP_D3D11_DEVICE,
-  PROP_MONITOR_HANDLE,
-};
-
-#define DEFAULT_MONITOR_INDEX -1
-
-struct _GstD3D11ScreenCapture
-{
-  GstObject parent;
-
-  GstD3D11Device *device;
-  guint cached_width;
-  guint cached_height;
-
-  D3D11DesktopDupObject *dupl_obj;
-  IDXGIOutput *output;
-
-  HMONITOR monitor_handle;
-  RECT desktop_coordinates;
-  gboolean prepared;
-  gint64 adapter_luid;
-
-  CRITICAL_SECTION lock;
-};
-
-static void gst_d3d11_screen_capture_constructed (GObject * object);
-static void gst_d3d11_screen_capture_dispose (GObject * object);
-static void gst_d3d11_screen_capture_finalize (GObject * object);
-static void gst_d3d11_screen_capture_set_property (GObject * object,
-    guint prop_id, const GValue * value, GParamSpec * pspec);
-
 #define gst_d3d11_screen_capture_parent_class parent_class
-G_DEFINE_TYPE (GstD3D11ScreenCapture, gst_d3d11_screen_capture,
+G_DEFINE_ABSTRACT_TYPE (GstD3D11ScreenCapture, gst_d3d11_screen_capture,
     GST_TYPE_OBJECT);
 
 static void
 gst_d3d11_screen_capture_class_init (GstD3D11ScreenCaptureClass * klass)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
-  gobject_class->constructed = gst_d3d11_screen_capture_constructed;
-  gobject_class->dispose = gst_d3d11_screen_capture_dispose;
-  gobject_class->finalize = gst_d3d11_screen_capture_finalize;
-  gobject_class->set_property = gst_d3d11_screen_capture_set_property;
-
-  g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
-      g_param_spec_object ("d3d11device", "D3D11 Device",
-          "GstD3D11Device object for operating",
-          GST_TYPE_D3D11_DEVICE, (GParamFlags)
-          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
-              G_PARAM_STATIC_STRINGS)));
-  g_object_class_install_property (gobject_class, PROP_MONITOR_HANDLE,
-      g_param_spec_pointer ("monitor-handle", "Monitor Handle",
-          "A HMONITOR handle of monitor to capture", (GParamFlags)
-          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
-              G_PARAM_STATIC_STRINGS)));
 }
 
 static void
 gst_d3d11_screen_capture_init (GstD3D11ScreenCapture * self)
 {
-  InitializeCriticalSection (&self->lock);
-
-  memset (&self->desktop_coordinates, 0, sizeof (RECT));
-}
-
-static void
-gst_d3d11_screen_capture_constructed (GObject * object)
-{
-  GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
-  /* *INDENT-OFF* */
-  ComPtr<IDXGIDevice> dxgi_device;
-  ComPtr<IDXGIAdapter1> adapter;
-  ComPtr<IDXGIOutput> output;
-  ComPtr<IDXGIOutput1> output1;
-  /* *INDENT-ON* */
-  HRESULT hr;
-  gboolean ret = FALSE;
-  DXGI_OUTPUT_DESC output_desc;
-  DXGI_ADAPTER_DESC adapter_desc;
-  gint64 luid, device_luid;
-
-  if (!self->device) {
-    GST_WARNING_OBJECT (self, "D3D11 device is unavailable");
-    goto out;
-  }
-
-  if (!self->monitor_handle) {
-    GST_WARNING_OBJECT (self, "Null monitor handle");
-    goto out;
-  }
-
-  hr = gst_d3d11_screen_capture_find_output_for_monitor (self->monitor_handle,
-      &adapter, &output);
-  if (!gst_d3d11_result (hr, self->device)) {
-    GST_WARNING_OBJECT (self,
-        "Failed to find associated adapter for monitor %p",
-        self->monitor_handle);
-    goto out;
-  }
-
-  hr = output.As (&output1);
-  if (!gst_d3d11_result (hr, self->device)) {
-    GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
-    goto out;
-  }
-
-  hr = adapter->GetDesc (&adapter_desc);
-  if (!gst_d3d11_result (hr, self->device)) {
-    GST_WARNING_OBJECT (self, "Failed to get adapter desc");
-    goto out;
-  }
-
-  luid = gst_d3d11_luid_to_int64 (&adapter_desc.AdapterLuid);
-  g_object_get (self->device, "adapter-luid", &device_luid, nullptr);
-  if (luid != device_luid) {
-    GST_WARNING_OBJECT (self, "Incompatible d3d11 device");
-    goto out;
-  }
-
-  hr = output->GetDesc (&output_desc);
-  if (!gst_d3d11_result (hr, self->device)) {
-    GST_WARNING_OBJECT (self, "Failed to get output desc");
-    goto out;
-  }
-
-  /* DesktopCoordinates will not report actual texture size in case that
-   * application is running without dpi-awareness. To get actual monitor size,
-   * we need to use Win32 API... */
-  MONITORINFOEXW monitor_info;
-  DEVMODEW dev_mode;
-
-  monitor_info.cbSize = sizeof (MONITORINFOEXW);
-  if (!GetMonitorInfoW (output_desc.Monitor, (LPMONITORINFO) & monitor_info)) {
-    GST_WARNING_OBJECT (self, "Couldn't get monitor info");
-    goto out;
-  }
-
-  dev_mode.dmSize = sizeof (DEVMODEW);
-  dev_mode.dmDriverExtra = sizeof (POINTL);
-  dev_mode.dmFields = DM_POSITION;
-  if (!EnumDisplaySettingsW
-      (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
-    GST_WARNING_OBJECT (self, "Couldn't enumerate display settings");
-    goto out;
-  }
-
-  self->desktop_coordinates.left = dev_mode.dmPosition.x;
-  self->desktop_coordinates.top = dev_mode.dmPosition.y;
-  self->desktop_coordinates.right =
-      dev_mode.dmPosition.x + dev_mode.dmPelsWidth;
-  self->desktop_coordinates.bottom =
-      dev_mode.dmPosition.y + dev_mode.dmPelsHeight;
-
-  self->cached_width =
-      self->desktop_coordinates.right - self->desktop_coordinates.left;
-  self->cached_height =
-      self->desktop_coordinates.bottom - self->desktop_coordinates.top;
-
-  GST_DEBUG_OBJECT (self,
-      "Desktop coordinates left:top:right:bottom = %ld:%ld:%ld:%ld (%dx%d)",
-      self->desktop_coordinates.left, self->desktop_coordinates.top,
-      self->desktop_coordinates.right, self->desktop_coordinates.bottom,
-      self->cached_width, self->cached_height);
-
-  g_object_get (self->device, "adapter-luid", &self->adapter_luid, nullptr);
-
-  self->output = output.Detach ();
-
-  ret = TRUE;
-
-out:
-  if (!ret)
-    gst_clear_object (&self->device);
-
-  G_OBJECT_CLASS (parent_class)->constructed (object);
-}
-
-static void
-gst_d3d11_screen_capture_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
-{
-  GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
-
-  switch (prop_id) {
-    case PROP_D3D11_DEVICE:
-      self->device = (GstD3D11Device *) g_value_dup_object (value);
-      break;
-    case PROP_MONITOR_HANDLE:
-      self->monitor_handle = (HMONITOR) g_value_get_pointer (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_d3d11_screen_capture_dispose (GObject * object)
-{
-  GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
-
-  GST_D3D11_CLEAR_COM (self->output);
-
-  if (self->dupl_obj) {
-    delete self->dupl_obj;
-    self->dupl_obj = nullptr;
-  }
-
-  gst_clear_object (&self->device);
-
-  G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_d3d11_screen_capture_finalize (GObject * object)
-{
-  GstD3D11ScreenCapture *self = GST_D3D11_SCREEN_CAPTURE (object);
-
-  DeleteCriticalSection (&self->lock);
-
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_d3d11_screen_capture_weak_ref_notify (gpointer data,
-    GstD3D11ScreenCapture * dupl)
-{
-  G_LOCK (dupl_list_lock);
-  dupl_list = g_list_remove (dupl_list, dupl);
-  G_UNLOCK (dupl_list_lock);
-}
-
-GstD3D11ScreenCapture *
-gst_d3d11_screen_capture_new (GstD3D11Device * device, HMONITOR monitor_handle)
-{
-  GstD3D11ScreenCapture *self = nullptr;
-  GList *iter;
-
-  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
-
-  /* Check if we have dup object corresponding to monitor_handle,
-   * and if there is already configured capture object, reuse it.
-   * This is because of the limitation of desktop duplication API
-   * (i.e., in a process, only one duplication object can exist).
-   * See also
-   * https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutput1-duplicateoutput#remarks
-   */
-  G_LOCK (dupl_list_lock);
-  for (iter = dupl_list; iter; iter = g_list_next (iter)) {
-    GstD3D11ScreenCapture *dupl = (GstD3D11ScreenCapture *) iter->data;
-
-    if (dupl->monitor_handle == monitor_handle) {
-      GST_DEBUG ("Found configured desktop dup object for monitor handle %p",
-          monitor_handle);
-      self = (GstD3D11ScreenCapture *) gst_object_ref (dupl);
-      break;
-    }
-  }
-
-  if (self) {
-    G_UNLOCK (dupl_list_lock);
-    return self;
-  }
-
-  self = (GstD3D11ScreenCapture *) g_object_new (GST_TYPE_D3D11_SCREEN_CAPTURE,
-      "d3d11device", device, "monitor-handle", monitor_handle, nullptr);
-
-  if (!self->device) {
-    GST_WARNING_OBJECT (self, "Couldn't configure desktop dup object");
-    gst_object_unref (self);
-    G_UNLOCK (dupl_list_lock);
-
-    return nullptr;
-  }
-
-  g_object_weak_ref (G_OBJECT (self),
-      (GWeakNotify) gst_d3d11_screen_capture_weak_ref_notify, nullptr);
-  dupl_list = g_list_append (dupl_list, self);
-
-  G_UNLOCK (dupl_list_lock);
-
-  return self;
 }
 
 GstFlowReturn
 gst_d3d11_screen_capture_prepare (GstD3D11ScreenCapture * capture)
 {
-  GstFlowReturn ret;
+  GstD3D11ScreenCaptureClass *klass;
 
   g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR);
-  g_return_val_if_fail (capture->device != nullptr, GST_FLOW_ERROR);
-
-  GstD3D11CSLockGuard lk (&capture->lock);
-
-  if (capture->prepared) {
-    GST_DEBUG_OBJECT (capture, "Already prepared");
-    return GST_FLOW_OK;
-  }
-
-  capture->dupl_obj = new D3D11DesktopDupObject ();
-  ret = capture->dupl_obj->Init (capture->device, capture->monitor_handle);
-  if (ret != GST_FLOW_OK) {
-    GST_WARNING_OBJECT (capture,
-        "Couldn't prepare capturing, %sexpected failure",
-        ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR ? "" : "un");
 
-    delete capture->dupl_obj;
-    capture->dupl_obj = nullptr;
+  klass = GST_D3D11_SCREEN_CAPTURE_GET_CLASS (capture);
+  g_assert (klass->prepare);
 
-    return ret;
-  }
-
-  capture->prepared = TRUE;
-
-  return GST_FLOW_OK;
+  return klass->prepare (capture);
 }
 
 gboolean
 gst_d3d11_screen_capture_get_size (GstD3D11ScreenCapture * capture,
     guint * width, guint * height)
 {
+  GstD3D11ScreenCaptureClass *klass;
+
   g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), FALSE);
   g_return_val_if_fail (width != nullptr, FALSE);
   g_return_val_if_fail (height != nullptr, FALSE);
 
-  GstD3D11CSLockGuard lk (&capture->lock);
-  *width = 0;
-  *height = 0;
-
-  if (capture->dupl_obj) {
-    capture->dupl_obj->GetSize (&capture->cached_width,
-        &capture->cached_height);
-  }
-
-  *width = capture->cached_width;
-  *height = capture->cached_height;
+  klass = GST_D3D11_SCREEN_CAPTURE_GET_CLASS (capture);
+  g_assert (klass->get_size);
 
-  return TRUE;
+  return klass->get_size (capture, width, height);
 }
 
 gboolean
 gst_d3d11_screen_capture_get_colorimetry (GstD3D11ScreenCapture * capture,
     GstVideoColorimetry * colorimetry)
 {
-  DXGI_COLOR_SPACE_TYPE dxgi_cs;
-  GstVideoInfo info;
+  GstD3D11ScreenCaptureClass *klass;
 
   g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), FALSE);
   g_return_val_if_fail (colorimetry != nullptr, FALSE);
 
-  dxgi_cs = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
+  klass = GST_D3D11_SCREEN_CAPTURE_GET_CLASS (capture);
+  g_assert (klass->get_colorimetry);
 
-  if (capture->output) {
-    ComPtr < IDXGIOutput6 > output;
-    HRESULT hr;
-    DXGI_OUTPUT_DESC1 desc;
-
-    hr = capture->output->QueryInterface (IID_PPV_ARGS (&output));
-    if (SUCCEEDED (hr))
-      hr = output->GetDesc1 (&desc);
-
-    if (SUCCEEDED (hr))
-      dxgi_cs = desc.ColorSpace;
-  }
-
-  gst_video_info_set_format (&info, GST_VIDEO_FORMAT_BGRA, 16, 16);
-  if (gst_video_info_apply_dxgi_color_space (dxgi_cs, &info)) {
-    *colorimetry = info.colorimetry;
-    return TRUE;
-  }
-
-  return FALSE;
+  return klass->get_colorimetry (capture, colorimetry);
 }
 
 GstFlowReturn
@@ -1879,87 +101,16 @@ gst_d3d11_screen_capture_do_capture (GstD3D11ScreenCapture * capture,
     ID3D11SamplerState * sampler, ID3D11BlendState * blend,
     D3D11_BOX * crop_box, gboolean draw_mouse)
 {
-  GstFlowReturn ret = GST_FLOW_OK;
-  gboolean shared_device = FALSE;
-  guint width, height;
+  GstD3D11ScreenCaptureClass *klass;
 
   g_return_val_if_fail (GST_IS_D3D11_SCREEN_CAPTURE (capture), GST_FLOW_ERROR);
   g_return_val_if_fail (texture != nullptr, GST_FLOW_ERROR);
 
-  if (device != capture->device) {
-    gint64 luid;
-
-    g_object_get (device, "adapter-luid", &luid, nullptr);
-    /* source element must hold d3d11 device for the same GPU already
-     * by DXGI duplication API design */
-    if (luid != capture->adapter_luid) {
-      GST_ERROR_OBJECT (capture, "Trying to capture from different device");
-      return GST_FLOW_ERROR;
-    }
-
-    shared_device = TRUE;
-  }
-
-  GstD3D11CSLockGuard lk (&capture->lock);
-  if (!capture->prepared)
-    ret = gst_d3d11_screen_capture_prepare (capture);
-
-  if (ret != GST_FLOW_OK) {
-    GST_WARNING_OBJECT (capture, "We are not prepared");
-    return ret;
-  }
-
-  gst_d3d11_screen_capture_get_size (capture, &width, &height);
-
-  if (crop_box->left > width || crop_box->right > width ||
-      crop_box->top > height || crop_box->bottom > height) {
-    GST_INFO_OBJECT (capture,
-        "Capture area (%u, %u, %u, %u) doesn't fit into screen size %ux%u",
-        crop_box->left, crop_box->right, crop_box->top,
-        crop_box->bottom, width, height);
-
-    return GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED;
-  }
-
-  gst_d3d11_device_lock (capture->device);
-  ret = capture->dupl_obj->Capture ();
-  if (ret != GST_FLOW_OK) {
-    gst_d3d11_device_unlock (capture->device);
-
-    delete capture->dupl_obj;
-    capture->dupl_obj = nullptr;
-    capture->prepared = FALSE;
-
-    if (ret == GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR) {
-      GST_WARNING_OBJECT (capture,
-          "Couldn't capture frame, but expected failure");
-    } else {
-      GST_ERROR_OBJECT (capture, "Unexpected failure during capture");
-    }
-
-    return ret;
-  }
-
-  GST_LOG_OBJECT (capture, "Capture done");
-  if (shared_device)
-    gst_d3d11_device_lock (device);
-
-  ret = capture->dupl_obj->CopyToTexture (device, texture, crop_box);
-  if (ret != GST_FLOW_OK)
-    goto out;
-
-  if (draw_mouse) {
-    capture->dupl_obj->DrawMouse (device,
-        rtv, vs, ps, layout, sampler, blend, crop_box);
-  }
-
-out:
-  if (shared_device)
-    gst_d3d11_device_unlock (device);
-
-  gst_d3d11_device_unlock (capture->device);
+  klass = GST_D3D11_SCREEN_CAPTURE_GET_CLASS (capture);
+  g_assert (klass->do_capture);
 
-  return ret;
+  return klass->do_capture (capture, device, texture, rtv,
+      vs, ps, layout, sampler, blend, crop_box, draw_mouse);
 }
 
 HRESULT
index f5be239..e4878e4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * GStreamer
- * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 
 G_BEGIN_DECLS
 
+#define GST_TYPE_D3D11_SCREEN_CAPTURE             (gst_d3d11_screen_capture_get_type())
+#define GST_D3D11_SCREEN_CAPTURE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_SCREEN_CAPTURE,GstD3D11ScreenCapture))
+#define GST_D3D11_SCREEN_CAPTURE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D11_SCREEN_CAPTURE,GstD3D11ScreenCaptureClass))
+#define GST_D3D11_SCREEN_CAPTURE_GET_CLASS(obj)   (GST_D3D11_SCREEN_CAPTURE_CLASS(G_OBJECT_GET_CLASS(obj)))
+#define GST_IS_D3D11_SCREEN_CAPTURE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_SCREEN_CAPTURE))
+#define GST_IS_D3D11_SCREEN_CAPTURE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_SCREEN_CAPTURE))
+#define GST_D3D11_SCREEN_CAPTURE_CAST(obj)        ((GstD3D11ScreenCapture*)(obj))
+
+typedef struct _GstD3D11ScreenCapture GstD3D11ScreenCapture;
+typedef struct _GstD3D11ScreenCaptureClass GstD3D11ScreenCaptureClass;
+
 #define GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR GST_FLOW_CUSTOM_SUCCESS
 #define GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED GST_FLOW_CUSTOM_SUCCESS_1
 #define GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED GST_FLOW_CUSTOM_ERROR
 
-#define GST_TYPE_D3D11_SCREEN_CAPTURE (gst_d3d11_screen_capture_get_type())
-G_DECLARE_FINAL_TYPE (GstD3D11ScreenCapture, gst_d3d11_screen_capture,
-    GST, D3D11_SCREEN_CAPTURE, GstObject);
+struct _GstD3D11ScreenCapture
+{
+  GstObject parent;
+};
+
+struct _GstD3D11ScreenCaptureClass
+{
+  GstObjectClass parent_class;
+
+  GstFlowReturn (*prepare) (GstD3D11ScreenCapture * capture);
 
-GstD3D11ScreenCapture * gst_d3d11_screen_capture_new (GstD3D11Device * device,
-                                                      HMONITOR monitor_handle);
+  gboolean      (*get_size) (GstD3D11ScreenCapture * capture,
+                             guint * width,
+                             guint * height);
+
+  gboolean      (*get_colorimetry) (GstD3D11ScreenCapture * capture,
+                                    GstVideoColorimetry * colorimetry);
+
+  GstFlowReturn (*do_capture)      (GstD3D11ScreenCapture * capture,
+                                    GstD3D11Device * device,
+                                    ID3D11Texture2D * texture,
+                                    ID3D11RenderTargetView * rtv,
+                                    ID3D11VertexShader * vs,
+                                    ID3D11PixelShader * ps,
+                                    ID3D11InputLayout * layout,
+                                    ID3D11SamplerState * sampler,
+                                    ID3D11BlendState * blend,
+                                    D3D11_BOX * crop_box,
+                                    gboolean draw_mouse);
+};
+
+GType           gst_d3d11_screen_capture_get_type (void);
 
 GstFlowReturn   gst_d3d11_screen_capture_prepare (GstD3D11ScreenCapture * capture);
 
@@ -71,5 +108,7 @@ HRESULT         gst_d3d11_screen_capture_find_nth_monitor (guint index,
                                                            IDXGIAdapter1 ** adapter,
                                                            IDXGIOutput ** output);
 
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstD3D11ScreenCapture, gst_object_unref)
+
 G_END_DECLS
 
index 246fb61..f8ceb1e 100644 (file)
@@ -37,7 +37,7 @@
 #endif
 
 #include "gstd3d11screencapturesrc.h"
-#include "gstd3d11screencapture.h"
+#include "gstd3d11dxgicapture.h"
 #include "gstd3d11pluginutils.h"
 #include <wrl.h>
 #include <string.h>
@@ -759,7 +759,7 @@ gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc)
     return FALSE;
   }
 
-  self->capture = gst_d3d11_screen_capture_new (self->device, monitor);
+  self->capture = gst_d3d11_dxgi_capture_new (self->device, monitor);
   if (!self->capture)
     goto error;
 
index 5bf7da8..8712c43 100644 (file)
@@ -56,10 +56,11 @@ if d3d11_winapi_app
 endif
 
 if d3d11_winapi_desktop
-  d3d11_sources += ['gstd3d11window_win32.cpp']
-  d3d11_sources += ['gstd3d11screencapture.cpp',
+  d3d11_sources += ['gstd3d11dxgicapture.cpp',
+                    'gstd3d11screencapture.cpp',
                     'gstd3d11screencapturedevice.cpp',
-                    'gstd3d11screencapturesrc.cpp']
+                    'gstd3d11screencapturesrc.cpp',
+                    'gstd3d11window_win32.cpp']
 
   # multimedia clock is desktop only API
   if winmm_lib.found() and cc.has_header('mmsystem.h')