d3d11: Add device provider for d3d11desktopdupsrc
authorSeungha Yang <seungha@centricular.com>
Tue, 5 Oct 2021 12:49:38 +0000 (21:49 +0900)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 11 Oct 2021 15:21:26 +0000 (15:21 +0000)
... and add support for multi-GPU/multi-monitor

By using newly added "monitor-handle" property, user can specify a
monitor to be captured via HMONITOR handle.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1673
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1103>

subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdup.cpp
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdup.h
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.h [new file with mode: 0644]
subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupsrc.cpp
subprojects/gst-plugins-bad/sys/d3d11/meson.build
subprojects/gst-plugins-bad/sys/d3d11/plugin.cpp

index 1e5590e..a77c4b4 100644 (file)
@@ -63,15 +63,6 @@ using namespace Microsoft::WRL;
 G_LOCK_DEFINE_STATIC (dupl_list_lock);
 static GList *dupl_list = nullptr;
 
-enum
-{
-  PROP_0,
-  PROP_D3D11_DEVICE,
-  PROP_OUTPUT_INDEX,
-};
-
-#define DEFAULT_MONITOR_INDEX -1
-
 /* Below implemenation were taken from Microsoft sample
  * https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/DXGIDesktopDuplication
  */
@@ -260,7 +251,7 @@ public:
   }
 
   GstFlowReturn
-  Init (GstD3D11Device * device, UINT monitor_index)
+  Init (GstD3D11Device * device, HMONITOR monitor)
   {
     GstFlowReturn ret;
     ID3D11Device *device_handle;
@@ -270,7 +261,7 @@ public:
     if (!InitShader (device))
       return GST_FLOW_ERROR;
 
-    ret = InitDupl (device, monitor_index);
+    ret = InitDupl (device, monitor);
     if (ret != GST_FLOW_OK)
       return ret;
 
@@ -661,34 +652,22 @@ private:
 
   /* Maybe returning expected error code depending on desktop status */
   GstFlowReturn
-  InitDupl (GstD3D11Device * device, UINT monitor_index)
+  InitDupl (GstD3D11Device * device, HMONITOR monitor)
   {
     ComPtr<ID3D11Device> d3d11_device;
-    ComPtr<IDXGIDevice> dxgi_device;
-    ComPtr<IDXGIAdapter> adapter;
+    ComPtr<IDXGIAdapter1> adapter;
     ComPtr<IDXGIOutput> output;
     ComPtr<IDXGIOutput1> output1;
 
     d3d11_device = gst_d3d11_device_get_device_handle (device);
 
-    HRESULT hr = d3d11_device.As (&dxgi_device);
+    HRESULT hr = gst_d3d11_desktop_dup_find_output_for_monitor (monitor,
+        &adapter, &output);
     if (!gst_d3d11_result (hr, device)) {
-      GST_ERROR ("Couldn't get IDXGIDevice interface, hr 0x%x", (guint) hr);
+      GST_ERROR ("Couldn't get adapter and output for monitor");
       return GST_FLOW_ERROR;
     }
 
-    hr = dxgi_device->GetParent (IID_PPV_ARGS (&adapter));
-    if (!gst_d3d11_result (hr, device)) {
-      return gst_d3d11_desktop_dup_return_from_hr (d3d11_device.Get(),
-          hr, SystemTransitionsExpectedErrors);
-    }
-
-    hr = adapter->EnumOutputs(monitor_index, &output);
-    if (!gst_d3d11_result (hr, device)) {
-      return gst_d3d11_desktop_dup_return_from_hr (d3d11_device.Get(),
-          hr, EnumOutputsExpectedErrors);
-    }
-
     hr = output.As (&output1);
     if (!gst_d3d11_result (hr, device)) {
       GST_ERROR ("Couldn't get IDXGIOutput1 interface, hr 0x%x", (guint) hr);
@@ -1469,6 +1448,15 @@ private:
 };
 /* *INDENT-ON* */
 
+enum
+{
+  PROP_0,
+  PROP_D3D11_DEVICE,
+  PROP_MONITOR_HANDLE,
+};
+
+#define DEFAULT_MONITOR_INDEX -1
+
 struct _GstD3D11DesktopDup
 {
   GstObject parent;
@@ -1479,8 +1467,7 @@ struct _GstD3D11DesktopDup
 
   D3D11DesktopDupObject *dupl_obj;
 
-  gboolean primary;
-  gint monitor_index;
+  HMONITOR monitor_handle;
   RECT desktop_coordinates;
   gboolean prepared;
 
@@ -1512,10 +1499,9 @@ gst_d3d11_desktop_dup_class_init (GstD3D11DesktopDupClass * klass)
           GST_TYPE_D3D11_DEVICE, (GParamFlags)
           (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
               G_PARAM_STATIC_STRINGS)));
-  g_object_class_install_property (gobject_class, PROP_OUTPUT_INDEX,
-      g_param_spec_int ("monitor-index", "Monitor Index",
-          "Zero-based index for monitor to capture (-1 = primary monitor)",
-          -1, G_MAXINT, DEFAULT_MONITOR_INDEX, (GParamFlags)
+  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)));
 }
@@ -1523,122 +1509,99 @@ gst_d3d11_desktop_dup_class_init (GstD3D11DesktopDupClass * klass)
 static void
 gst_d3d11_desktop_dup_init (GstD3D11DesktopDup * self)
 {
-  self->monitor_index = DEFAULT_MONITOR_INDEX;
   g_rec_mutex_init (&self->lock);
 
   memset (&self->desktop_coordinates, 0, sizeof (RECT));
 }
 
