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
*/
}
GstFlowReturn
- Init (GstD3D11Device * device, UINT monitor_index)
+ Init (GstD3D11Device * device, HMONITOR monitor)
{
GstFlowReturn ret;
ID3D11Device *device_handle;
if (!InitShader (device))
return GST_FLOW_ERROR;
- ret = InitDupl (device, monitor_index);
+ ret = InitDupl (device, monitor);
if (ret != GST_FLOW_OK)
return ret;
/* 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);
};
/* *INDENT-ON* */
+enum
+{
+ PROP_0,
+ PROP_D3D11_DEVICE,
+ PROP_MONITOR_HANDLE,
+};
+
+#define DEFAULT_MONITOR_INDEX -1
+
struct _GstD3D11DesktopDup
{
GstObject parent;
D3D11DesktopDupObject *dupl_obj;
- gboolean primary;
- gint monitor_index;
+ HMONITOR monitor_handle;
RECT desktop_coordinates;
gboolean prepared;
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)));
}
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 =
out:
if (!ret)
gst_clear_object (&self->device);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
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);
}
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
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;
}
}
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");
}
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",
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;
+}
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);
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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
+
#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
{
PROP_0,
PROP_MONITOR_INDEX,
+ PROP_MONITOR_HANDLE,
PROP_SHOW_CURSOR,
PROP_LAST,
GstBufferPool *pool;
- gint adapter;
+ gint64 adapter_luid;
gint monitor_index;
+ HMONITOR monitor_handle;
gboolean show_cursor;
gboolean flushing;
(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",
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;
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;
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;
{
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);
}
{
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;
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;
}
# 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
#endif
#ifdef HAVE_DXGI_DESKTOP_DUP
#include "gstd3d11desktopdupsrc.h"
+#include "gstd3d11desktopdupdevice.h"
#endif
#ifdef HAVE_D3D11_VIDEO_PROC
#include "gstd3d11deinterlace.h"
#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
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