From 239a679fbf35a910be84726c4aa26f6ec12ec28f Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sat, 5 Jul 2025 03:10:51 +0900 Subject: [PATCH] d3d12screencapture: Add support for monitor add/remove in device provider Update device list on WM_DISPLAYCHANGE event Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4521 Part-of: --- .../sys/d3d12/gstd3d12screencapturedevice.cpp | 259 +++++++++++++++++- 1 file changed, 257 insertions(+), 2 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12screencapturedevice.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12screencapturedevice.cpp index 9e57dc1cb2..f065653875 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12screencapturedevice.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12screencapturedevice.cpp @@ -23,19 +23,35 @@ #include "gstd3d12screencapturedevice.h" #include "gstd3d12screencapture.h" +#include "gstd3d12pluginutils.h" #include #include #include #include #include #include +#include +#include /* *INDENT-OFF* */ using namespace Microsoft::WRL; /* *INDENT-ON* */ -GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_screen_capture_debug); -#define GST_CAT_DEFAULT gst_d3d12_screen_capture_debug +#ifndef GST_DISABLE_GST_DEBUG +#define GST_CAT_DEFAULT ensure_debug_category() +static GstDebugCategory * +ensure_debug_category (void) +{ + static GstDebugCategory *cat = nullptr; + + GST_D3D12_CALL_ONCE_BEGIN { + cat = _gst_debug_category_new ("d3d12screencapture", + 0, "d3d12screencapture"); + } GST_D3D12_CALL_ONCE_END; + + return cat; +} +#endif static GstStaticCaps template_caps = GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES @@ -136,6 +152,70 @@ gst_d3d12_screen_capture_device_create_element (GstDevice * device, return elem; } +static void gst_d3d12_screen_capture_device_provider_update (gpointer provider); +static gpointer +gst_d3d12_screen_capture_device_provider_monitor_thread (gpointer user_data); + +/* *INDENT-OFF* */ +class MonitorNotificationManager +{ +public: + MonitorNotificationManager (const MonitorNotificationManager &) = delete; + MonitorNotificationManager& operator= (const MonitorNotificationManager &) = delete; + static MonitorNotificationManager * Inst() + { + static MonitorNotificationManager *inst = nullptr; + GST_D3D12_CALL_ONCE_BEGIN { + inst = new MonitorNotificationManager (); + } GST_D3D12_CALL_ONCE_END; + + return inst; + } + + void + Register (GstD3D12ScreenCaptureDeviceProvider* client) + { + std::lock_guard lk(lock_); + clients_.insert(client); + } + + void + Unregister (GstD3D12ScreenCaptureDeviceProvider* client) + { + std::lock_guard lk(lock_); + clients_.erase(client); + } + + void OnDisplayChange () + { + std::vector clients; + { + std::lock_guard lk (lock_); + for (auto it : clients_) + clients.push_back (gst_object_ref (it)); + } + + for (auto it : clients) { + gst_d3d12_screen_capture_device_provider_update (it); + gst_object_unref (it); + } + } + +private: + MonitorNotificationManager() + { + thread_ = g_thread_new ("GstD3D12ScreenCaptureMonitor", + (GThreadFunc) gst_d3d12_screen_capture_device_provider_monitor_thread, + nullptr); + } + +private: + std::mutex lock_; + std::set clients_; + GThread *thread_; +}; +/* *INDENT-ON* */ + struct _GstD3D12ScreenCaptureDeviceProvider { GstDeviceProvider parent; @@ -144,17 +224,29 @@ struct _GstD3D12ScreenCaptureDeviceProvider G_DEFINE_TYPE (GstD3D12ScreenCaptureDeviceProvider, gst_d3d12_screen_capture_device_provider, GST_TYPE_DEVICE_PROVIDER); +static void gst_d3d12_screen_capture_device_provider_dispose (GObject * object); static GList *gst_d3d12_screen_capture_device_provider_probe (GstDeviceProvider * provider); +static gboolean +gst_d3d12_screen_capture_device_provider_start (GstDeviceProvider * provider); +static void +gst_d3d12_screen_capture_device_provider_stop (GstDeviceProvider * provider); static void gst_d3d12_screen_capture_device_provider_class_init (GstD3D12ScreenCaptureDeviceProviderClass * klass) { + auto object_class = G_OBJECT_CLASS (klass); auto provider_class = GST_DEVICE_PROVIDER_CLASS (klass); + object_class->dispose = gst_d3d12_screen_capture_device_provider_dispose; + provider_class->probe = GST_DEBUG_FUNCPTR (gst_d3d12_screen_capture_device_provider_probe); + provider_class->start = + GST_DEBUG_FUNCPTR (gst_d3d12_screen_capture_device_provider_start); + provider_class->stop = + GST_DEBUG_FUNCPTR (gst_d3d12_screen_capture_device_provider_stop); gst_device_provider_class_set_static_metadata (provider_class, "Direct3D12 Screen Capture Device Provider", @@ -168,6 +260,17 @@ static void { } +static void +gst_d3d12_screen_capture_device_provider_dispose (GObject * object) +{ + auto self = GST_D3D12_SCREEN_CAPTURE_DEVICE_PROVIDER (object); + + MonitorNotificationManager::Inst ()->Unregister (self); + + G_OBJECT_CLASS + (gst_d3d12_screen_capture_device_provider_parent_class)->dispose (object); +} + static gboolean get_monitor_name (const MONITORINFOEXW * info, DISPLAYCONFIG_TARGET_DEVICE_NAME * target) @@ -454,3 +557,155 @@ gst_d3d12_screen_capture_device_provider_probe (GstDeviceProvider * provider) return devices; } + +static gboolean +gst_d3d12_screen_capture_device_is_in_list (GList * list, GstDevice * device) +{ + GList *iter; + GstStructure *s; + gboolean found = FALSE; + + s = gst_device_get_properties (device); + g_assert (s); + + for (iter = list; iter; iter = g_list_next (iter)) { + GstStructure *other_s; + + other_s = gst_device_get_properties (GST_DEVICE (iter->data)); + g_assert (other_s); + + found = gst_structure_is_equal (s, other_s); + + gst_structure_free (other_s); + if (found) + break; + } + + gst_structure_free (s); + + return found; +} + +static void +gst_d3d12_screen_capture_device_provider_update (gpointer self) +{ + auto provider = GST_DEVICE_PROVIDER_CAST (self); + GList *prev_devices = nullptr; + GList *new_devices = nullptr; + GList *to_add = nullptr; + GList *to_remove = nullptr; + GList *iter; + + GST_DEBUG_OBJECT (self, "Device updated"); + + GST_OBJECT_LOCK (provider); + prev_devices = g_list_copy_deep (provider->devices, + (GCopyFunc) gst_object_ref, nullptr); + GST_OBJECT_UNLOCK (provider); + + new_devices = gst_d3d12_screen_capture_device_provider_probe (provider); + + /* Ownership of GstDevice for gst_device_provider_device_add() + * and gst_device_provider_device_remove() is a bit complicated. + * Remove floating reference here for things to be clear */ + for (iter = new_devices; iter; iter = g_list_next (iter)) + gst_object_ref_sink (iter->data); + + /* Check newly added devices */ + for (iter = new_devices; iter; iter = g_list_next (iter)) { + if (!gst_d3d12_screen_capture_device_is_in_list (prev_devices, + GST_DEVICE (iter->data))) { + to_add = g_list_prepend (to_add, gst_object_ref (iter->data)); + } + } + + /* Check removed device */ + for (iter = prev_devices; iter; iter = g_list_next (iter)) { + if (!gst_d3d12_screen_capture_device_is_in_list (new_devices, + GST_DEVICE (iter->data))) { + to_remove = g_list_prepend (to_remove, gst_object_ref (iter->data)); + } + } + + for (iter = to_remove; iter; iter = g_list_next (iter)) + gst_device_provider_device_remove (provider, GST_DEVICE (iter->data)); + + for (iter = to_add; iter; iter = g_list_next (iter)) + gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); + + if (prev_devices) + g_list_free_full (prev_devices, (GDestroyNotify) gst_object_unref); + + if (to_add) + g_list_free_full (to_add, (GDestroyNotify) gst_object_unref); + + if (to_remove) + g_list_free_full (to_remove, (GDestroyNotify) gst_object_unref); +} + +static gboolean +gst_d3d12_screen_capture_device_provider_start (GstDeviceProvider * provider) +{ + auto self = GST_D3D12_SCREEN_CAPTURE_DEVICE_PROVIDER (provider); + + auto devices = gst_d3d12_screen_capture_device_provider_probe (provider); + if (devices) { + GList *iter; + for (iter = devices; iter; iter = g_list_next (iter)) + gst_device_provider_device_add (provider, GST_DEVICE (iter->data)); + + g_list_free (devices); + } + + MonitorNotificationManager::Inst ()->Register (self); + + return TRUE; +} + +static void +gst_d3d12_screen_capture_device_provider_stop (GstDeviceProvider * provider) +{ + auto self = GST_D3D12_SCREEN_CAPTURE_DEVICE_PROVIDER (provider); + + MonitorNotificationManager::Inst ()->Unregister (self); +} + +static LRESULT CALLBACK +gst_d3d12_screen_capture_device_provider_wnd_proc (HWND hwnd, + UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (msg == WM_DISPLAYCHANGE) { + MonitorNotificationManager::Inst ()->OnDisplayChange (); + return 0; + } + + return DefWindowProcW (hwnd, msg, wparam, lparam); +} + +static gpointer +gst_d3d12_screen_capture_device_provider_monitor_thread (gpointer user_data) +{ + static const wchar_t HWND_CLASS_NAME[] = + L"GstD3D12ScreenCaptureDeviceProvider"; + + GST_D3D12_CALL_ONCE_BEGIN { + WNDCLASSW wc = { }; + wc.lpfnWndProc = gst_d3d12_screen_capture_device_provider_wnd_proc; + wc.hInstance = GetModuleHandle (nullptr); + wc.lpszClassName = HWND_CLASS_NAME; + RegisterClassW (&wc); + } GST_D3D12_CALL_ONCE_END; + + auto hwnd = CreateWindowExW (0, HWND_CLASS_NAME, L"", 0, + 0, 0, 0, 0, nullptr, nullptr, GetModuleHandle (nullptr), nullptr); + + MSG msg; + while (GetMessage (&msg, nullptr, 0, 0)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + CloseWindow (hwnd); + + return nullptr; +}