-static gboolean
-gst_d3d11_desktop_dup_get_monitor_size (GstD3D11DesktopDup * self,
-    HMONITOR hmonitor, RECT * size)
-{
-  MONITORINFOEX monitor_info;
-  DEVMODE dev_mode;
-
-  monitor_info.cbSize = sizeof (MONITORINFOEX);
-  if (!GetMonitorInfo (hmonitor, (LPMONITORINFO) & monitor_info)) {
-    GST_WARNING_OBJECT (self, "Couldn't get monitor info");
-    return FALSE;
-  }
-
-  dev_mode.dmSize = sizeof (DEVMODE);
-  dev_mode.dmDriverExtra = sizeof (POINTL);
-  dev_mode.dmFields = DM_POSITION;
-  if (!EnumDisplaySettings
-      (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
-    GST_WARNING_OBJECT (self, "Couldn't enumerate display settings");
-    return FALSE;
-  }
-
-  size->left = dev_mode.dmPosition.x;
-  size->top = dev_mode.dmPosition.y;
-  size->right = size->left + dev_mode.dmPelsWidth;
-  size->bottom = size->top + dev_mode.dmPelsHeight;
-
-  return TRUE;
-}
-
 static void
 gst_d3d11_desktop_dup_constructed (GObject * object)
 {
   GstD3D11DesktopDup *self = GST_D3D11_DESKTOP_DUP (object);
   /* *INDENT-OFF* */
   ComPtr<IDXGIDevice> dxgi_device;
-  ComPtr<IDXGIAdapter> adapter;
+  ComPtr<IDXGIAdapter1> adapter;
   ComPtr<IDXGIOutput> output;
   ComPtr<IDXGIOutput1> output1;
   /* *INDENT-ON* */
-  ID3D11Device *device_handle;
   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");
-    return;
+    goto out;
   }
 
-  device_handle = gst_d3d11_device_get_device_handle (self->device);
+  if (!self->monitor_handle) {
+    GST_WARNING_OBJECT (self, "Null monitor handle");
+    goto out;
+  }
 
-  /* Below code is just for getting resolution of IDXGIOutput (i.e., monitor)
-   * and we will setup IDXGIOutputDuplication interface later.
-   */
-  hr = device_handle->QueryInterface (IID_PPV_ARGS (&dxgi_device));
-  if (!gst_d3d11_result (hr, self->device))
+  hr = gst_d3d11_desktop_dup_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 = dxgi_device->GetParent (IID_PPV_ARGS (&adapter));
-  if (!gst_d3d11_result (hr, self->device))
+  hr = output.As (&output1);
+  if (!gst_d3d11_result (hr, self->device)) {
+    GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
     goto out;
+  }
 
-  if (self->monitor_index < 0) {
-    guint index = 0;
-    /* Enumerate all outputs to find primary monitor */
-    do {
-      hr = adapter->EnumOutputs (index, output.ReleaseAndGetAddressOf ());
-      if (!gst_d3d11_result (hr, self->device))
-        goto out;
-
-      output->GetDesc (&output_desc);
-      if (output_desc.DesktopCoordinates.left == 0 &&
-          output_desc.DesktopCoordinates.top == 0) {
-        GST_DEBUG_OBJECT (self, "Found primary output, index %d", index);
-        self->monitor_index = index;
-        self->primary = TRUE;
-        break;
-      }
-      index++;
-    } while (gst_d3d11_result (hr, self->device));
-  } else {
-    hr = adapter->EnumOutputs (self->monitor_index, &output);
-    if (!gst_d3d11_result (hr, self->device)) {
-      GST_WARNING_OBJECT (self, "No available output");
-      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;
+  }
 
-    output->GetDesc (&output_desc);
-    if (output_desc.DesktopCoordinates.left == 0 &&
-        output_desc.DesktopCoordinates.top == 0) {
-      GST_DEBUG_OBJECT (self, "We are primary output");
-      self->primary = TRUE;
-    }
+  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.As (&output1);
+  hr = output->GetDesc (&output_desc);
   if (!gst_d3d11_result (hr, self->device)) {
-    GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
+    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... */
-  if (!gst_d3d11_desktop_dup_get_monitor_size (self,
-          output_desc.Monitor, &self->desktop_coordinates)) {
+  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 =
@@ -1655,6 +1618,8 @@ gst_d3d11_desktop_dup_constructed (GObject * object)
 out:
   if (!ret)
     gst_clear_object (&self->device);
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
 }
 
 static void
@@ -1667,8 +1632,8 @@ gst_d3d11_desktop_dup_set_property (GObject * object, guint prop_id,
     case PROP_D3D11_DEVICE:
       self->device = (GstD3D11Device *) g_value_dup_object (value);
       break;
-    case PROP_OUTPUT_INDEX:
-      self->monitor_index = g_value_get_int (value);
+    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);
@@ -1710,15 +1675,15 @@ gst_d3d11_desktop_dup_weak_ref_notify (gpointer data, GstD3D11DesktopDup * dupl)
 }
 
 GstD3D11DesktopDup *
-gst_d3d11_desktop_dup_new (GstD3D11Device * device, gint monitor_index)
+gst_d3d11_desktop_dup_new (GstD3D11Device * device, HMONITOR monitor_handle)
 {
   GstD3D11DesktopDup *self = nullptr;
   GList *iter;
 
   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
 
-  /* Check if we have dup object corresponding to monitor_index, and if there is
-   * already configured capture object, reuse it.
+  /* 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
@@ -1728,10 +1693,9 @@ gst_d3d11_desktop_dup_new (GstD3D11Device * device, gint monitor_index)
   for (iter = dupl_list; iter; iter = g_list_next (iter)) {
     GstD3D11DesktopDup *dupl = (GstD3D11DesktopDup *) iter->data;
 
-    if (dupl->monitor_index == monitor_index ||
-        (monitor_index < 0 && dupl->primary)) {
-      GST_DEBUG ("Found configured desktop dup object for output index %d",
-          monitor_index);
+    if (dupl->monitor_handle == monitor_handle) {
+      GST_DEBUG ("Found configured desktop dup object for monitor handle %p",
+          monitor_handle);
       self = (GstD3D11DesktopDup *) gst_object_ref (dupl);
       break;
     }
@@ -1743,7 +1707,7 @@ gst_d3d11_desktop_dup_new (GstD3D11Device * device, gint monitor_index)
   }
 
   self = (GstD3D11DesktopDup *) g_object_new (GST_TYPE_D3D11_DESKTOP_DUP,
-      "d3d11device", device, "monitor-index", monitor_index, nullptr);
+      "d3d11device", device, "monitor-handle", monitor_handle, nullptr);
 
   if (!self->device) {
     GST_WARNING_OBJECT (self, "Couldn't configure desktop dup object");
@@ -1778,7 +1742,7 @@ gst_d3d11_desktop_dup_prepare (GstD3D11DesktopDup * desktop)
   }
 
   desktop->dupl_obj = new D3D11DesktopDupObject ();
-  ret = desktop->dupl_obj->Init (desktop->device, desktop->monitor_index);
+  ret = desktop->dupl_obj->Init (desktop->device, desktop->monitor_handle);
   if (ret != GST_FLOW_OK) {
     GST_WARNING_OBJECT (desktop,
         "Couldn't prepare capturing, %sexpected failure",
@@ -1885,3 +1849,154 @@ gst_d3d11_desktop_dup_capture (GstD3D11DesktopDup * desktop,
 
   return GST_FLOW_OK;
 }
+
+HRESULT
+gst_d3d11_desktop_dup_find_output_for_monitor (HMONITOR monitor,
+    IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
+{
+  ComPtr < IDXGIFactory1 > factory;
+  HRESULT hr = S_OK;
+
+  g_return_val_if_fail (monitor != nullptr, E_INVALIDARG);
+
+  hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
+  if (FAILED (hr))
+    return hr;
+
+  for (UINT adapter_idx = 0;; adapter_idx++) {
+    ComPtr < IDXGIAdapter1 > adapter_tmp;
+
+    hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
+    if (FAILED (hr))
+      break;
+
+    for (UINT output_idx = 0;; output_idx++) {
+      ComPtr < IDXGIOutput > output_tmp;
+      DXGI_OUTPUT_DESC desc;
+
+      hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
+      if (FAILED (hr))
+        break;
+
+      hr = output_tmp->GetDesc (&desc);
+      if (FAILED (hr))
+        continue;
+
+      if (desc.Monitor == monitor) {
+        if (adapter)
+          *adapter = adapter_tmp.Detach ();
+        if (output)
+          *output = output_tmp.Detach ();
+
+        return S_OK;
+      }
+    }
+  }
+
+  return E_FAIL;
+}
+
+HRESULT
+gst_d3d11_desktop_dup_find_primary_monitor (HMONITOR * monitor,
+    IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
+{
+  ComPtr < IDXGIFactory1 > factory;
+  HRESULT hr = S_OK;
+
+  hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
+  if (FAILED (hr))
+    return hr;
+
+  for (UINT adapter_idx = 0;; adapter_idx++) {
+    ComPtr < IDXGIAdapter1 > adapter_tmp;
+
+    hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
+    if (FAILED (hr))
+      break;
+
+    for (UINT output_idx = 0;; output_idx++) {
+      ComPtr < IDXGIOutput > output_tmp;
+      DXGI_OUTPUT_DESC desc;
+      MONITORINFOEXW minfo;
+
+      hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
+      if (FAILED (hr))
+        break;
+
+      hr = output_tmp->GetDesc (&desc);
+      if (FAILED (hr))
+        continue;
+
+      minfo.cbSize = sizeof (MONITORINFOEXW);
+      if (!GetMonitorInfoW (desc.Monitor, &minfo))
+        continue;
+
+      if ((minfo.dwFlags & MONITORINFOF_PRIMARY) != 0) {
+        if (monitor)
+          *monitor = desc.Monitor;
+        if (adapter)
+          *adapter = adapter_tmp.Detach ();
+        if (output)
+          *output = output_tmp.Detach ();
+
+        return S_OK;
+      }
+    }
+  }
+
+  return E_FAIL;
+}
+
+HRESULT
+gst_d3d11_desktop_dup_find_nth_monitor (guint index, HMONITOR * monitor,
+    IDXGIAdapter1 ** adapter, IDXGIOutput ** output)
+{
+  ComPtr < IDXGIFactory1 > factory;
+  HRESULT hr = S_OK;
+  guint num_found = 0;
+
+  hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
+  if (FAILED (hr))
+    return hr;
+
+  for (UINT adapter_idx = 0;; adapter_idx++) {
+    ComPtr < IDXGIAdapter1 > adapter_tmp;
+
+    hr = factory->EnumAdapters1 (adapter_idx, &adapter_tmp);
+    if (FAILED (hr))
+      break;
+
+    for (UINT output_idx = 0;; output_idx++) {
+      ComPtr < IDXGIOutput > output_tmp;
+      DXGI_OUTPUT_DESC desc;
+      MONITORINFOEXW minfo;
+
+      hr = adapter_tmp->EnumOutputs (output_idx, &output_tmp);
+      if (FAILED (hr))
+        break;
+
+      hr = output_tmp->GetDesc (&desc);
+      if (FAILED (hr))
+        continue;
+
+      minfo.cbSize = sizeof (MONITORINFOEXW);
+      if (!GetMonitorInfoW (desc.Monitor, &minfo))
+        continue;
+
+      if (num_found == index) {
+        if (monitor)
+          *monitor = desc.Monitor;
+        if (adapter)
+          *adapter = adapter_tmp.Detach ();
+        if (output)
+          *output = output_tmp.Detach ();
+
+        return S_OK;
+      }
+
+      num_found++;
+    }
+  }
+
+  return E_FAIL;
+}
index 5741c19..eeacb99 100644 (file)
@@ -36,7 +36,7 @@ G_DECLARE_FINAL_TYPE (GstD3D11DesktopDup, gst_d3d11_desktop_dup,
     GST, D3D11_DESKTOP_DUP, GstObject);
 
 GstD3D11DesktopDup *  gst_d3d11_desktop_dup_new (GstD3D11Device * device,
-                                                 gint output_index);
+                                                 HMONITOR monitor_handle);
 
 GstFlowReturn   gst_d3d11_desktop_dup_prepare (GstD3D11DesktopDup * desktop);
 
@@ -49,6 +49,19 @@ GstFlowReturn   gst_d3d11_desktop_dup_capture (GstD3D11DesktopDup * desktop,
                                                ID3D11RenderTargetView *rtv,
                                                gboolean draw_mouse);
 
+HRESULT         gst_d3d11_desktop_dup_find_output_for_monitor (HMONITOR monitor,
+                                                               IDXGIAdapter1 ** adapter,
+                                                               IDXGIOutput ** output);
+
+HRESULT         gst_d3d11_desktop_dup_find_primary_monitor (HMONITOR * monitor,
+                                                            IDXGIAdapter1 ** adapter,
+                                                            IDXGIOutput ** output);
+
+HRESULT         gst_d3d11_desktop_dup_find_nth_monitor (guint index,
+                                                        HMONITOR * monitor,
+                                                        IDXGIAdapter1 ** adapter,
+                                                        IDXGIOutput ** output);
+
 G_END_DECLS
 
 #endif /* __GST_D3D11_DESKTOP_DUP_H__ */
diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.cpp b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.cpp
new file mode 100644 (file)
index 0000000..db1c934
--- /dev/null
@@ -0,0 +1,447 @@
+/* GStreamer
+ * Copyright (C) 2021 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstd3d11desktopdupdevice.h"
+#include <gst/d3d11/gstd3d11.h>
+#include <gst/video/video.h>
+#include <wrl.h>
+#include <string.h>
+#include <string>
+#include <locale>
+#include <codecvt>
+
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+/* *INDENT-ON* */
+
+GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_desktop_dup_device_debug);
+#define GST_CAT_DEFAULT gst_d3d11_desktop_dup_device_debug
+
+static GstStaticCaps template_caps =
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
+    (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, "BGRA") ";"
+    GST_VIDEO_CAPS_MAKE ("BGRA"));
+
+enum
+{
+  PROP_0,
+  PROP_MONITOR_HANDLE,
+};
+
+struct _GstD3D11DesktopDupDevice
+{
+  GstDevice parent;
+
+  HMONITOR monitor_handle;
+};
+
+static void gst_d3d11_desktop_dup_device_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_d3d11_desktop_dup_device_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static GstElement *gst_d3d11_desktop_dup_device_create_element (GstDevice *
+    device, const gchar * name);
+
+G_DEFINE_TYPE (GstD3D11DesktopDupDevice,
+    gst_d3d11_desktop_dup_device, GST_TYPE_DEVICE);
+
+static void
+gst_d3d11_desktop_dup_device_class_init (GstD3D11DesktopDupDeviceClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
+
+  object_class->get_property = gst_d3d11_desktop_dup_device_get_property;
+  object_class->set_property = gst_d3d11_desktop_dup_device_set_property;
+
+  g_object_class_install_property (object_class, PROP_MONITOR_HANDLE,
+      g_param_spec_uint64 ("monitor-handle", "Monitor Handle",
+          "A HMONITOR handle", 0, G_MAXUINT64, 0,
+          (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE |
+              G_PARAM_CONSTRUCT_ONLY)));
+
+
+  dev_class->create_element = gst_d3d11_desktop_dup_device_create_element;
+}
+
+static void
+gst_d3d11_desktop_dup_device_init (GstD3D11DesktopDupDevice * self)
+{
+}
+
+static void
+gst_d3d11_desktop_dup_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstD3D11DesktopDupDevice *self = GST_D3D11_DESKTOP_DUP_DEVICE (object);
+
+  switch (prop_id) {
+    case PROP_MONITOR_HANDLE:
+      g_value_set_uint64 (value, (guint64) self->monitor_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_d3d11_desktop_dup_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstD3D11DesktopDupDevice *self = GST_D3D11_DESKTOP_DUP_DEVICE (object);
+
+  switch (prop_id) {
+    case PROP_MONITOR_HANDLE:
+      self->monitor_handle = (HMONITOR) g_value_get_uint64 (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstElement *
+gst_d3d11_desktop_dup_device_create_element (GstDevice * device,
+    const gchar * name)
+{
+  GstD3D11DesktopDupDevice *self = GST_D3D11_DESKTOP_DUP_DEVICE (device);
+  GstElement *elem;
+
+  elem = gst_element_factory_make ("d3d11desktopdupsrc", name);
+
+  g_object_set (elem, "monitor-handle", self->monitor_handle, NULL);
+
+  return elem;
+}
+
+struct _GstD3D11DesktopDupDeviceProvider
+{
+  GstDeviceProvider parent;
+};
+
+G_DEFINE_TYPE (GstD3D11DesktopDupDeviceProvider,
+    gst_d3d11_desktop_dup_device_provider, GST_TYPE_DEVICE_PROVIDER);
+
+static GList *gst_d3d11_desktop_dup_device_provider_probe (GstDeviceProvider *
+    provider);
+
+static void
+    gst_d3d11_desktop_dup_device_provider_class_init
+    (GstD3D11DesktopDupDeviceProviderClass * klass)
+{
+  GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS (klass);
+
+  provider_class->probe =
+      GST_DEBUG_FUNCPTR (gst_d3d11_desktop_dup_device_provider_probe);
+
+  gst_device_provider_class_set_static_metadata (provider_class,
+      "Direct3D11 Desktop Capture Device Provider",
+      "Source/Monitor", "List Direct3D11 desktop capture source devices",
+      "Seungha Yang <seungha@centricular.com>");
+}
+
+static void
+gst_d3d11_desktop_dup_device_provider_init (GstD3D11DesktopDupDeviceProvider *
+    self)
+{
+}
+
+static gboolean
+get_monitor_name (const MONITORINFOEXW * info,
+    DISPLAYCONFIG_TARGET_DEVICE_NAME * target)
+{
+  UINT32 num_path = 0;
+  UINT32 num_mode = 0;
+  LONG query_ret;
+
+  memset (target, 0, sizeof (DISPLAYCONFIG_TARGET_DEVICE_NAME));
+
+  query_ret = GetDisplayConfigBufferSizes (QDC_ONLY_ACTIVE_PATHS,
+      &num_path, &num_mode);
+  if (query_ret != ERROR_SUCCESS || num_path == 0 || num_mode == 0)
+    return FALSE;
+
+  DISPLAYCONFIG_PATH_INFO *path_infos = (DISPLAYCONFIG_PATH_INFO *)
+      g_alloca (num_path * sizeof (DISPLAYCONFIG_PATH_INFO));
+  DISPLAYCONFIG_MODE_INFO *mode_infos = (DISPLAYCONFIG_MODE_INFO *)
+      g_alloca (num_mode * sizeof (DISPLAYCONFIG_MODE_INFO));
+
+  query_ret = QueryDisplayConfig (QDC_ONLY_ACTIVE_PATHS, &num_path,
+      path_infos, &num_mode, mode_infos, nullptr);
+  if (query_ret != ERROR_SUCCESS)
+    return FALSE;
+
+  for (UINT32 i = 0; i < num_path; i++) {
+    DISPLAYCONFIG_PATH_INFO *p = &path_infos[i];
+    DISPLAYCONFIG_SOURCE_DEVICE_NAME source;
+
+    memset (&source, 0, sizeof (DISPLAYCONFIG_SOURCE_DEVICE_NAME));
+
+    source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+    source.header.size = sizeof (DISPLAYCONFIG_SOURCE_DEVICE_NAME);
+    source.header.adapterId = p->sourceInfo.adapterId;
+    source.header.id = p->sourceInfo.id;
+
+    query_ret = DisplayConfigGetDeviceInfo (&source.header);
+    if (query_ret != ERROR_SUCCESS)
+      continue;
+
+    if (wcscmp (info->szDevice, source.viewGdiDeviceName) != 0)
+      continue;
+
+    DISPLAYCONFIG_TARGET_DEVICE_NAME tmp;
+
+    memset (&tmp, 0, sizeof (DISPLAYCONFIG_TARGET_DEVICE_NAME));
+
+    tmp.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+    tmp.header.size = sizeof (DISPLAYCONFIG_TARGET_DEVICE_NAME);
+    tmp.header.adapterId = p->sourceInfo.adapterId;
+    tmp.header.id = p->targetInfo.id;
+
+    query_ret = DisplayConfigGetDeviceInfo (&tmp.header);
+    if (query_ret != ERROR_SUCCESS)
+      continue;
+
+    memcpy (target, &tmp, sizeof (DISPLAYCONFIG_TARGET_DEVICE_NAME));
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* XXX: please bump MinGW toolchain version,
+ * DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY defined in wingdi.h */
+typedef enum
+{
+  OUTPUT_TECH_OTHER                   = -1,
+  OUTPUT_TECH_HD15                    =  0,
+  OUTPUT_TECH_SVIDEO                  =  1,
+  OUTPUT_TECH_COMPOSITE_VIDEO         =  2,
+  OUTPUT_TECH_COMPONENT_VIDEO         =  3,
+  OUTPUT_TECH_DVI                     =  4,
+  OUTPUT_TECH_HDMI                    =  5,
+  OUTPUT_TECH_LVDS                    =  6,
+  OUTPUT_TECH_D_JPN                   =  8,
+  OUTPUT_TECH_SDI                     =  9,
+  OUTPUT_TECH_DISPLAYPORT_EXTERNAL    = 10,
+  OUTPUT_TECH_DISPLAYPORT_EMBEDDED    = 11,
+  OUTPUT_TECH_UDI_EXTERNAL            = 12,
+  OUTPUT_TECH_UDI_EMBEDDED            = 13,
+  OUTPUT_TECH_SDTVDONGLE              = 14,
+  OUTPUT_TECH_MIRACAST                = 15,
+  OUTPUT_TECH_INDIRECT_WIRED          = 16,
+  OUTPUT_TECH_INDIRECT_VIRTUAL        = 17,
+  OUTPUT_TECH_INTERNAL                = 0x80000000,
+  OUTPUT_TECH_FORCE_UINT32            = 0xFFFFFFFF
+} GST_OUTPUT_TECHNOLOGY;
+
+static const gchar *
+output_tech_to_string (GST_OUTPUT_TECHNOLOGY tech)
+{
+  switch (tech) {
+    case OUTPUT_TECH_HD15:
+      return "hd15";
+    case OUTPUT_TECH_SVIDEO:
+      return "svideo";
+    case OUTPUT_TECH_COMPOSITE_VIDEO:
+      return "composite-video";
+    case OUTPUT_TECH_DVI:
+      return "dvi";
+    case OUTPUT_TECH_HDMI:
+      return "hdmi";
+    case OUTPUT_TECH_LVDS:
+      return "lvds";
+    case OUTPUT_TECH_D_JPN:
+      return "d-jpn";
+    case OUTPUT_TECH_SDI:
+      return "sdi";
+    case OUTPUT_TECH_DISPLAYPORT_EXTERNAL:
+      return "displayport-external";
+    case OUTPUT_TECH_DISPLAYPORT_EMBEDDED:
+      return "displayport-internal";
+    case OUTPUT_TECH_UDI_EXTERNAL:
+      return "udi-external";
+    case OUTPUT_TECH_UDI_EMBEDDED:
+      return "udi-embedded";
+    case OUTPUT_TECH_SDTVDONGLE:
+      return "sdtv";
+    case OUTPUT_TECH_MIRACAST:
+      return "miracast";
+    case OUTPUT_TECH_INDIRECT_WIRED:
+      return "indirect-wired";
+    case OUTPUT_TECH_INDIRECT_VIRTUAL:
+      return "indirect-virtual";
+    case OUTPUT_TECH_INTERNAL:
+      return "internal";
+    default:
+      break;
+  }
+
+  return "unknown";
+}
+
+static GstDevice *
+create_device (const DXGI_ADAPTER_DESC * adapter_desc,
+    const DXGI_OUTPUT_DESC * output_desc,
+    const MONITORINFOEXW * minfo, const DEVMODEW * dev_mode,
+    const DISPLAYCONFIG_TARGET_DEVICE_NAME * target)
+{
+  GstCaps *caps;
+  gint width, height, left, top, right, bottom;
+  GstStructure *props;
+  std::wstring_convert < std::codecvt_utf8 < wchar_t >, wchar_t >converter;
+  std::string device_name;
+  std::string display_name;
+  std::string device_path;
+  std::string device_description;
+  const gchar *output_type;
+  gboolean primary = FALSE;
+  GstDevice *device;
+
+  left = (gint) dev_mode->dmPosition.x;
+  top = (gint) dev_mode->dmPosition.y;
+  width = dev_mode->dmPelsWidth;
+  height = dev_mode->dmPelsHeight;
+  right = left + width;
+  bottom = top + height;
+
+  caps = gst_static_caps_get (&template_caps);
+  caps = gst_caps_make_writable (caps);
+  gst_caps_set_simple (caps,
+      "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, nullptr);
+
+  device_name = converter.to_bytes (minfo->szDevice);
+  display_name = converter.to_bytes (target->monitorFriendlyDeviceName);
+  device_path = converter.to_bytes (target->monitorDevicePath);
+  device_description = converter.to_bytes (adapter_desc->Description);
+  output_type =
+      output_tech_to_string ((GST_OUTPUT_TECHNOLOGY) target->outputTechnology);
+  if ((minfo->dwFlags & MONITORINFOF_PRIMARY) != 0)
+    primary = TRUE;
+
+  props = gst_structure_new ("d3d11desktopdupdevice-proplist",
+      "device.api", G_TYPE_STRING, "d3d11",
+      "device.name", G_TYPE_STRING, GST_STR_NULL (device_name.c_str ()),
+      "device.path", G_TYPE_STRING, GST_STR_NULL (device_path.c_str ()),
+      "device.primary", G_TYPE_BOOLEAN, primary,
+      "device.type", G_TYPE_STRING, output_type,
+      "device.hmonitor", G_TYPE_UINT64, (guint64) output_desc->Monitor,
+      "device.adapter.luid", G_TYPE_INT64,
+      gst_d3d11_luid_to_int64 (&adapter_desc->AdapterLuid),
+      "device.adapter.description", G_TYPE_STRING,
+      GST_STR_NULL (device_description.c_str ()),
+      "desktop.coordinates.left", G_TYPE_INT,
+      (gint) output_desc->DesktopCoordinates.left,
+      "desktop.coordinates.top", G_TYPE_INT,
+      (gint) output_desc->DesktopCoordinates.top,
+      "desktop.coordinates.right", G_TYPE_INT,
+      (gint) output_desc->DesktopCoordinates.right,
+      "desktop.coordinates.bottom", G_TYPE_INT,
+      (gint) output_desc->DesktopCoordinates.bottom,
+      "display.coordinates.left", G_TYPE_INT, left,
+      "display.coordinates.top", G_TYPE_INT, top,
+      "display.coordinates.right", G_TYPE_INT, right,
+      "display.coordinates.bottom", G_TYPE_INT, bottom, nullptr);
+
+  device = (GstDevice *) g_object_new (GST_TYPE_D3D11_DESKTOP_DUP_DEVICE,
+      "display-name", display_name.c_str (), "caps", caps, "device-class",
+      "Source/Monitor", "properties", props, "monitor-handle",
+      (guint64) output_desc->Monitor, nullptr);
+
+  gst_caps_unref (caps);
+
+  return device;
+}
+
+static GList *
+gst_d3d11_desktop_dup_device_provider_probe (GstDeviceProvider * provider)
+{
+  GList *devices = nullptr;
+  ComPtr < IDXGIFactory1 > factory;
+  HRESULT hr = S_OK;
+
+  hr = CreateDXGIFactory1 (IID_PPV_ARGS (&factory));
+  if (FAILED (hr))
+    return nullptr;
+
+  for (UINT adapter_idx = 0;; adapter_idx++) {
+    ComPtr < IDXGIAdapter1 > adapter;
+    DXGI_ADAPTER_DESC adapter_desc;
+
+    hr = factory->EnumAdapters1 (adapter_idx, &adapter);
+    if (FAILED (hr))
+      break;
+
+    hr = adapter->GetDesc (&adapter_desc);
+    if (FAILED (hr))
+      continue;
+
+    for (UINT output_idx = 0;; output_idx++) {
+      ComPtr < IDXGIOutput > output;
+      ComPtr < IDXGIOutput1 > output1;
+      DXGI_OUTPUT_DESC desc;
+      MONITORINFOEXW minfo;
+      DEVMODEW dev_mode;
+      DISPLAYCONFIG_TARGET_DEVICE_NAME target;
+      GstDevice *dev;
+
+      hr = adapter->EnumOutputs (output_idx, &output);
+      if (FAILED (hr))
+        break;
+
+      hr = output.As (&output1);
+      if (FAILED (hr))
+        continue;
+
+      hr = output->GetDesc (&desc);
+      if (FAILED (hr))
+        continue;
+
+      minfo.cbSize = sizeof (MONITORINFOEXW);
+      if (!GetMonitorInfoW (desc.Monitor, &minfo))
+        continue;
+
+      dev_mode.dmSize = sizeof (DEVMODEW);
+      dev_mode.dmDriverExtra = sizeof (POINTL);
+      dev_mode.dmFields = DM_POSITION;
+      if (!EnumDisplaySettingsW (minfo.szDevice,
+              ENUM_CURRENT_SETTINGS, &dev_mode)) {
+        continue;
+      }
+
+      /* Human readable monitor name is not always availabe, if it's empty
+       * fill it with generic one */
+      if (!get_monitor_name (&minfo, &target) ||
+          wcslen (target.monitorFriendlyDeviceName) == 0) {
+        wcscpy (target.monitorFriendlyDeviceName, L"Generic PnP Monitor");
+      }
+
+      dev = create_device (&adapter_desc, &desc, &minfo, &dev_mode, &target);
+      devices = g_list_append (devices, dev);
+    }
+  }
+
+  return devices;
+}
diff --git a/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.h b/subprojects/gst-plugins-bad/sys/d3d11/gstd3d11desktopdupdevice.h
new file mode 100644 (file)
index 0000000..d919424
--- /dev/null
@@ -0,0 +1,36 @@
+/* GStreamer
+ * Copyright (C) 2021 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>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_DESKTOP_DUP_DEVICE (gst_d3d11_desktop_dup_device_get_type())
+G_DECLARE_FINAL_TYPE (GstD3D11DesktopDupDevice, gst_d3d11_desktop_dup_device,
+    GST, D3D11_DESKTOP_DUP_DEVICE, GstDevice);
+
+#define GST_TYPE_D3D11_DESKTOP_DUP_DEVICE_PROVIDER (gst_d3d11_desktop_dup_device_provider_get_type())
+G_DECLARE_FINAL_TYPE (GstD3D11DesktopDupDeviceProvider,
+    gst_d3d11_desktop_dup_device_provider,
+    GST, D3D11_DESKTOP_DUP_DEVICE_PROVIDER, GstDeviceProvider);
+
+G_END_DECLS
+
index f5aa087..6861fb2 100644 (file)
 #include "gstd3d11desktopdupsrc.h"
 #include "gstd3d11desktopdup.h"
 #include "gstd3d11pluginutils.h"
-
+#include <wrl.h>
 #include <string.h>
 
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+/* *INDENT-ON* */
+
 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_desktop_dup_debug);
 #define GST_CAT_DEFAULT gst_d3d11_desktop_dup_debug
 
@@ -49,6 +53,7 @@ enum
 {
   PROP_0,
   PROP_MONITOR_INDEX,
+  PROP_MONITOR_HANDLE,
   PROP_SHOW_CURSOR,
 
   PROP_LAST,
@@ -77,8 +82,9 @@ struct _GstD3D11DesktopDupSrc
 
   GstBufferPool *pool;
 
-  gint adapter;
+  gint64 adapter_luid;
   gint monitor_index;
+  HMONITOR monitor_handle;
   gboolean show_cursor;
 
   gboolean flushing;
@@ -138,6 +144,13 @@ gst_d3d11_desktop_dup_src_class_init (GstD3D11DesktopDupSrcClass * klass)
       (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
           G_PARAM_STATIC_STRINGS));
 
+  properties[PROP_MONITOR_HANDLE] =
+      g_param_spec_uint64 ("monitor-handle", "Monitor Handle",
+      "A HMONITOR handle of monitor to capture",
+      0, G_MAXUINT64, 0,
+      (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
+          G_PARAM_STATIC_STRINGS));
+
   properties[PROP_SHOW_CURSOR] =
       g_param_spec_boolean ("show-cursor",
       "Show Mouse Cursor", "Whether to show mouse cursor",
@@ -183,8 +196,6 @@ gst_d3d11_desktop_dup_src_init (GstD3D11DesktopDupSrc * self)
   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
 
-  /* FIXME: investigate non-zero adapter use case */
-  self->adapter = 0;
   self->monitor_index = DEFAULT_MONITOR_INDEX;
   self->show_cursor = DEFAULT_SHOW_CURSOR;
   self->min_latency = GST_CLOCK_TIME_NONE;
@@ -212,6 +223,9 @@ gst_d3d11_desktop_dup_src_set_property (GObject * object, guint prop_id,
     case PROP_MONITOR_INDEX:
       self->monitor_index = g_value_get_int (value);
       break;
+    case PROP_MONITOR_HANDLE:
+      self->monitor_handle = (HMONITOR) g_value_get_uint64 (value);
+      break;
     case PROP_SHOW_CURSOR:
       self->show_cursor = g_value_get_boolean (value);
       break;
@@ -231,6 +245,9 @@ gst_d3d11_desktop_dup_src_get_property (GObject * object, guint prop_id,
     case PROP_MONITOR_INDEX:
       g_value_set_int (value, self->monitor_index);
       break;
+    case PROP_MONITOR_HANDLE:
+      g_value_set_uint64 (value, (guint64) self->monitor_handle);
+      break;
     case PROP_SHOW_CURSOR:
       g_value_set_boolean (value, self->show_cursor);
       break;
@@ -246,7 +263,8 @@ gst_d3d11_desktop_dup_src_set_context (GstElement * element,
 {
   GstD3D11DesktopDupSrc *self = GST_D3D11_DESKTOP_DUP_SRC (element);
 
-  gst_d3d11_handle_set_context (element, context, self->adapter, &self->device);
+  gst_d3d11_handle_set_context_for_adapter_luid (element,
+      context, self->adapter_luid, &self->device);
 
   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
 }
@@ -487,20 +505,42 @@ gst_d3d11_desktop_dup_src_start (GstBaseSrc * bsrc)
 {
   GstD3D11DesktopDupSrc *self = GST_D3D11_DESKTOP_DUP_SRC (bsrc);
   GstFlowReturn ret;
+  HMONITOR monitor = self->monitor_handle;
+  ComPtr < IDXGIAdapter1 > adapter;
+  DXGI_ADAPTER_DESC desc;
+  HRESULT hr;
+
+  if (monitor) {
+    hr = gst_d3d11_desktop_dup_find_output_for_monitor (monitor,
+        &adapter, nullptr);
+  } else if (self->monitor_index < 0) {
+    hr = gst_d3d11_desktop_dup_find_primary_monitor (&monitor,
+        &adapter, nullptr);
+  } else {
+    hr = gst_d3d11_desktop_dup_find_nth_monitor (self->monitor_index,
+        &monitor, &adapter, nullptr);
+  }
 
-  /* FIXME: this element will use only the first adapter, but
-   * this might cause issue in case of multi-gpu environment and
-   * some monitor is connected to non-default adapter */
-  if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), self->adapter,
-          &self->device)) {
+  if (FAILED (hr))
+    goto error;
+
+  hr = adapter->GetDesc (&desc);
+  if (FAILED (hr))
+    goto error;
+
+  self->adapter_luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
+  gst_clear_object (&self->device);
+
+  if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT_CAST (self),
+          self->adapter_luid, &self->device)) {
     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
-        ("D3D11 device with adapter index %d is unavailble", self->adapter),
-        (NULL));
+        ("D3D11 device for LUID %" G_GINT64_FORMAT " is unavailble",
+            self->adapter_luid), (nullptr));
 
     return FALSE;
   }
 
-  self->dupl = gst_d3d11_desktop_dup_new (self->device, self->monitor_index);
+  self->dupl = gst_d3d11_desktop_dup_new (self->device, monitor);
   if (!self->dupl)
     goto error;
 
@@ -524,16 +564,18 @@ gst_d3d11_desktop_dup_src_start (GstBaseSrc * bsrc)
 error:
   {
     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
-        ("Failed to prepare duplication for output index %d",
-            self->monitor_index), (NULL));
+        ("Failed to prepare duplication with given configuration, "
+            "monitor-index: %d, monitor-handle: %p",
+            self->monitor_index, self->monitor_handle), (nullptr));
+    return FALSE;
   }
-  return FALSE;
 
 unsupported:
   {
     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
-        ("Failed to prepare duplication for output index %d",
-            self->monitor_index),
+        ("Failed to prepare duplication with given configuration, "
+            "monitor-index: %d, monitor-handle: %p",
+            self->monitor_index, self->monitor_handle),
         ("Try run the application on the integrated GPU"));
     return FALSE;
   }
index 43f213d..d7ddf66 100644 (file)
@@ -84,7 +84,9 @@ if d3d11_winapi_desktop
     # Desktop Duplication API is unavailable for UWP
     # and MinGW is not supported due to some missing headers
     extra_args += ['-DHAVE_DXGI_DESKTOP_DUP']
-    d3d11_sources += ['gstd3d11desktopdup.cpp', 'gstd3d11desktopdupsrc.cpp']
+    d3d11_sources += ['gstd3d11desktopdup.cpp',
+                      'gstd3d11desktopdupdevice.cpp',
+                      'gstd3d11desktopdupsrc.cpp']
     message('Enable D3D11 Desktop Duplication API')
   endif
   # multimedia clock is desktop only API
index bc41e38..0a8ffdf 100644 (file)
@@ -40,6 +40,7 @@
 #endif
 #ifdef HAVE_DXGI_DESKTOP_DUP
 #include "gstd3d11desktopdupsrc.h"
+#include "gstd3d11desktopdupdevice.h"
 #endif
 #ifdef HAVE_D3D11_VIDEO_PROC
 #include "gstd3d11deinterlace.h"
@@ -68,6 +69,7 @@ GST_DEBUG_CATEGORY (gst_d3d11_av1_dec_debug);
 
 #ifdef HAVE_DXGI_DESKTOP_DUP
 GST_DEBUG_CATEGORY (gst_d3d11_desktop_dup_debug);
+GST_DEBUG_CATEGORY (gst_d3d11_desktop_dup_device_debug);
 #endif
 
 #ifdef HAVE_D3D11_VIDEO_PROC
@@ -213,8 +215,14 @@ plugin_init (GstPlugin * plugin)
   if (gst_d3d11_is_windows_8_or_greater ()) {
     GST_DEBUG_CATEGORY_INIT (gst_d3d11_desktop_dup_debug,
         "d3d11desktopdupsrc", 0, "d3d11desktopdupsrc");
+    GST_DEBUG_CATEGORY_INIT (gst_d3d11_desktop_dup_device_debug,
+        "d3d11desktopdupdevice", 0, "d3d11desktopdupdevice");
+
     gst_element_register (plugin,
         "d3d11desktopdupsrc", GST_RANK_NONE, GST_TYPE_D3D11_DESKTOP_DUP_SRC);
+    gst_device_provider_register (plugin,
+        "d3d11desktopdupdeviceprovider", GST_RANK_PRIMARY,
+        GST_TYPE_D3D11_DESKTOP_DUP_DEVICE_PROVIDER);
   }
 #endif