From e4daa2ef43106a57645e488c17c7207a316e35b4 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Mon, 30 Dec 2019 18:58:59 +0900 Subject: [PATCH] d3d11: Add support for Universal Windows Platform Initial UWP support via new window (CoreWindow and SwapChainPanel) implementation. --- sys/d3d11/gstd3d11_fwd.h | 2 +- sys/d3d11/gstd3d11device.c | 119 +- sys/d3d11/gstd3d11device.h | 14 + sys/d3d11/gstd3d11utils.c | 6 +- sys/d3d11/gstd3d11videosink.c | 116 +- sys/d3d11/gstd3d11window.c | 1598 --------------------------- sys/d3d11/gstd3d11window.cpp | 940 ++++++++++++++++ sys/d3d11/gstd3d11window.h | 149 +-- sys/d3d11/gstd3d11window_corewindow.cpp | 364 ++++++ sys/d3d11/gstd3d11window_corewindow.h | 40 + sys/d3d11/gstd3d11window_swapchainpanel.cpp | 499 +++++++++ sys/d3d11/gstd3d11window_swapchainpanel.h | 41 + sys/d3d11/gstd3d11window_win32.cpp | 924 ++++++++++++++++ sys/d3d11/gstd3d11window_win32.h | 40 + sys/d3d11/meson.build | 45 +- sys/d3d11/plugin.c | 5 +- 16 files changed, 3184 insertions(+), 1718 deletions(-) delete mode 100644 sys/d3d11/gstd3d11window.c create mode 100644 sys/d3d11/gstd3d11window.cpp create mode 100644 sys/d3d11/gstd3d11window_corewindow.cpp create mode 100644 sys/d3d11/gstd3d11window_corewindow.h create mode 100644 sys/d3d11/gstd3d11window_swapchainpanel.cpp create mode 100644 sys/d3d11/gstd3d11window_swapchainpanel.h create mode 100644 sys/d3d11/gstd3d11window_win32.cpp create mode 100644 sys/d3d11/gstd3d11window_win32.h diff --git a/sys/d3d11/gstd3d11_fwd.h b/sys/d3d11/gstd3d11_fwd.h index 6d8c372..1a1dc08 100644 --- a/sys/d3d11/gstd3d11_fwd.h +++ b/sys/d3d11/gstd3d11_fwd.h @@ -21,7 +21,7 @@ #define __GST_D3D11_FWD_H__ #include -#include "d3d11config.h" +#include "gstd3d11config.h" #ifndef INITGUID #include diff --git a/sys/d3d11/gstd3d11device.c b/sys/d3d11/gstd3d11device.c index 7f09631..56553d5 100644 --- a/sys/d3d11/gstd3d11device.c +++ b/sys/d3d11/gstd3d11device.c @@ -21,8 +21,6 @@ #include "config.h" #endif -#include "d3d11config.h" - #include "gstd3d11device.h" #include "gstd3d11utils.h" #include "gmodule.h" @@ -200,20 +198,39 @@ static gboolean gst_d3d11_device_enable_dxgi_debug (void) { static volatile gsize _init = 0; + gboolean ret = FALSE; /* If all below libraries are unavailable, d3d11 device would fail with * D3D11_CREATE_DEVICE_DEBUG flag */ if (g_once_init_enter (&_init)) { +#if (!GST_D3D11_WINAPI_ONLY_APP) dxgi_debug_module = g_module_open ("dxgidebug.dll", G_MODULE_BIND_LAZY); if (dxgi_debug_module) g_module_symbol (dxgi_debug_module, "DXGIGetDebugInterface", (gpointer *) & GstDXGIGetDebugInterface); - + ret = ! !GstDXGIGetDebugInterface; +#elif (DXGI_HEADER_VERSION >= 3) + ret = TRUE; +#endif g_once_init_leave (&_init, 1); } - return ! !GstDXGIGetDebugInterface; + return ret; +} + +static HRESULT +gst_d3d11_device_dxgi_get_device_interface (REFIID riid, void **debug) +{ +#if (!GST_D3D11_WINAPI_ONLY_APP) + if (GstDXGIGetDebugInterface) { + return GstDXGIGetDebugInterface (riid, debug); + } +#elif (DXGI_HEADER_VERSION >= 3) + return DXGIGetDebugInterface1 (0, riid, debug); +#endif + + return E_NOINTERFACE; } static inline GstDebugLevel @@ -374,14 +391,15 @@ gst_d3d11_device_constructed (GObject * object) GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self, "dxgi debug library was loaded"); - hr = GstDXGIGetDebugInterface (&IID_IDXGIDebug, (void **) &debug); + hr = gst_d3d11_device_dxgi_get_device_interface (&IID_IDXGIDebug, + (void **) &debug); if (SUCCEEDED (hr)) { GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self, "IDXGIDebug interface available"); priv->dxgi_debug = debug; - hr = GstDXGIGetDebugInterface (&IID_IDXGIInfoQueue, + hr = gst_d3d11_device_dxgi_get_device_interface (&IID_IDXGIInfoQueue, (void **) &info_queue); if (SUCCEEDED (hr)) { GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self, @@ -762,7 +780,7 @@ gst_d3d11_device_create_swap_chain (GstD3D11Device * device, gst_d3d11_device_unlock (device); if (!gst_d3d11_result (hr, device)) { - GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x", + GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x", (guint) hr); swap_chain = NULL; } @@ -771,6 +789,7 @@ gst_d3d11_device_create_swap_chain (GstD3D11Device * device, } #if (DXGI_HEADER_VERSION >= 2) +#if (!GST_D3D11_WINAPI_ONLY_APP) /** * gst_d3d11_device_create_swap_chain_for_hwnd: * @device: a #GstD3D11Device @@ -807,14 +826,96 @@ gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Device * device, gst_d3d11_device_unlock (device); if (!gst_d3d11_result (hr, device)) { - GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x", + GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x", (guint) hr); swap_chain = NULL; } return swap_chain; } -#endif +#endif /* GST_D3D11_WINAPI_ONLY_APP */ + +#if GST_D3D11_WINAPI_ONLY_APP +/** + * gst_d3d11_device_create_swap_chain_for_core_window: + * @device: a #GstD3D11Device + * @core_window: CoreWindow handle + * @desc: a DXGI_SWAP_CHAIN_DESC1 structure for swapchain + * @output: (nullable): a IDXGIOutput interface for the output to restrict content to + * + * Create a IDXGISwapChain1 object. Caller must release returned swap chain object + * via IDXGISwapChain1_Release() + * + * Returns: (transfer full) (nullable): a new IDXGISwapChain1 or %NULL + * when failed to create swap chain with given @desc + */ +IDXGISwapChain1 * +gst_d3d11_device_create_swap_chain_for_core_window (GstD3D11Device * device, + guintptr core_window, const DXGI_SWAP_CHAIN_DESC1 * desc, + IDXGIOutput * output) +{ + GstD3D11DevicePrivate *priv; + IDXGISwapChain1 *swap_chain = NULL; + HRESULT hr; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + + priv = device->priv; + + gst_d3d11_device_lock (device); + hr = IDXGIFactory2_CreateSwapChainForCoreWindow ((IDXGIFactory2 *) + priv->factory, (IUnknown *) priv->device, (IUnknown *) core_window, desc, + output, &swap_chain); + gst_d3d11_device_unlock (device); + + if (!gst_d3d11_result (hr, device)) { + GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x", + (guint) hr); + swap_chain = NULL; + } + + return swap_chain; +} + +/** + * gst_d3d11_device_create_swap_chain_for_composition: + * @device: a #GstD3D11Device + * @desc: a DXGI_SWAP_CHAIN_DESC1 structure for swapchain + * @output: (nullable): a IDXGIOutput interface for the output to restrict content to + * + * Create a IDXGISwapChain1 object. Caller must release returned swap chain object + * via IDXGISwapChain1_Release() + * + * Returns: (transfer full) (nullable): a new IDXGISwapChain1 or %NULL + * when failed to create swap chain with given @desc + */ +IDXGISwapChain1 * +gst_d3d11_device_create_swap_chain_for_composition (GstD3D11Device * device, + const DXGI_SWAP_CHAIN_DESC1 * desc, IDXGIOutput * output) +{ + GstD3D11DevicePrivate *priv; + IDXGISwapChain1 *swap_chain = NULL; + HRESULT hr; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + + priv = device->priv; + + gst_d3d11_device_lock (device); + hr = IDXGIFactory2_CreateSwapChainForComposition ((IDXGIFactory2 *) + priv->factory, (IUnknown *) priv->device, desc, output, &swap_chain); + gst_d3d11_device_unlock (device); + + if (!gst_d3d11_result (hr, device)) { + GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x", + (guint) hr); + swap_chain = NULL; + } + + return swap_chain; +} +#endif /* GST_D3D11_WINAPI_ONLY_APP */ +#endif /* (DXGI_HEADER_VERSION >= 2) */ /** * gst_d3d11_device_release_swap_chain: diff --git a/sys/d3d11/gstd3d11device.h b/sys/d3d11/gstd3d11device.h index 5cffd8a..7827943 100644 --- a/sys/d3d11/gstd3d11device.h +++ b/sys/d3d11/gstd3d11device.h @@ -91,6 +91,7 @@ IDXGISwapChain * gst_d3d11_device_create_swap_chain (GstD3D11Device * devi const DXGI_SWAP_CHAIN_DESC * desc); #if (DXGI_HEADER_VERSION >= 2) +#if (!GST_D3D11_WINAPI_ONLY_APP) IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Device * device, HWND hwnd, const DXGI_SWAP_CHAIN_DESC1 * desc, @@ -98,6 +99,19 @@ IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Devic IDXGIOutput * output); #endif +#if GST_D3D11_WINAPI_ONLY_APP +IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_core_window (GstD3D11Device * device, + guintptr core_window, + const DXGI_SWAP_CHAIN_DESC1 * desc, + IDXGIOutput * output); + +IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_composition (GstD3D11Device * device, + const DXGI_SWAP_CHAIN_DESC1 * desc, + IDXGIOutput * output); + +#endif /* GST_D3D11_WINAPI_ONLY_APP */ +#endif /* (DXGI_HEADER_VERSION >= 2) */ + void gst_d3d11_device_release_swap_chain (GstD3D11Device * device, IDXGISwapChain * swap_chain); diff --git a/sys/d3d11/gstd3d11utils.c b/sys/d3d11/gstd3d11utils.c index cf933b7..e396f3c 100644 --- a/sys/d3d11/gstd3d11utils.c +++ b/sys/d3d11/gstd3d11utils.c @@ -21,8 +21,6 @@ #include "config.h" #endif -#include "d3d11config.h" - #include "gstd3d11utils.h" #include "gstd3d11device.h" @@ -331,8 +329,12 @@ gst_d3d11_is_windows_8_or_greater (void) static gboolean ret = FALSE; if (g_once_init_enter (&version_once)) { +#if (!GST_D3D11_WINAPI_ONLY_APP) if (IsWindows8OrGreater ()) ret = TRUE; +#else + ret = TRUE; +#endif g_once_init_leave (&version_once, 1); } diff --git a/sys/d3d11/gstd3d11videosink.c b/sys/d3d11/gstd3d11videosink.c index 5fceabf..237a280 100644 --- a/sys/d3d11/gstd3d11videosink.c +++ b/sys/d3d11/gstd3d11videosink.c @@ -28,6 +28,13 @@ #include "gstd3d11bufferpool.h" #include "gstd3d11format.h" +#if GST_D3D11_WINAPI_ONLY_APP +#include "gstd3d11window_corewindow.h" +#include "gstd3d11window_swapchainpanel.h" +#else +#include "gstd3d11window_win32.h" +#endif + enum { PROP_0, @@ -82,6 +89,7 @@ static gboolean gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink, static gboolean gst_d3d11_video_sink_query (GstBaseSink * sink, GstQuery * query); static gboolean gst_d3d11_video_sink_unlock (GstBaseSink * sink); +static gboolean gst_d3d11_video_sink_unlock_stop (GstBaseSink * sink); static GstFlowReturn gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf); @@ -159,6 +167,8 @@ gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass) GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_propose_allocation); basesink_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_query); basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_unlock); + basesink_class->unlock_stop = + GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_unlock_stop); videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_show_frame); @@ -362,15 +372,6 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps) if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0) goto no_display_size; - if (!self->window_id) - gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (self)); - - if (self->window_id) { - GST_DEBUG_OBJECT (self, "Set external window %" G_GUINTPTR_FORMAT, - (guintptr) self->window_id); - gst_d3d11_window_set_window_handle (self->window, self->window_id); - } - GST_OBJECT_LOCK (self); if (!self->pending_render_rect) { self->render_rect.x = 0; @@ -383,12 +384,6 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps) self->render_rect.x, self->render_rect.y, self->render_rect.w, self->render_rect.h); self->pending_render_rect = FALSE; - - g_object_set (self->window, - "force-aspect-ratio", self->force_aspect_ratio, - "fullscreen-toggle-mode", self->fullscreen_toggle_mode, - "fullscreen", self->fullscreen, NULL); - GST_OBJECT_UNLOCK (self); if (!gst_d3d11_window_prepare (self->window, GST_VIDEO_SINK_WIDTH (self), @@ -484,21 +479,12 @@ gst_d3d11_video_mouse_key_event (GstD3D11Window * window, const gchar * event, } } -static void -gst_d3d11_video_sink_got_window_handle (GstD3D11Window * window, - gpointer window_handle, GstD3D11VideoSink * self) -{ - GST_LOG_OBJECT (self, - "got window handle %" G_GUINTPTR_FORMAT, (guintptr) window_handle); - gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (self), - (guintptr) window_handle); -} - static gboolean gst_d3d11_video_sink_start (GstBaseSink * sink) { GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink); gboolean is_hardware = TRUE; + GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND; GST_DEBUG_OBJECT (self, "Start"); @@ -508,30 +494,75 @@ gst_d3d11_video_sink_start (GstBaseSink * sink) return FALSE; } - self->window = gst_d3d11_window_new (self->device); - if (!self->window) { - GST_ERROR_OBJECT (sink, "Cannot create d3d11window"); + if (!self->window_id) + gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (self)); + + if (self->window_id) { + window_type = + gst_d3d11_window_get_native_type_from_handle (self->window_id); + + if (window_type != GST_D3D11_WINDOW_NATIVE_TYPE_NONE) { + gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (self), + self->window_id); + } + } + + GST_DEBUG_OBJECT (self, "Create window (type: %s)", + gst_d3d11_window_get_native_type_to_string (window_type)); + +#if GST_D3D11_WINAPI_ONLY_APP + if (window_type != GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW && + window_type != GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL) { + GST_ERROR_OBJECT (sink, "Overlay handle must be set before READY state"); return FALSE; } +#endif - g_object_get (self->device, "hardware", &is_hardware, NULL); + switch (window_type) { +#if (!GST_D3D11_WINAPI_ONLY_APP) + case GST_D3D11_WINDOW_NATIVE_TYPE_HWND: + self->window = gst_d3d11_window_win32_new (self->device, self->window_id); + break; +#else + case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW: + self->window = gst_d3d11_window_core_window_new (self->device, + self->window_id); + break; + case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL: + self->window = gst_d3d11_window_swap_chain_panel_new (self->device, + self->window_id); + break; +#endif + default: + break; + } - if (!is_hardware) { - GST_WARNING_OBJECT (self, "D3D11 device is running on software emulation"); - self->can_convert = FALSE; - } else { - self->can_convert = TRUE; + if (!self->window) { + GST_ERROR_OBJECT (sink, "Cannot create d3d11window"); + return FALSE; } + GST_OBJECT_LOCK (self); g_object_set (self->window, + "force-aspect-ratio", self->force_aspect_ratio, + "fullscreen-toggle-mode", self->fullscreen_toggle_mode, + "fullscreen", self->fullscreen, "enable-navigation-events", self->enable_navigation_events, NULL); + GST_OBJECT_UNLOCK (self); g_signal_connect (self->window, "key-event", G_CALLBACK (gst_d3d11_video_sink_key_event), self); g_signal_connect (self->window, "mouse-event", G_CALLBACK (gst_d3d11_video_mouse_key_event), self); - g_signal_connect (self->window, "got-window-handle", - G_CALLBACK (gst_d3d11_video_sink_got_window_handle), self); + + g_object_get (self->device, "hardware", &is_hardware, NULL); + + if (!is_hardware) { + GST_WARNING_OBJECT (self, "D3D11 device is running on software emulation"); + self->can_convert = FALSE; + } else { + self->can_convert = TRUE; + } return TRUE; } @@ -688,7 +719,18 @@ gst_d3d11_video_sink_unlock (GstBaseSink * sink) GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink); if (self->window) - gst_d3d11_window_flush (self->window); + gst_d3d11_window_unlock (self->window); + + return TRUE; +} + +static gboolean +gst_d3d11_video_sink_unlock_stop (GstBaseSink * sink) +{ + GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink); + + if (self->window) + gst_d3d11_window_unlock_stop (self->window); return TRUE; } diff --git a/sys/d3d11/gstd3d11window.c b/sys/d3d11/gstd3d11window.c deleted file mode 100644 index 561b5c6..0000000 --- a/sys/d3d11/gstd3d11window.c +++ /dev/null @@ -1,1598 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2008 Julien Isorce - * Copyright (C) 2012 Matthew Waters - * Copyright (C) 2019 Seungha Yang - * - * 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 "d3d11config.h" - -#include "gstd3d11window.h" -#include "gstd3d11device.h" -#include "gstd3d11memory.h" -#include "gstd3d11utils.h" - -#include - -G_LOCK_DEFINE_STATIC (create_lock); - -enum -{ - PROP_0, - PROP_D3D11_DEVICE, - PROP_FORCE_ASPECT_RATIO, - PROP_ENABLE_NAVIGATION_EVENTS, - PROP_FULLSCREEN_TOGGLE_MODE, - PROP_FULLSCREEN, -}; - -#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE -#define DEFAULT_FORCE_ASPECT_RATIO TRUE -#define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE -#define DEFAULT_FULLSCREEN FALSE - -enum -{ - SIGNAL_KEY_EVENT, - SIGNAL_MOUSE_EVENT, - SIGNAL_GOT_WINDOW_HANDLE, - SIGNAL_LAST -}; - -static guint d3d11_window_signals[SIGNAL_LAST] = { 0, }; - -GType -gst_d3d11_window_fullscreen_toggle_mode_type (void) -{ - static volatile gsize mode_type = 0; - - if (g_once_init_enter (&mode_type)) { - static const GFlagsValue mode_types[] = { - {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE, - "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"}, - {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER, - "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"}, - {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY, - "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"}, - {0, NULL, NULL}, - }; - GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode", - mode_types); - g_once_init_leave (&mode_type, tmp); - } - - return (GType) mode_type; -} - -#define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc" -#define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_object" - -#define WM_GST_D3D11_FULLSCREEN (WM_USER + 1) - -static LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, - LPARAM lParam); -static LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, - LPARAM lParam); - -GST_DEBUG_CATEGORY_STATIC (gst_d3d11_window_debug); -#define GST_CAT_DEFAULT gst_d3d11_window_debug - -#define gst_d3d11_window_parent_class parent_class -G_DEFINE_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT); - -static void gst_d3d11_window_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_d3d11_window_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_d3d11_window_constructed (GObject * object); -static void gst_d3d11_window_dispose (GObject * object); -static void gst_d3d11_window_finalize (GObject * object); -static gpointer gst_d3d11_window_thread_func (gpointer data); -static gboolean gst_d3d11_window_create_internal_window (GstD3D11Window * self); -static void gst_d3d11_window_close_internal_window (GstD3D11Window * self); -static void release_external_win_id (GstD3D11Window * self); -static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self, - GstBuffer * buffer); -static void gst_d3d11_window_change_fullscreen_mode (GstD3D11Window * self); - -static void -gst_d3d11_window_class_init (GstD3D11WindowClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gst_d3d11_window_set_property; - gobject_class->get_property = gst_d3d11_window_get_property; - gobject_class->constructed = gst_d3d11_window_constructed; - gobject_class->dispose = gst_d3d11_window_dispose; - gobject_class->finalize = gst_d3d11_window_finalize; - - g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE, - g_param_spec_object ("d3d11device", "D3D11 Device", - "GstD3D11Device object for creating swapchain", - GST_TYPE_D3D11_DEVICE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, - g_param_spec_boolean ("force-aspect-ratio", - "Force aspect ratio", - "When enabled, scaling will respect original aspect ratio", - DEFAULT_FORCE_ASPECT_RATIO, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS, - g_param_spec_boolean ("enable-navigation-events", - "Enable navigation events", - "When enabled, signals for navigation events are emitted", - DEFAULT_ENABLE_NAVIGATION_EVENTS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE, - g_param_spec_flags ("fullscreen-toggle-mode", - "Full screen toggle mode", - "Full screen toggle mode used to trigger fullscreen mode change", - GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_FULLSCREEN, - g_param_spec_boolean ("fullscreen", - "fullscreen", - "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"", - DEFAULT_FULLSCREEN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - d3d11_window_signals[SIGNAL_KEY_EVENT] = - g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); - - d3d11_window_signals[SIGNAL_MOUSE_EVENT] = - g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE); - - d3d11_window_signals[SIGNAL_GOT_WINDOW_HANDLE] = - g_signal_new ("got-window-handle", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); - - GST_DEBUG_CATEGORY_INIT (gst_d3d11_window_debug, "d3d11window", 0, - "d3d11 window"); -} - -static void -gst_d3d11_window_init (GstD3D11Window * self) -{ - g_mutex_init (&self->lock); - g_cond_init (&self->cond); - - self->main_context = g_main_context_new (); - - self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; - self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; - self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE; - self->fullscreen = DEFAULT_FULLSCREEN; - - self->aspect_ratio_n = 1; - self->aspect_ratio_d = 1; - - GST_TRACE_OBJECT (self, "Initialized"); -} - -static void -gst_d3d11_window_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (object); - - GST_OBJECT_LOCK (self); - switch (prop_id) { - case PROP_D3D11_DEVICE: - self->device = g_value_dup_object (value); - break; - case PROP_FORCE_ASPECT_RATIO: - { - gboolean force_aspect_ratio; - - force_aspect_ratio = g_value_get_boolean (value); - if (force_aspect_ratio != self->force_aspect_ratio) { - self->force_aspect_ratio = force_aspect_ratio; - PostMessage (self->internal_win_id, WM_SIZE, 0, 0); - } - break; - } - case PROP_ENABLE_NAVIGATION_EVENTS: - self->enable_navigation_events = g_value_get_boolean (value); - break; - case PROP_FULLSCREEN_TOGGLE_MODE: - self->fullscreen_toggle_mode = g_value_get_flags (value); - break; - case PROP_FULLSCREEN: - { - self->requested_fullscreen = g_value_get_boolean (value); - if (self->swap_chain) { - g_atomic_int_add (&self->pending_fullscreen_count, 1); - PostMessage (self->internal_win_id, WM_GST_D3D11_FULLSCREEN, 0, 0); - } - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_OBJECT_UNLOCK (self); -} - -static void -gst_d3d11_window_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (object); - - switch (prop_id) { - case PROP_ENABLE_NAVIGATION_EVENTS: - g_value_set_boolean (value, self->enable_navigation_events); - break; - case PROP_FORCE_ASPECT_RATIO: - g_value_set_boolean (value, self->force_aspect_ratio); - break; - case PROP_FULLSCREEN_TOGGLE_MODE: - g_value_set_flags (value, self->fullscreen_toggle_mode); - break; - case PROP_FULLSCREEN: - g_value_set_boolean (value, self->fullscreen); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_d3d11_window_constructed (GObject * object) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (object); - - g_mutex_lock (&self->lock); - self->loop = g_main_loop_new (self->main_context, FALSE); - self->thread = g_thread_new ("GstD3D11Window", - (GThreadFunc) gst_d3d11_window_thread_func, self); - while (!g_main_loop_is_running (self->loop)) - g_cond_wait (&self->cond, &self->lock); - g_mutex_unlock (&self->lock); -} - -static void -gst_d3d11_window_release_resources (GstD3D11Device * device, - GstD3D11Window * window) -{ - if (window->rtv) { - ID3D11RenderTargetView_Release (window->rtv); - window->rtv = NULL; - } - - if (window->swap_chain) { - IDXGISwapChain_Release (window->swap_chain); - window->swap_chain = NULL; - } -} - -static void -gst_d3d11_window_dispose (GObject * object) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (object); - - release_external_win_id (self); - - if (self->loop) { - g_main_loop_quit (self->loop); - } - - if (self->thread) { - g_thread_join (self->thread); - self->thread = NULL; - } - - if (self->loop) { - g_main_loop_unref (self->loop); - self->loop = NULL; - } - - if (self->main_context) { - g_main_context_unref (self->main_context); - self->main_context = NULL; - } - - if (self->device) { - gst_d3d11_window_release_resources (self->device, self); - } - - if (self->converter) { - gst_d3d11_color_converter_free (self->converter); - self->converter = NULL; - } - - if (self->compositor) { - gst_d3d11_overlay_compositor_free (self->compositor); - self->compositor = NULL; - } - - gst_clear_buffer (&self->cached_buffer); - gst_clear_object (&self->device); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_d3d11_window_finalize (GObject * object) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (object); - - g_mutex_clear (&self->lock); - g_cond_clear (&self->cond); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -running_cb (gpointer user_data) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (user_data); - - GST_TRACE_OBJECT (self, "Main loop running now"); - - g_mutex_lock (&self->lock); - g_cond_signal (&self->cond); - g_mutex_unlock (&self->lock); - - return G_SOURCE_REMOVE; -} - -static gpointer -gst_d3d11_window_thread_func (gpointer data) -{ - GstD3D11Window *self = GST_D3D11_WINDOW (data); - GSource *source; - - GST_DEBUG_OBJECT (self, "Enter loop"); - g_main_context_push_thread_default (self->main_context); - - self->created = gst_d3d11_window_create_internal_window (self); - - source = g_idle_source_new (); - g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL); - g_source_attach (source, self->main_context); - g_source_unref (source); - - g_main_loop_run (self->loop); - - gst_d3d11_window_close_internal_window (self); - - g_main_context_pop_thread_default (self->main_context); - - GST_DEBUG_OBJECT (self, "Exit loop"); - - return NULL; -} - -static gboolean -msg_cb (GIOChannel * source, GIOCondition condition, gpointer data) -{ - MSG msg; - - if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - return G_SOURCE_CONTINUE; - - TranslateMessage (&msg); - DispatchMessage (&msg); - - return G_SOURCE_CONTINUE; -} - -static void -gst_d3d11_window_close_internal_window (GstD3D11Window * self) -{ - if (self->internal_win_id) { - RemoveProp (self->internal_win_id, D3D11_WINDOW_PROP_NAME); - ShowWindow (self->internal_win_id, SW_HIDE); - SetParent (self->internal_win_id, NULL); - if (!DestroyWindow (self->internal_win_id)) - GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT - ", 0x%x", (guintptr) self->internal_win_id, (guint) GetLastError ()); - self->internal_win_id = NULL; - } - - if (self->msg_source) { - g_source_destroy (self->msg_source); - g_source_unref (self->msg_source); - self->msg_source = NULL; - } - - if (self->msg_io_channel) { - g_io_channel_unref (self->msg_io_channel); - self->msg_io_channel = NULL; - } -} - -static void -set_external_win_id (GstD3D11Window * self) -{ - WNDPROC external_window_proc; - RECT rect; - - if (!self->external_win_id) { - /* no parent so the internal window needs borders and system menu */ - SetWindowLongPtr (self->internal_win_id, GWL_STYLE, - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW); - SetParent (self->internal_win_id, NULL); - - return; - } - - external_window_proc = - (WNDPROC) GetWindowLongPtr (self->external_win_id, GWLP_WNDPROC); - - GST_DEBUG ("set external window %" G_GUINTPTR_FORMAT, - (guintptr) self->external_win_id); - - SetProp (self->external_win_id, EXTERNAL_PROC_PROP_NAME, - (WNDPROC) external_window_proc); - SetProp (self->external_win_id, D3D11_WINDOW_PROP_NAME, self); - SetWindowLongPtr (self->external_win_id, GWLP_WNDPROC, - (LONG_PTR) sub_class_proc); - - SetWindowLongPtr (self->internal_win_id, GWL_STYLE, WS_CHILD | WS_MAXIMIZE); - SetParent (self->internal_win_id, self->external_win_id); - - /* take changes into account: SWP_FRAMECHANGED */ - GetClientRect (self->external_win_id, &rect); - SetWindowPos (self->internal_win_id, HWND_TOP, rect.left, rect.top, - rect.right, rect.bottom, - SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | - SWP_FRAMECHANGED | SWP_NOACTIVATE); - MoveWindow (self->internal_win_id, rect.left, rect.top, rect.right, - rect.bottom, FALSE); -} - -static void -release_external_win_id (GstD3D11Window * self) -{ - WNDPROC external_proc; - - if (!self->external_win_id) - return; - - external_proc = GetProp (self->external_win_id, EXTERNAL_PROC_PROP_NAME); - if (!external_proc) - return; - - GST_DEBUG ("release external window %" G_GUINTPTR_FORMAT, - (guintptr) self->external_win_id); - - SetWindowLongPtr (self->external_win_id, - GWLP_WNDPROC, (LONG_PTR) external_proc); - - RemoveProp (self->external_win_id, EXTERNAL_PROC_PROP_NAME); - RemoveProp (self->external_win_id, D3D11_WINDOW_PROP_NAME); - self->external_win_id = NULL; -} - -static gboolean -gst_d3d11_window_create_internal_window (GstD3D11Window * self) -{ - WNDCLASSEX wc; - ATOM atom = 0; - HINSTANCE hinstance = GetModuleHandle (NULL); - - GST_LOG_OBJECT (self, "Attempting to create a win32 window"); - - G_LOCK (create_lock); - atom = GetClassInfoEx (hinstance, "GSTD3D11", &wc); - if (atom == 0) { - GST_LOG_OBJECT (self, "Register internal window class"); - ZeroMemory (&wc, sizeof (WNDCLASSEX)); - - wc.cbSize = sizeof (WNDCLASSEX); - wc.lpfnWndProc = window_proc; - wc.hInstance = hinstance; - wc.hIcon = LoadIcon (NULL, IDI_WINLOGO); - wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - wc.hCursor = LoadCursor (NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); - wc.lpszClassName = "GSTD3D11"; - - atom = RegisterClassEx (&wc); - - if (atom == 0) { - G_UNLOCK (create_lock); - GST_ERROR_OBJECT (self, "Failed to register window class 0x%x", - (unsigned int) GetLastError ()); - return FALSE; - } - } else { - GST_LOG_OBJECT (self, "window class was already registered"); - } - - self->device_handle = 0; - self->internal_win_id = 0; - self->visible = FALSE; - - self->internal_win_id = CreateWindowEx (0, - "GSTD3D11", - "Direct3D11 renderer", - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, - 0, 0, (HWND) NULL, (HMENU) NULL, hinstance, self); - - G_UNLOCK (create_lock); - - if (!self->internal_win_id) { - GST_ERROR_OBJECT (self, "Failed to create d3d11 window"); - return FALSE; - } - - GST_DEBUG_OBJECT (self, "d3d11 window created: %" G_GUINTPTR_FORMAT, - (guintptr) self->internal_win_id); - - g_signal_emit (self, - d3d11_window_signals[SIGNAL_GOT_WINDOW_HANDLE], 0, self->internal_win_id); - - /* device_handle is set in the window_proc */ - if (!self->device_handle) { - GST_ERROR_OBJECT (self, "device handle is not available"); - return FALSE; - } - - GST_LOG_OBJECT (self, - "Created a internal d3d11 window %p", self->internal_win_id); - - self->msg_io_channel = - g_io_channel_win32_new_messages ((guintptr) self->internal_win_id); - self->msg_source = g_io_create_watch (self->msg_io_channel, G_IO_IN); - g_source_set_callback (self->msg_source, (GSourceFunc) msg_cb, self, NULL); - g_source_attach (self->msg_source, self->main_context); - - return TRUE; -} - -static void -gst_d3d11_window_on_resize (GstD3D11Window * window, gboolean redraw) -{ - HRESULT hr; - ID3D11Device *d3d11_dev; - guint width, height; - D3D11_TEXTURE2D_DESC desc; - DXGI_SWAP_CHAIN_DESC swap_desc; - ID3D11Texture2D *backbuffer = NULL; - GstVideoRectangle src_rect, dst_rect, rst_rect; - - gst_d3d11_device_lock (window->device); - if (!window->swap_chain) - goto done; - - d3d11_dev = gst_d3d11_device_get_device_handle (window->device); - - if (window->rtv) { - ID3D11RenderTargetView_Release (window->rtv); - window->rtv = NULL; - } - - /* Set zero width and height here. dxgi will decide client area by itself */ - IDXGISwapChain_GetDesc (window->swap_chain, &swap_desc); - hr = IDXGISwapChain_ResizeBuffers (window->swap_chain, - 0, 0, 0, DXGI_FORMAT_UNKNOWN, swap_desc.Flags); - if (!gst_d3d11_result (hr, window->device)) { - GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr); - goto done; - } - - hr = IDXGISwapChain_GetBuffer (window->swap_chain, - 0, &IID_ID3D11Texture2D, (void **) &backbuffer); - if (!gst_d3d11_result (hr, window->device)) { - GST_ERROR_OBJECT (window, - "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr); - goto done; - } - - ID3D11Texture2D_GetDesc (backbuffer, &desc); - window->surface_width = desc.Width; - window->surface_height = desc.Height; - - width = window->width; - height = window->height; - - { - src_rect.x = 0; - src_rect.y = 0; - src_rect.w = width * window->aspect_ratio_n; - src_rect.h = height * window->aspect_ratio_d; - - dst_rect.x = 0; - dst_rect.y = 0; - dst_rect.w = window->surface_width; - dst_rect.h = window->surface_height; - - if (window->force_aspect_ratio) { - src_rect.w = width * window->aspect_ratio_n; - src_rect.h = height * window->aspect_ratio_d; - - gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE); - } else { - rst_rect = dst_rect; - } - } - - window->render_rect.left = rst_rect.x; - window->render_rect.top = rst_rect.y; - window->render_rect.right = rst_rect.x + rst_rect.w; - window->render_rect.bottom = rst_rect.y + rst_rect.h; - - GST_LOG_OBJECT (window, - "New client area %dx%d, render rect x: %d, y: %d, %dx%d", - desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h); - - hr = ID3D11Device_CreateRenderTargetView (d3d11_dev, - (ID3D11Resource *) backbuffer, NULL, &window->rtv); - if (!gst_d3d11_result (hr, window->device)) { - GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x", - (guint) hr); - - goto done; - } - - window->first_present = TRUE; - - if (redraw) - gst_d3d111_window_present (window, NULL); - -done: - if (backbuffer) - ID3D11Texture2D_Release (backbuffer); - - gst_d3d11_device_unlock (window->device); -} - -/* always called from window thread */ -static void -gst_d3d11_window_change_fullscreen_mode (GstD3D11Window * self) -{ - HWND hwnd = self->external_win_id ? self->external_win_id : - self->internal_win_id; - - if (!self->swap_chain) - return; - - if (self->requested_fullscreen == self->fullscreen) - return; - - GST_DEBUG_OBJECT (self, "Change mode to %s", - self->requested_fullscreen ? "fullscreen" : "windowed"); - - self->fullscreen = !self->fullscreen; - - if (!self->fullscreen) { - /* Restore the window's attributes and size */ - SetWindowLong (hwnd, GWL_STYLE, self->restore_style); - - SetWindowPos (hwnd, HWND_NOTOPMOST, - self->restore_rect.left, - self->restore_rect.top, - self->restore_rect.right - self->restore_rect.left, - self->restore_rect.bottom - self->restore_rect.top, - SWP_FRAMECHANGED | SWP_NOACTIVATE); - - ShowWindow (hwnd, SW_NORMAL); - } else { - IDXGIOutput *output; - DXGI_OUTPUT_DESC output_desc; - - /* show window before change style */ - ShowWindow (hwnd, SW_SHOW); - - /* Save the old window rect so we can restore it when exiting - * fullscreen mode */ - GetWindowRect (hwnd, &self->restore_rect); - self->restore_style = GetWindowLong (hwnd, GWL_STYLE); - - /* Make the window borderless so that the client area can fill the screen */ - SetWindowLong (hwnd, GWL_STYLE, - self->restore_style & - ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | - WS_THICKFRAME)); - - IDXGISwapChain_GetContainingOutput (self->swap_chain, &output); - IDXGIOutput_GetDesc (output, &output_desc); - IDXGIOutput_Release (output); - - SetWindowPos (hwnd, HWND_TOPMOST, - output_desc.DesktopCoordinates.left, - output_desc.DesktopCoordinates.top, - output_desc.DesktopCoordinates.right, - output_desc.DesktopCoordinates.bottom, - SWP_FRAMECHANGED | SWP_NOACTIVATE); - - ShowWindow (hwnd, SW_MAXIMIZE); - } - - GST_DEBUG_OBJECT (self, "Fullscreen mode change done"); -} - -static void -gst_d3d11_window_on_keyboard_event (GstD3D11Window * self, - HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - gunichar2 wcrep[128]; - const gchar *event; - - if (!self->enable_navigation_events) - return; - - if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) { - gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL); - if (utfrep) { - if (uMsg == WM_KEYDOWN) - event = "key-press"; - else - event = "key-release"; - - g_signal_emit (self, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, - event, utfrep); - g_free (utfrep); - } - } -} - -static void -gst_d3d11_window_on_mouse_event (GstD3D11Window * self, - HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - gint button; - const gchar *event = NULL; - - if (!self->enable_navigation_events) - return; - - /* FIXME: convert to render coordinate */ - switch (uMsg) { - case WM_MOUSEMOVE: - button = 0; - event = "mouse-move"; - break; - case WM_LBUTTONDOWN: - button = 1; - event = "mouse-button-press"; - break; - case WM_LBUTTONUP: - button = 1; - event = "mouse-button-release"; - break; - case WM_RBUTTONDOWN: - button = 2; - event = "mouse-button-press"; - break; - case WM_RBUTTONUP: - button = 2; - event = "mouse-button-release"; - break; - case WM_MBUTTONDOWN: - button = 3; - event = "mouse-button-press"; - break; - case WM_MBUTTONUP: - button = 3; - event = "mouse-button-release"; - break; - default: - break; - } - - if (event) - g_signal_emit (self, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0, - event, button, (gdouble) LOWORD (lParam), (gdouble) HIWORD (lParam)); -} - -static void -gst_d3d11_window_handle_window_proc (GstD3D11Window * self, - HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_SIZE: - gst_d3d11_window_on_resize (self, TRUE); - break; - case WM_CLOSE: - if (self->internal_win_id) { - ShowWindow (self->internal_win_id, SW_HIDE); - gst_d3d11_window_close_internal_window (self); - } - break; - case WM_KEYDOWN: - case WM_KEYUP: - gst_d3d11_window_on_keyboard_event (self, hWnd, uMsg, wParam, lParam); - break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_MOUSEMOVE: - gst_d3d11_window_on_mouse_event (self, hWnd, uMsg, wParam, lParam); - break; - case WM_SYSKEYDOWN: - if ((self->fullscreen_toggle_mode & - GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) - == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) { - WORD state = GetKeyState (VK_RETURN); - BYTE high = HIBYTE (state); - - if (high & 0x1) { - self->requested_fullscreen = !self->fullscreen; - gst_d3d11_window_change_fullscreen_mode (self); - } - } - break; - case WM_GST_D3D11_FULLSCREEN: - if (g_atomic_int_get (&self->pending_fullscreen_count)) { - g_atomic_int_dec_and_test (&self->pending_fullscreen_count); - if ((self->fullscreen_toggle_mode & - GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY) - == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY) - gst_d3d11_window_change_fullscreen_mode (self); - } - break; - default: - break; - } - - return; -} - -static LRESULT CALLBACK -window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - GstD3D11Window *self; - - if (uMsg == WM_CREATE) { - self = GST_D3D11_WINDOW (((LPCREATESTRUCT) lParam)->lpCreateParams); - - GST_LOG_OBJECT (self, "WM_CREATE"); - - self->device_handle = GetDC (hWnd); - /* Do this, otherwise we hang on exit. We can still use it (due to the - * CS_OWNDC flag in the WindowClass) after we have Released. - */ - ReleaseDC (hWnd, self->device_handle); - - SetProp (hWnd, D3D11_WINDOW_PROP_NAME, self); - } else if (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)) { - self = GST_D3D11_WINDOW (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)); - - g_assert (self->internal_win_id == hWnd); - - gst_d3d11_window_handle_window_proc (self, hWnd, uMsg, wParam, lParam); - } - - if (uMsg == WM_SIZE) - return 0; - - return DefWindowProc (hWnd, uMsg, wParam, lParam); -} - -static LRESULT FAR PASCAL -sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - WNDPROC external_window_proc = GetProp (hWnd, EXTERNAL_PROC_PROP_NAME); - GstD3D11Window *self = - (GstD3D11Window *) GetProp (hWnd, D3D11_WINDOW_PROP_NAME); - - if (uMsg == WM_SIZE) { - MoveWindow (self->internal_win_id, 0, 0, LOWORD (lParam), HIWORD (lParam), - FALSE); - } else if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) { - g_mutex_lock (&self->lock); - GST_WARNING_OBJECT (self, "external window is closing"); - release_external_win_id (self); - self->external_win_id = NULL; - self->overlay_state = GST_D3D11_WINDOW_OVERLAY_STATE_CLOSED; - g_mutex_unlock (&self->lock); - } else { - gst_d3d11_window_handle_window_proc (self, hWnd, uMsg, wParam, lParam); - } - - return CallWindowProc (external_window_proc, hWnd, uMsg, wParam, lParam); -} - -GstD3D11Window * -gst_d3d11_window_new (GstD3D11Device * device) -{ - GstD3D11Window *window; - - g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); - - window = g_object_new (GST_TYPE_D3D11_WINDOW, "d3d11device", device, NULL); - g_object_ref_sink (window); - - if (!window->created) - gst_clear_object (&window); - - return window; -} - -#if (DXGI_HEADER_VERSION >= 5) -static inline UINT16 -fraction_to_uint (guint num, guint den, guint scale) -{ - gdouble val; - gst_util_fraction_to_double (num, den, &val); - - return (UINT16) val *scale; -} - -static void -mastering_display_gst_to_dxgi (GstVideoMasteringDisplayInfo * m, - GstVideoContentLightLevel * c, DXGI_HDR_METADATA_HDR10 * meta) -{ - meta->RedPrimary[0] = fraction_to_uint (m->Rx_n, m->Rx_d, 50000); - meta->RedPrimary[1] = fraction_to_uint (m->Ry_n, m->Ry_d, 50000); - meta->GreenPrimary[0] = fraction_to_uint (m->Gx_n, m->Gx_d, 50000); - meta->GreenPrimary[1] = fraction_to_uint (m->Gy_n, m->Gy_d, 50000); - meta->BluePrimary[0] = fraction_to_uint (m->Bx_n, m->Bx_d, 50000); - meta->BluePrimary[1] = fraction_to_uint (m->By_n, m->By_d, 50000); - meta->WhitePoint[0] = fraction_to_uint (m->Wx_n, m->Wx_d, 50000); - meta->WhitePoint[1] = fraction_to_uint (m->Wy_n, m->Wy_d, 50000); - meta->MaxMasteringLuminance = - fraction_to_uint (m->max_luma_n, m->max_luma_d, 1); - meta->MinMasteringLuminance = - fraction_to_uint (m->min_luma_n, m->min_luma_d, 1); - meta->MaxContentLightLevel = fraction_to_uint (c->maxCLL_n, c->maxCLL_d, 1); - meta->MaxFrameAverageLightLevel = - fraction_to_uint (c->maxFALL_n, c->maxFALL_d, 1); -} - -/* missing in mingw header... */ -typedef enum -{ - GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0, - GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1, - GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2, - GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3, - GST_DXGI_COLOR_SPACE_RESERVED = 4, - GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6, - GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8, - GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10, - GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11, - GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13, - GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16, - GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18, - GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19, - GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20, - GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23, - GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24, - GST_DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF -} GST_DXGI_COLOR_SPACE_TYPE; - -typedef struct -{ - GST_DXGI_COLOR_SPACE_TYPE type; - GstVideoColorRange range; - GstVideoTransferFunction transfer; - GstVideoColorPrimaries primaries; -} DxgiColorSpaceMap; - -/* https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type */ -static const DxgiColorSpaceMap colorspace_map[] = { - /* RGB, bt709 */ - {GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255, - GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709}, - {GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255, - GST_VIDEO_TRANSFER_GAMMA10, GST_VIDEO_COLOR_PRIMARIES_BT709}, - {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235, - GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709}, - /* RGB, bt2020 */ - {GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255, - GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020}, - {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235, - GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020}, - /* RGB, bt2084 */ - {GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255, - GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020}, - {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020, - GST_VIDEO_COLOR_RANGE_16_235, - GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020}, - /* RGB, SRGB */ - {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235, - GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT709}, - {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235, - GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT2020}, -}; - -static gboolean -gst_d3d11_window_color_space_from_video_info (GstD3D11Window * self, - GstVideoInfo * info, IDXGISwapChain4 * swapchain, - GST_DXGI_COLOR_SPACE_TYPE * dxgi_colorspace) -{ - gint i; - gint best_idx = -1; - gint best_score = 0; - - g_return_val_if_fail (info != NULL, FALSE); - g_return_val_if_fail (dxgi_colorspace != NULL, FALSE); - - /* We render only RGB for now */ - if (!GST_VIDEO_FORMAT_INFO_IS_RGB (info->finfo)) - return FALSE; - - /* find the best matching colorspace */ - for (i = 0; i < G_N_ELEMENTS (colorspace_map); i++) { - GstVideoColorimetry *cinfo = &info->colorimetry; - UINT can_support = 0; - HRESULT hr; - gint score = 0; - GstVideoTransferFunction transfer = cinfo->transfer; - - if (transfer == GST_VIDEO_TRANSFER_BT2020_12) - transfer = GST_VIDEO_TRANSFER_BT2020_10; - - hr = IDXGISwapChain4_CheckColorSpaceSupport (swapchain, - colorspace_map[i].type, &can_support); - - if (SUCCEEDED (hr) && - (can_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) == - DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) { - if (cinfo->range == colorspace_map[i].range) - score++; - - if (transfer == colorspace_map[i].transfer) - score++; - - if (cinfo->primaries == colorspace_map[i].primaries) - score++; - - GST_DEBUG_OBJECT (self, - "colorspace %d supported, score %d", colorspace_map[i].type, score); - - if (score > best_score) { - best_score = score; - best_idx = i; - } - } else { - GST_DEBUG_OBJECT (self, - "colorspace %d not supported", colorspace_map[i].type); - } - } - - if (best_idx < 0) - return FALSE; - - *dxgi_colorspace = colorspace_map[best_idx].type; - - return TRUE; -} -#endif - -static void -gst_d3d11_window_disable_alt_enter (GstD3D11Window * window, - IDXGISwapChain * swap_chain, HWND hwnd) -{ - IDXGIFactory1 *factory = NULL; - HRESULT hr; - - hr = IDXGISwapChain_GetParent (swap_chain, &IID_IDXGIFactory1, - (void **) &factory); - if (!gst_d3d11_result (hr, window->device) || !factory) { - GST_WARNING_OBJECT (window, - "Cannot get parent dxgi factory for swapchain %p, hr: 0x%x", - swap_chain, (guint) hr); - return; - } - - hr = IDXGIFactory1_MakeWindowAssociation (factory, - hwnd, DXGI_MWA_NO_ALT_ENTER); - if (!gst_d3d11_result (hr, window->device)) { - GST_WARNING_OBJECT (window, - "MakeWindowAssociation failure, hr: 0x%x", (guint) hr); - } - - IDXGIFactory1_Release (factory); -} - -gboolean -gst_d3d11_window_prepare (GstD3D11Window * window, guint width, guint height, - guint aspect_ratio_n, guint aspect_ratio_d, GstCaps * caps, GError ** error) -{ - DXGI_SWAP_CHAIN_DESC desc = { 0, }; - GstCaps *render_caps; - UINT swapchain_flags = 0; - DXGI_SWAP_EFFECT swap_effect = DXGI_SWAP_EFFECT_DISCARD; -#if (DXGI_HEADER_VERSION >= 5) - gboolean have_cll = FALSE; - gboolean have_mastering = FALSE; - gboolean swapchain4_available = FALSE; -#endif - - g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE); - g_return_val_if_fail (aspect_ratio_n > 0, FALSE); - g_return_val_if_fail (aspect_ratio_d > 0, FALSE); - - if (gst_d3d11_is_windows_8_or_greater ()) - swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - - GST_DEBUG_OBJECT (window, "Prepare window with %dx%d caps %" GST_PTR_FORMAT, - width, height, caps); - - render_caps = gst_d3d11_device_get_supported_caps (window->device, - D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY); - - GST_DEBUG_OBJECT (window, "rendering caps %" GST_PTR_FORMAT, render_caps); - render_caps = gst_d3d11_caps_fixate_format (caps, render_caps); - - if (!render_caps || gst_caps_is_empty (render_caps)) { - GST_ERROR_OBJECT (window, "Couldn't define render caps"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Couldn't define render caps"); - gst_clear_caps (&render_caps); - - return FALSE; - } - - render_caps = gst_caps_fixate (render_caps); - gst_video_info_from_caps (&window->render_info, render_caps); - gst_clear_caps (&render_caps); - - window->render_format = - gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (&window->render_info)); - if (!window->render_format || - window->render_format->dxgi_format == DXGI_FORMAT_UNKNOWN) { - GST_ERROR_OBJECT (window, "Unknown dxgi render format"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Unknown dxgi render format"); - - return FALSE; - } - - gst_video_info_from_caps (&window->info, caps); - - if (window->converter) - gst_d3d11_color_converter_free (window->converter); - window->converter = NULL; - - if (window->compositor) - gst_d3d11_overlay_compositor_free (window->compositor); - window->compositor = NULL; - - /* preserve upstream colorimetry */ - window->render_info.width = width; - window->render_info.height = height; - - window->render_info.colorimetry.primaries = - window->info.colorimetry.primaries; - window->render_info.colorimetry.transfer = window->info.colorimetry.transfer; - - window->converter = - gst_d3d11_color_converter_new (window->device, &window->info, - &window->render_info); - - if (!window->converter) { - GST_ERROR_OBJECT (window, "Cannot create converter"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Cannot create converter"); - - return FALSE; - } - - window->compositor = - gst_d3d11_overlay_compositor_new (window->device, &window->render_info); - if (!window->compositor) { - GST_ERROR_OBJECT (window, "Cannot create overlay compositor"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Cannot create overlay compositor"); - - return FALSE; - } - - window->allow_tearing = FALSE; - window->have_swapchain1 = FALSE; -#if (DXGI_HEADER_VERSION >= 5) - if (!gst_video_content_light_level_from_caps (&window->content_light_level, - caps)) { - gst_video_content_light_level_init (&window->content_light_level); - } else { - have_cll = TRUE; - } - - if (!gst_video_mastering_display_info_from_caps - (&window->mastering_display_info, caps)) { - gst_video_mastering_display_info_init (&window->mastering_display_info); - } else { - have_mastering = TRUE; - } - - if (gst_d3d11_device_get_chosen_dxgi_factory_version (window->device) >= - GST_D3D11_DXGI_FACTORY_5) { - gboolean allow_tearing = FALSE; - - GST_DEBUG_OBJECT (window, "DXGI 1.5 interface is available"); - swapchain4_available = TRUE; - - g_object_get (window->device, "allow-tearing", &allow_tearing, NULL); - if (allow_tearing) { - GST_DEBUG_OBJECT (window, "device support tearning"); - swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - window->allow_tearing = TRUE; - } - } -#endif - - if (window->swap_chain) { - gst_d3d11_device_lock (window->device); - gst_d3d11_window_release_resources (window->device, window); - gst_d3d11_device_unlock (window->device); - } - - window->aspect_ratio_n = aspect_ratio_n; - window->aspect_ratio_d = aspect_ratio_d; - - window->render_rect.left = 0; - window->render_rect.top = 0; - window->render_rect.right = width; - window->render_rect.bottom = height; - - if (window->external_win_id) { - RECT client_rect = { 0, }; - GetClientRect (window->external_win_id, &client_rect); - - window->surface_width = client_rect.right - client_rect.left; - window->surface_height = client_rect.bottom - client_rect.top; - } else { - window->surface_width = width; - window->surface_height = height; - } - - window->width = width; - window->height = height; - -#if (DXGI_HEADER_VERSION >= 2) - if (!window->swap_chain) { - DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, }; - desc1.Width = 0; - desc1.Height = 0; - desc1.Format = window->render_format->dxgi_format; - /* FIXME: add support stereo */ - desc1.Stereo = FALSE; - desc1.SampleDesc.Count = 1; - desc1.SampleDesc.Quality = 0; - desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc1.BufferCount = 2; - /* NOTE: for UWP app, this should be DXGI_SCALING_ASPECT_RATIO_STRETCH - * with CreateSwapChainForComposition or CreateSwapChainForCoreWindow */ - desc1.Scaling = DXGI_SCALING_STRETCH; - - /* scaling-stretch would break aspect-ratio so we prefer to use scaling-none, - * but Windows7 does not support this method */ - if (gst_d3d11_is_windows_8_or_greater ()) - desc1.Scaling = DXGI_SCALING_NONE; - desc1.SwapEffect = swap_effect; - /* FIXME: might need to define for ovelay composition */ - desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; - desc1.Flags = swapchain_flags; - - window->swap_chain = (IDXGISwapChain *) - gst_d3d11_device_create_swap_chain_for_hwnd (window->device, - window->internal_win_id, &desc1, NULL, NULL); - - if (!window->swap_chain) { - GST_WARNING_OBJECT (window, "Failed to create swapchain1"); - } else { - window->have_swapchain1 = TRUE; - } - } -#endif - - if (!window->swap_chain) { - /* we will get client area at on_resize */ - desc.BufferDesc.Width = 0; - desc.BufferDesc.Height = 0; - /* don't care refresh rate */ - desc.BufferDesc.RefreshRate.Numerator = 0; - desc.BufferDesc.RefreshRate.Denominator = 1; - desc.BufferDesc.Format = window->render_format->dxgi_format; - desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = 2; - desc.SwapEffect = swap_effect; - desc.OutputWindow = window->internal_win_id; - desc.Windowed = TRUE; - desc.Flags = swapchain_flags; - - window->swap_chain = - gst_d3d11_device_create_swap_chain (window->device, &desc); - } - - if (!window->swap_chain) { - GST_ERROR_OBJECT (window, "Cannot create swapchain"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Cannot create swapchain"); - - return FALSE; - } - - /* disable alt+enter here. It should be manually handled */ - gst_d3d11_device_lock (window->device); - gst_d3d11_window_disable_alt_enter (window, - window->swap_chain, desc.OutputWindow); - gst_d3d11_device_unlock (window->device); - -#if (DXGI_HEADER_VERSION >= 5) - if (swapchain4_available) { - HRESULT hr; - GST_DXGI_COLOR_SPACE_TYPE ctype; - - if (gst_d3d11_window_color_space_from_video_info (window, - &window->render_info, (IDXGISwapChain4 *) window->swap_chain, - &ctype)) { - hr = IDXGISwapChain4_SetColorSpace1 ((IDXGISwapChain4 *) - window->swap_chain, (DXGI_COLOR_SPACE_TYPE) ctype); - - if (!gst_d3d11_result (hr, window->device)) { - GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x", - ctype, (guint) hr); - } else { - GST_DEBUG_OBJECT (window, "Set colorspace %d", ctype); - } - - if (have_cll && have_mastering) { - DXGI_HDR_METADATA_HDR10 metadata = { 0, }; - - GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain"); - - mastering_display_gst_to_dxgi (&window->mastering_display_info, - &window->content_light_level, &metadata); - - hr = IDXGISwapChain4_SetHDRMetaData ((IDXGISwapChain4 *) - window->swap_chain, DXGI_HDR_METADATA_TYPE_HDR10, - sizeof (DXGI_HDR_METADATA_HDR10), &metadata); - if (!gst_d3d11_result (hr, window->device)) { - GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x", - (guint) hr); - } - } - } else { - GST_DEBUG_OBJECT (window, - "Could not get color space from %" GST_PTR_FORMAT, caps); - } - } -#endif - - gst_d3d11_window_on_resize (window, FALSE); - - if (!window->rtv) { - gst_d3d11_window_release_resources (window->device, window); - GST_ERROR_OBJECT (window, "Failed to setup internal resources"); - g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, - "Failed to setup internal resources"); - - return FALSE; - } - - if (window->requested_fullscreen != window->fullscreen) { - g_atomic_int_add (&window->pending_fullscreen_count, 1); - PostMessage (window->internal_win_id, WM_GST_D3D11_FULLSCREEN, 0, 0); - } - - GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain); - - return TRUE; -} - -void -gst_d3d11_window_set_window_handle (GstD3D11Window * window, guintptr id) -{ - g_return_if_fail (GST_IS_D3D11_WINDOW (window)); - - window->overlay_state = GST_D3D11_WINDOW_OVERLAY_STATE_NONE; - - if (window->visible) { - ShowWindow (window->internal_win_id, SW_HIDE); - window->visible = FALSE; - } - - release_external_win_id (window); - window->external_win_id = (HWND) id; - set_external_win_id (window); - - if (window->external_win_id) - window->overlay_state = GST_D3D11_WINDOW_OVERLAY_STATE_OPENED; -} - -void -gst_d3d11_window_show (GstD3D11Window * window) -{ - gint width, height; - - g_return_if_fail (GST_IS_D3D11_WINDOW (window)); - - width = window->width; - height = window->height; - - if (!window->visible) { - /* if no parent the real size has to be set now because this has not been done - * when at window creation */ - if (!window->external_win_id) { - RECT rect; - GetClientRect (window->internal_win_id, &rect); - width += 2 * GetSystemMetrics (SM_CXSIZEFRAME); - height += - 2 * GetSystemMetrics (SM_CYSIZEFRAME) + - GetSystemMetrics (SM_CYCAPTION); - MoveWindow (window->internal_win_id, rect.left, rect.top, width, - height, FALSE); - } - - ShowWindow (window->internal_win_id, SW_SHOW); - window->visible = TRUE; - } -} - -void -gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, gint x, gint y, - gint width, gint height) -{ - g_return_if_fail (GST_IS_D3D11_WINDOW (window)); - - if (x < 0 || y < 0) { - x = y = 0; - width = window->surface_width; - height = window->surface_height; - } - - if (x < 0 || y < 0 || width <= 0 || height <= 0) - return; - - /* TODO: resize window and view */ -} - -static GstFlowReturn -gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer) -{ - HRESULT hr; - UINT present_flags = 0; - - if (buffer) { - gst_buffer_replace (&self->cached_buffer, buffer); - } - - if (self->cached_buffer) { - ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES]; - gint i, j, k; - - for (i = 0, j = 0; i < gst_buffer_n_memory (self->cached_buffer); i++) { - GstD3D11Memory *mem = - (GstD3D11Memory *) gst_buffer_peek_memory (self->cached_buffer, i); - for (k = 0; k < mem->num_shader_resource_views; k++) { - srv[j] = mem->shader_resource_view[k]; - j++; - } - } - - if (self->first_present) { - gst_d3d11_color_converter_update_rect (self->converter, - &self->render_rect); - gst_d3d11_overlay_compositor_update_rect (self->compositor, - &self->render_rect); - } - - gst_d3d11_color_converter_convert_unlocked (self->converter, - srv, &self->rtv); - - gst_d3d11_overlay_compositor_upload (self->compositor, self->cached_buffer); - gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &self->rtv); - -#if (DXGI_HEADER_VERSION >= 5) - if (self->allow_tearing) { - present_flags |= DXGI_PRESENT_ALLOW_TEARING; - } -#endif - -#if (DXGI_HEADER_VERSION >= 2) - if (self->have_swapchain1) { - DXGI_PRESENT_PARAMETERS present_params = { 0, }; - if (self->first_present) { - /* the first present should not specify dirty-rect */ - hr = IDXGISwapChain1_Present1 ((IDXGISwapChain1 *) self->swap_chain, - 0, present_flags, &present_params); - } else { - present_params.DirtyRectsCount = 1; - present_params.pDirtyRects = &self->render_rect; - hr = IDXGISwapChain1_Present1 ((IDXGISwapChain1 *) self->swap_chain, - 0, present_flags, &present_params); - } - } else -#endif - { - hr = IDXGISwapChain_Present (self->swap_chain, 0, present_flags); - } - - self->first_present = FALSE; - - if (!gst_d3d11_result (hr, self->device)) { - GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x", - (guint) hr); - } - } - - return GST_FLOW_OK; -} - -GstFlowReturn -gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer, - GstVideoRectangle * rect) -{ - GstMemory *mem; - GstFlowReturn ret; - - g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR); - g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR); - - mem = gst_buffer_peek_memory (buffer, 0); - if (!gst_is_d3d11_memory (mem)) { - GST_ERROR_OBJECT (window, "Invalid buffer"); - - return GST_FLOW_ERROR; - } - - g_mutex_lock (&window->lock); - if ((!window->external_win_id && - window->overlay_state == GST_D3D11_WINDOW_OVERLAY_STATE_CLOSED) - || !window->internal_win_id) { - GST_ERROR_OBJECT (window, "Output window was closed"); - g_mutex_unlock (&window->lock); - - return GST_D3D11_WINDOW_FLOW_CLOSED; - } - g_mutex_unlock (&window->lock); - - gst_d3d11_device_lock (window->device); - ret = gst_d3d111_window_present (window, buffer); - gst_d3d11_device_unlock (window->device); - - return ret; -} - -gboolean -gst_d3d11_window_flush (GstD3D11Window * window) -{ - g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE); - - gst_clear_buffer (&window->cached_buffer); - - return TRUE; -} diff --git a/sys/d3d11/gstd3d11window.cpp b/sys/d3d11/gstd3d11window.cpp new file mode 100644 index 0000000..dde2882 --- /dev/null +++ b/sys/d3d11/gstd3d11window.cpp @@ -0,0 +1,940 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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 "gstd3d11window.h" +#include "gstd3d11device.h" +#include "gstd3d11memory.h" +#include "gstd3d11utils.h" + +#if GST_D3D11_WINAPI_ONLY_APP +/* workaround for GetCurrentTime collision */ +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif +#include +#include +#include +#include + +using namespace Microsoft::WRL; +#endif + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug); +#define GST_CAT_DEFAULT gst_d3d11_window_debug +} + +enum +{ + PROP_0, + PROP_D3D11_DEVICE, + PROP_FORCE_ASPECT_RATIO, + PROP_ENABLE_NAVIGATION_EVENTS, + PROP_FULLSCREEN_TOGGLE_MODE, + PROP_FULLSCREEN, + PROP_WINDOW_HANDLE, +}; + +#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE +#define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE +#define DEFAULT_FULLSCREEN FALSE + +enum +{ + SIGNAL_KEY_EVENT, + SIGNAL_MOUSE_EVENT, + SIGNAL_LAST +}; + +static guint d3d11_window_signals[SIGNAL_LAST] = { 0, }; + +GType +gst_d3d11_window_fullscreen_toggle_mode_type (void) +{ + static volatile gsize mode_type = 0; + + if (g_once_init_enter (&mode_type)) { + static const GFlagsValue mode_types[] = { + {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE, + "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"}, + {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER, + "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"}, + {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY, + "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"}, + {0, NULL, NULL}, + }; + GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode", + mode_types); + g_once_init_leave (&mode_type, tmp); + } + + return (GType) mode_type; +} + +#define gst_d3d11_window_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT); + +static void gst_d3d11_window_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_d3d11_window_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_d3d11_window_dispose (GObject * object); +static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self, + GstBuffer * buffer); + +static void +gst_d3d11_window_class_init (GstD3D11WindowClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gst_d3d11_window_set_property; + gobject_class->get_property = gst_d3d11_window_get_property; + gobject_class->dispose = gst_d3d11_window_dispose; + + g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE, + g_param_spec_object ("d3d11device", "D3D11 Device", + "GstD3D11Device object for creating swapchain", + GST_TYPE_D3D11_DEVICE, + (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, + g_param_spec_boolean ("force-aspect-ratio", + "Force aspect ratio", + "When enabled, scaling will respect original aspect ratio", + DEFAULT_FORCE_ASPECT_RATIO, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS, + g_param_spec_boolean ("enable-navigation-events", + "Enable navigation events", + "When enabled, signals for navigation events are emitted", + DEFAULT_ENABLE_NAVIGATION_EVENTS, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE, + g_param_spec_flags ("fullscreen-toggle-mode", + "Full screen toggle mode", + "Full screen toggle mode used to trigger fullscreen mode change", + GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_FULLSCREEN, + g_param_spec_boolean ("fullscreen", + "fullscreen", + "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"", + DEFAULT_FULLSCREEN, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE, + g_param_spec_pointer ("window-handle", + "Window Handle", "External Window Handle", + (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); + + d3d11_window_signals[SIGNAL_KEY_EVENT] = + g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + + d3d11_window_signals[SIGNAL_MOUSE_EVENT] = + g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +static void +gst_d3d11_window_init (GstD3D11Window * self) +{ + self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; + self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; + self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE; + self->fullscreen = DEFAULT_FULLSCREEN; + + self->aspect_ratio_n = 1; + self->aspect_ratio_d = 1; +} + +static void +gst_d3d11_window_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstD3D11Window *self = GST_D3D11_WINDOW (object); + GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object); + + switch (prop_id) { + case PROP_D3D11_DEVICE: + self->device = (GstD3D11Device *) g_value_dup_object (value); + break; + case PROP_FORCE_ASPECT_RATIO: + { + self->force_aspect_ratio = g_value_get_boolean (value); + if (self->swap_chain) + klass->update_swap_chain (self); + break; + } + case PROP_ENABLE_NAVIGATION_EVENTS: + self->enable_navigation_events = g_value_get_boolean (value); + break; + case PROP_FULLSCREEN_TOGGLE_MODE: + self->fullscreen_toggle_mode = + (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value); + break; + case PROP_FULLSCREEN: + { + self->requested_fullscreen = g_value_get_boolean (value); + if (self->swap_chain) + klass->change_fullscreen_mode (self); + break; + } + case PROP_WINDOW_HANDLE: + self->external_handle = (guintptr) g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_d3d11_window_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D11Window *self = GST_D3D11_WINDOW (object); + + switch (prop_id) { + case PROP_ENABLE_NAVIGATION_EVENTS: + g_value_set_boolean (value, self->enable_navigation_events); + break; + case PROP_FORCE_ASPECT_RATIO: + g_value_set_boolean (value, self->force_aspect_ratio); + break; + case PROP_FULLSCREEN_TOGGLE_MODE: + g_value_set_flags (value, self->fullscreen_toggle_mode); + break; + case PROP_FULLSCREEN: + g_value_set_boolean (value, self->fullscreen); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_d3d11_window_release_resources (GstD3D11Device * device, + GstD3D11Window * window) +{ + if (window->rtv) { + window->rtv->Release (); + window->rtv = NULL; + } + + if (window->swap_chain) { + window->swap_chain->Release (); + window->swap_chain = NULL; + } +} + +static void +gst_d3d11_window_dispose (GObject * object) +{ + GstD3D11Window *self = GST_D3D11_WINDOW (object); + + if (self->device) { + gst_d3d11_window_release_resources (self->device, self); + } + + if (self->converter) { + gst_d3d11_color_converter_free (self->converter); + self->converter = NULL; + } + + if (self->compositor) { + gst_d3d11_overlay_compositor_free (self->compositor); + self->compositor = NULL; + } + + gst_clear_buffer (&self->cached_buffer); + gst_clear_object (&self->device); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +void +gst_d3d11_window_on_resize (GstD3D11Window * window, guint width, guint height) +{ + HRESULT hr; + ID3D11Device *device_handle; + D3D11_TEXTURE2D_DESC desc; + DXGI_SWAP_CHAIN_DESC swap_desc; + ID3D11Texture2D *backbuffer = NULL; + GstVideoRectangle src_rect, dst_rect, rst_rect; + IDXGISwapChain *swap_chain; + + gst_d3d11_device_lock (window->device); + if (!window->swap_chain) + goto done; + + device_handle = gst_d3d11_device_get_device_handle (window->device); + swap_chain = window->swap_chain; + + if (window->rtv) { + window->rtv->Release (); + window->rtv = NULL; + } + + /* Set zero width and height here. dxgi will decide client area by itself */ + swap_chain->GetDesc (&swap_desc); + hr = swap_chain->ResizeBuffers (0, width, height, DXGI_FORMAT_UNKNOWN, + swap_desc.Flags); + if (!gst_d3d11_result (hr, window->device)) { + GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr); + goto done; + } + + hr = swap_chain->GetBuffer (0, IID_ID3D11Texture2D, (void **) &backbuffer); + if (!gst_d3d11_result (hr, window->device)) { + GST_ERROR_OBJECT (window, + "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr); + goto done; + } + + backbuffer->GetDesc (&desc); + window->surface_width = desc.Width; + window->surface_height = desc.Height; + + width = window->width; + height = window->height; + + { + src_rect.x = 0; + src_rect.y = 0; + src_rect.w = width * window->aspect_ratio_n; + src_rect.h = height * window->aspect_ratio_d; + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.w = window->surface_width; + dst_rect.h = window->surface_height; + + if (window->force_aspect_ratio) { + src_rect.w = width * window->aspect_ratio_n; + src_rect.h = height * window->aspect_ratio_d; + + gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE); + } else { + rst_rect = dst_rect; + } + } + + window->render_rect.left = rst_rect.x; + window->render_rect.top = rst_rect.y; + window->render_rect.right = rst_rect.x + rst_rect.w; + window->render_rect.bottom = rst_rect.y + rst_rect.h; + + GST_LOG_OBJECT (window, + "New client area %dx%d, render rect x: %d, y: %d, %dx%d", + desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h); + + hr = device_handle->CreateRenderTargetView ((ID3D11Resource *) backbuffer, + NULL, &window->rtv); + if (!gst_d3d11_result (hr, window->device)) { + GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x", + (guint) hr); + + goto done; + } + + window->first_present = TRUE; + + /* redraw the last scene if cached buffer exits */ + gst_d3d111_window_present (window, NULL); + +done: + if (backbuffer) + backbuffer->Release (); + + gst_d3d11_device_unlock (window->device); +} + +void +gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event, + const gchar * key) +{ + g_return_if_fail (GST_IS_D3D11_WINDOW (window)); + + if (!window->enable_navigation_events) + return; + + g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key); +} + +void +gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event, + gint button, gdouble x, gdouble y) +{ + g_return_if_fail (GST_IS_D3D11_WINDOW (window)); + + if (!window->enable_navigation_events) + return; + + g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0, + event, button, x, y); +} + +#if (DXGI_HEADER_VERSION >= 5) +static inline UINT16 +fraction_to_uint (guint num, guint den, guint scale) +{ + gdouble val; + gst_util_fraction_to_double (num, den, &val); + + return (UINT16) val *scale; +} + +static void +mastering_display_gst_to_dxgi (GstVideoMasteringDisplayInfo * m, + GstVideoContentLightLevel * c, DXGI_HDR_METADATA_HDR10 * meta) +{ + meta->RedPrimary[0] = fraction_to_uint (m->Rx_n, m->Rx_d, 50000); + meta->RedPrimary[1] = fraction_to_uint (m->Ry_n, m->Ry_d, 50000); + meta->GreenPrimary[0] = fraction_to_uint (m->Gx_n, m->Gx_d, 50000); + meta->GreenPrimary[1] = fraction_to_uint (m->Gy_n, m->Gy_d, 50000); + meta->BluePrimary[0] = fraction_to_uint (m->Bx_n, m->Bx_d, 50000); + meta->BluePrimary[1] = fraction_to_uint (m->By_n, m->By_d, 50000); + meta->WhitePoint[0] = fraction_to_uint (m->Wx_n, m->Wx_d, 50000); + meta->WhitePoint[1] = fraction_to_uint (m->Wy_n, m->Wy_d, 50000); + meta->MaxMasteringLuminance = + fraction_to_uint (m->max_luma_n, m->max_luma_d, 1); + meta->MinMasteringLuminance = + fraction_to_uint (m->min_luma_n, m->min_luma_d, 1); + meta->MaxContentLightLevel = fraction_to_uint (c->maxCLL_n, c->maxCLL_d, 1); + meta->MaxFrameAverageLightLevel = + fraction_to_uint (c->maxFALL_n, c->maxFALL_d, 1); +} + +/* missing in mingw header... */ +typedef enum +{ + GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0, + GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1, + GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2, + GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3, + GST_DXGI_COLOR_SPACE_RESERVED = 4, + GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6, + GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8, + GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10, + GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11, + GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13, + GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16, + GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18, + GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19, + GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20, + GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23, + GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24, + GST_DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF +} GST_DXGI_COLOR_SPACE_TYPE; + +typedef struct +{ + GST_DXGI_COLOR_SPACE_TYPE type; + GstVideoColorRange range; + GstVideoTransferFunction transfer; + GstVideoColorPrimaries primaries; +} DxgiColorSpaceMap; + +/* https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type */ +static const DxgiColorSpaceMap colorspace_map[] = { + /* RGB, bt709 */ + {GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255, + GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709}, + {GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255, + GST_VIDEO_TRANSFER_GAMMA10, GST_VIDEO_COLOR_PRIMARIES_BT709}, + {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235, + GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709}, + /* RGB, bt2020 */ + {GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255, + GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020}, + {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235, + GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020}, + /* RGB, bt2084 */ + {GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255, + GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020}, + {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020, + GST_VIDEO_COLOR_RANGE_16_235, + GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020}, + /* RGB, SRGB */ + {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235, + GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT709}, + {GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235, + GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT2020}, +}; + +static gboolean +gst_d3d11_window_color_space_from_video_info (GstD3D11Window * self, + GstVideoInfo * info, IDXGISwapChain4 * swapchain, + GST_DXGI_COLOR_SPACE_TYPE * dxgi_colorspace) +{ + guint i; + gint best_idx = -1; + gint best_score = 0; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (dxgi_colorspace != NULL, FALSE); + + /* We render only RGB for now */ + if (!GST_VIDEO_FORMAT_INFO_IS_RGB (info->finfo)) + return FALSE; + + /* find the best matching colorspace */ + for (i = 0; i < G_N_ELEMENTS (colorspace_map); i++) { + GstVideoColorimetry *cinfo = &info->colorimetry; + UINT can_support = 0; + HRESULT hr; + gint score = 0; + GstVideoTransferFunction transfer = cinfo->transfer; + DXGI_COLOR_SPACE_TYPE type = (DXGI_COLOR_SPACE_TYPE) colorspace_map[i].type; + + if (transfer == GST_VIDEO_TRANSFER_BT2020_12) + transfer = GST_VIDEO_TRANSFER_BT2020_10; + + hr = swapchain->CheckColorSpaceSupport (type, &can_support); + + if (SUCCEEDED (hr) && + (can_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) == + DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) { + if (cinfo->range == colorspace_map[i].range) + score++; + + if (transfer == colorspace_map[i].transfer) + score++; + + if (cinfo->primaries == colorspace_map[i].primaries) + score++; + + GST_DEBUG_OBJECT (self, + "colorspace %d supported, score %d", type, score); + + if (score > best_score) { + best_score = score; + best_idx = i; + } + } else { + GST_DEBUG_OBJECT (self, + "colorspace %d not supported", type); + } + } + + if (best_idx < 0) + return FALSE; + + *dxgi_colorspace = colorspace_map[best_idx].type; + + return TRUE; +} +#endif + +gboolean +gst_d3d11_window_prepare (GstD3D11Window * window, guint width, guint height, + guint aspect_ratio_n, guint aspect_ratio_d, GstCaps * caps, GError ** error) +{ + GstD3D11WindowClass *klass; + GstCaps *render_caps; + guint swapchain_flags = 0; +#if (DXGI_HEADER_VERSION >= 5) + gboolean have_cll = FALSE; + gboolean have_mastering = FALSE; + gboolean swapchain4_available = FALSE; +#endif + + g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE); + g_return_val_if_fail (aspect_ratio_n > 0, FALSE); + g_return_val_if_fail (aspect_ratio_d > 0, FALSE); + + GST_DEBUG_OBJECT (window, "Prepare window with %dx%d caps %" GST_PTR_FORMAT, + width, height, caps); + + render_caps = gst_d3d11_device_get_supported_caps (window->device, + (D3D11_FORMAT_SUPPORT) (D3D11_FORMAT_SUPPORT_TEXTURE2D | + D3D11_FORMAT_SUPPORT_DISPLAY)); + + GST_DEBUG_OBJECT (window, "rendering caps %" GST_PTR_FORMAT, render_caps); + render_caps = gst_d3d11_caps_fixate_format (caps, render_caps); + + if (!render_caps || gst_caps_is_empty (render_caps)) { + GST_ERROR_OBJECT (window, "Couldn't define render caps"); + g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, + "Couldn't define render caps"); + gst_clear_caps (&render_caps); + + return FALSE; + } + + render_caps = gst_caps_fixate (render_caps); + gst_video_info_from_caps (&window->render_info, render_caps); + gst_clear_caps (&render_caps); + + window->render_format = + gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (&window->render_info)); + if (!window->render_format || + window->render_format->dxgi_format == DXGI_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (window, "Unknown dxgi render format"); + g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, + "Unknown dxgi render format"); + + return FALSE; + } + + gst_video_info_from_caps (&window->info, caps); + + if (window->converter) + gst_d3d11_color_converter_free (window->converter); + window->converter = NULL; + + if (window->compositor) + gst_d3d11_overlay_compositor_free (window->compositor); + window->compositor = NULL; + + /* preserve upstream colorimetry */ + window->render_info.width = width; + window->render_info.height = height; + + window->render_info.colorimetry.primaries = + window->info.colorimetry.primaries; + window->render_info.colorimetry.transfer = window->info.colorimetry.transfer; + + window->converter = + gst_d3d11_color_converter_new (window->device, &window->info, + &window->render_info); + + if (!window->converter) { + GST_ERROR_OBJECT (window, "Cannot create converter"); + g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, + "Cannot create converter"); + + return FALSE; + } + + window->compositor = + gst_d3d11_overlay_compositor_new (window->device, &window->render_info); + if (!window->compositor) { + GST_ERROR_OBJECT (window, "Cannot create overlay compositor"); + g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, + "Cannot create overlay compositor"); + + return FALSE; + } + + window->allow_tearing = FALSE; +#if (DXGI_HEADER_VERSION >= 5) + if (!gst_video_content_light_level_from_caps (&window->content_light_level, + caps)) { + gst_video_content_light_level_init (&window->content_light_level); + } else { + have_cll = TRUE; + } + + if (!gst_video_mastering_display_info_from_caps + (&window->mastering_display_info, caps)) { + gst_video_mastering_display_info_init (&window->mastering_display_info); + } else { + have_mastering = TRUE; + } + + if (gst_d3d11_device_get_chosen_dxgi_factory_version (window->device) >= + GST_D3D11_DXGI_FACTORY_5) { + GST_DEBUG_OBJECT (window, "DXGI 1.5 interface is available"); + swapchain4_available = TRUE; + + g_object_get (window->device, + "allow-tearing", &window->allow_tearing, NULL); + if (window->allow_tearing) { + GST_DEBUG_OBJECT (window, "device support tearning"); + swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + } +#endif + + if (window->swap_chain) { + gst_d3d11_device_lock (window->device); + gst_d3d11_window_release_resources (window->device, window); + gst_d3d11_device_unlock (window->device); + } + + window->aspect_ratio_n = aspect_ratio_n; + window->aspect_ratio_d = aspect_ratio_d; + + window->render_rect.left = 0; + window->render_rect.top = 0; + window->render_rect.right = width; + window->render_rect.bottom = height; + + window->width = width; + window->height = height; + + klass = GST_D3D11_WINDOW_GET_CLASS (window); + if (!klass->create_swap_chain (window, window->render_format->dxgi_format, + width, height, swapchain_flags, &window->swap_chain)) { + GST_ERROR_OBJECT (window, "Cannot create swapchain"); + g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, + "Cannot create swapchain"); + + return FALSE; + } +#if (DXGI_HEADER_VERSION >= 5) + if (swapchain4_available) { + HRESULT hr; + GST_DXGI_COLOR_SPACE_TYPE ctype; + IDXGISwapChain4* swap_chain4 = (IDXGISwapChain4 *) window->swap_chain; + + if (gst_d3d11_window_color_space_from_video_info (window, + &window->render_info, swap_chain4, &ctype)) { + hr = swap_chain4->SetColorSpace1 ((DXGI_COLOR_SPACE_TYPE) ctype); + + if (!gst_d3d11_result (hr, window->device)) { + GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x", + ctype, (guint) hr); + } else { + GST_DEBUG_OBJECT (window, "Set colorspace %d", ctype); + } + + if (have_cll && have_mastering) { + DXGI_HDR_METADATA_HDR10 metadata = { 0, }; + + GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain"); + + mastering_display_gst_to_dxgi (&window->mastering_display_info, + &window->content_light_level, &metadata); + + hr = swap_chain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10, + sizeof (DXGI_HDR_METADATA_HDR10), &metadata); + if (!gst_d3d11_result (hr, window->device)) { + GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x", + (guint) hr); + } + } + } else { + GST_DEBUG_OBJECT (window, + "Could not get color space from %" GST_PTR_FORMAT, caps); + } + } +#endif + + if (window->requested_fullscreen != window->fullscreen) { + klass->change_fullscreen_mode (window); + } + + GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain); + + return TRUE; +} + +void +gst_d3d11_window_show (GstD3D11Window * window) +{ + GstD3D11WindowClass *klass; + + g_return_if_fail (GST_IS_D3D11_WINDOW (window)); + + klass = GST_D3D11_WINDOW_GET_CLASS (window); + + if (klass->show) + klass->show (window); +} + +void +gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, gint x, gint y, + gint width, gint height) +{ + g_return_if_fail (GST_IS_D3D11_WINDOW (window)); + + /* TODO: resize window and view */ +} + +static GstFlowReturn +gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer) +{ + GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self); + GstFlowReturn ret = GST_FLOW_OK; + guint present_flags = 0; + + if (buffer) { + gst_buffer_replace (&self->cached_buffer, buffer); + } + + if (self->cached_buffer) { + ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES]; + guint i, j, k; + + for (i = 0, j = 0; i < gst_buffer_n_memory (self->cached_buffer); i++) { + GstD3D11Memory *mem = + (GstD3D11Memory *) gst_buffer_peek_memory (self->cached_buffer, i); + for (k = 0; k < mem->num_shader_resource_views; k++) { + srv[j] = mem->shader_resource_view[k]; + j++; + } + } + + if (self->first_present) { + gst_d3d11_color_converter_update_rect (self->converter, + &self->render_rect); + gst_d3d11_overlay_compositor_update_rect (self->compositor, + &self->render_rect); + } + + gst_d3d11_color_converter_convert_unlocked (self->converter, + srv, &self->rtv); + + gst_d3d11_overlay_compositor_upload (self->compositor, self->cached_buffer); + gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &self->rtv); + +#if (DXGI_HEADER_VERSION >= 5) + if (self->allow_tearing) { + present_flags |= DXGI_PRESENT_ALLOW_TEARING; + } +#endif + + ret = klass->present (self, present_flags); + + self->first_present = FALSE; + } + + return ret; +} + +GstFlowReturn +gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer, + GstVideoRectangle * rect) +{ + GstMemory *mem; + GstFlowReturn ret; + + g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR); + g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR); + + mem = gst_buffer_peek_memory (buffer, 0); + if (!gst_is_d3d11_memory (mem)) { + GST_ERROR_OBJECT (window, "Invalid buffer"); + + return GST_FLOW_ERROR; + } + + gst_d3d11_device_lock (window->device); + ret = gst_d3d111_window_present (window, buffer); + gst_d3d11_device_unlock (window->device); + + return ret; +} + +gboolean +gst_d3d11_window_unlock (GstD3D11Window * window) +{ + GstD3D11WindowClass *klass; + gboolean ret = TRUE; + + g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE); + + klass = GST_D3D11_WINDOW_GET_CLASS (window); + + if (klass->unlock) + ret = klass->unlock (window); + + return ret; +} + +gboolean +gst_d3d11_window_unlock_stop (GstD3D11Window * window) +{ + GstD3D11WindowClass *klass; + gboolean ret = TRUE; + + g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE); + + klass = GST_D3D11_WINDOW_GET_CLASS (window); + + if (klass->unlock_stop) + ret = klass->unlock_stop (window); + + gst_d3d11_device_lock (window->device); + gst_clear_buffer (&window->cached_buffer); + gst_d3d11_device_unlock (window->device); + + return ret; +} + +GstD3D11WindowNativeType +gst_d3d11_window_get_native_type_from_handle (guintptr handle) +{ + if (!handle) + return GST_D3D11_WINDOW_NATIVE_TYPE_NONE; + +#if (!GST_D3D11_WINAPI_ONLY_APP) + if (IsWindow ((HWND) handle)) + return GST_D3D11_WINDOW_NATIVE_TYPE_HWND; +#else + { + ComPtr window = reinterpret_cast (handle); + ComPtr core_window; + ComPtr panel; + + if (SUCCEEDED (window.As (&core_window))) + return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW; + + if (SUCCEEDED (window.As (&panel))) + return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL; + } +#endif + + return GST_D3D11_WINDOW_NATIVE_TYPE_NONE; +} + +const gchar * +gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type) +{ + switch (type) { + case GST_D3D11_WINDOW_NATIVE_TYPE_NONE: + return "none"; + case GST_D3D11_WINDOW_NATIVE_TYPE_HWND: + return "hwnd"; + case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW: + return "core-window"; + case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL: + return "swap-chain-panel"; + default: + break; + } + + return "none"; +} \ No newline at end of file diff --git a/sys/d3d11/gstd3d11window.h b/sys/d3d11/gstd3d11window.h index b9c7366..6118fd7 100644 --- a/sys/d3d11/gstd3d11window.h +++ b/sys/d3d11/gstd3d11window.h @@ -31,9 +31,9 @@ G_BEGIN_DECLS #define GST_TYPE_D3D11_WINDOW (gst_d3d11_window_get_type()) -#define GST_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_WINDOW, GstD3D11Window)) -#define GST_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass)) -#define GST_IS_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_WINDOW)) +#define GST_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_WINDOW, GstD3D11Window)) +#define GST_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass)) +#define GST_IS_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_WINDOW)) #define GST_IS_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_WINDOW)) #define GST_D3D11_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass)) #define GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE (gst_d3d11_window_fullscreen_toggle_mode_type()) @@ -45,13 +45,6 @@ typedef struct _GstD3D11WindowClass GstD3D11WindowClass; typedef enum { - GST_D3D11_WINDOW_OVERLAY_STATE_NONE = 0, - GST_D3D11_WINDOW_OVERLAY_STATE_OPENED, - GST_D3D11_WINDOW_OVERLAY_STATE_CLOSED, -} GstD3D11WindowOverlayState; - -typedef enum -{ GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE = 0, GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER = (1 << 1), GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY = (1 << 2), @@ -59,10 +52,30 @@ typedef enum GType gst_d3d11_window_fullscreen_toggle_mode_type (void); +typedef enum +{ + GST_D3D11_WINDOW_NATIVE_TYPE_NONE = 0, + GST_D3D11_WINDOW_NATIVE_TYPE_HWND, + GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW, + GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL, +} GstD3D11WindowNativeType; + struct _GstD3D11Window { GstObject parent; + /*< protected >*/ + gboolean initialized; + GstD3D11Device *device; + guintptr external_handle; + + /* properties */ + gboolean force_aspect_ratio; + gboolean enable_navigation_events; + GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode; + gboolean requested_fullscreen; + gboolean fullscreen; + GstVideoInfo info; GstVideoInfo render_info; const GstD3D11Format *render_format; @@ -78,12 +91,6 @@ struct _GstD3D11Window /* requested rect via gst_d3d11_window_render */ GstVideoRectangle rect; - GMutex lock; - GCond cond; - - GMainContext *main_context; - GMainLoop *loop; - guint width; guint height; @@ -93,77 +100,83 @@ struct _GstD3D11Window guint aspect_ratio_n; guint aspect_ratio_d; - gboolean visible; - - GSource *msg_source; - GIOChannel *msg_io_channel; - - GThread *thread; - - gboolean created; - - HWND internal_win_id; - HWND external_win_id; - GstD3D11WindowOverlayState overlay_state; - - HDC device_handle; IDXGISwapChain *swap_chain; ID3D11RenderTargetView *rtv; - DXGI_FORMAT format; - gboolean first_present; - gboolean have_swapchain1; - - GstD3D11Device *device; - - /* properties */ - gboolean force_aspect_ratio; - gboolean enable_navigation_events; - GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode; - gboolean requested_fullscreen; - gboolean fullscreen; - - /* atomic */ - volatile gint pending_fullscreen_count; GstBuffer *cached_buffer; + gboolean first_present; gboolean allow_tearing; - - /* fullscreen related */ - RECT restore_rect; - LONG restore_style; }; struct _GstD3D11WindowClass { GstObjectClass object_class; + + void (*show) (GstD3D11Window * window); + + void (*update_swap_chain) (GstD3D11Window * window); + + void (*change_fullscreen_mode) (GstD3D11Window * window); + + gboolean (*create_swap_chain) (GstD3D11Window * window, + DXGI_FORMAT format, + guint width, + guint height, + guint swapchain_flags, + IDXGISwapChain ** swap_chain); + + GstFlowReturn (*present) (GstD3D11Window * window, + guint present_flags); + + gboolean (*unlock) (GstD3D11Window * window); + + gboolean (*unlock_stop) (GstD3D11Window * window); }; -GType gst_d3d11_window_get_type (void); +GType gst_d3d11_window_get_type (void); + +void gst_d3d11_window_show (GstD3D11Window * window); + +void gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, + gint x, gint y, + gint width, gint height); + +gboolean gst_d3d11_window_prepare (GstD3D11Window * window, + guint width, + guint height, + guint aspect_ratio_n, + guint aspect_ratio_d, + GstCaps * caps, + GError ** error); + +GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window, + GstBuffer * buffer, + GstVideoRectangle * src_rect); + +gboolean gst_d3d11_window_unlock (GstD3D11Window * window); -GstD3D11Window * gst_d3d11_window_new (GstD3D11Device * device); +gboolean gst_d3d11_window_unlock_stop (GstD3D11Window * window); -void gst_d3d11_window_show (GstD3D11Window * window); +void gst_d3d11_window_on_resize (GstD3D11Window * window, + guint width, + guint height); -void gst_d3d11_window_set_window_handle (GstD3D11Window * window, - guintptr id); +void gst_d3d11_window_on_key_event (GstD3D11Window * window, + const gchar * event, + const gchar * key); -void gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, - gint x, gint y, - gint width, gint height); +void gst_d3d11_window_on_mouse_event (GstD3D11Window * window, + const gchar * event, + gint button, + gdouble x, + gdouble y); -gboolean gst_d3d11_window_prepare (GstD3D11Window * window, - guint width, - guint height, - guint aspect_ratio_n, - guint aspect_ratio_d, - GstCaps * caps, - GError ** error); +/* utils */ +GstD3D11WindowNativeType gst_d3d11_window_get_native_type_from_handle (guintptr handle); -GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window, - GstBuffer * buffer, - GstVideoRectangle * src_rect); +const gchar * gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type); -gboolean gst_d3d11_window_flush (GstD3D11Window * window); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstD3D11Window, gst_object_unref) G_END_DECLS diff --git a/sys/d3d11/gstd3d11window_corewindow.cpp b/sys/d3d11/gstd3d11window_corewindow.cpp new file mode 100644 index 0000000..7432609 --- /dev/null +++ b/sys/d3d11/gstd3d11window_corewindow.cpp @@ -0,0 +1,364 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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 "gstd3d11window.h" +#include "gstd3d11device.h" +#include "gstd3d11memory.h" +#include "gstd3d11utils.h" +#include "gstd3d11window_corewindow.h" + +/* workaround for GetCurrentTime collision */ +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::UI; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Graphics; + +typedef ABI::Windows::Foundation:: + __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowSizeChangedEventArgs_t + IWindowSizeChangedEventHandler; + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug); +#define GST_CAT_DEFAULT gst_d3d11_window_debug +} + +typedef struct _CoreWindowWinRTStorage +{ + ComPtr core_window; + EventRegistrationToken event_token; +} CoreWindowWinRTStorage; + +struct _GstD3D11WindowCoreWindow +{ + GstD3D11Window parent; + + CoreWindowWinRTStorage *storage; +}; + +#define gst_d3d11_window_core_window_parent_class parent_class +G_DEFINE_TYPE (GstD3D11WindowCoreWindow, gst_d3d11_window_core_window, + GST_TYPE_D3D11_WINDOW); + +static void gst_d3d11_window_core_window_constructed (GObject * object); +static void gst_d3d11_window_core_window_dispose (GObject * object); +static void gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window * window); +static void +gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window * window); +static gboolean +gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, guint swapchain_flags, + IDXGISwapChain ** swap_chain); +static GstFlowReturn +gst_d3d11_window_core_window_present (GstD3D11Window * window, + guint present_flags); +static void +gst_d3d11_window_core_window_on_resize (GstD3D11WindowCoreWindow * self, + guint width, guint height); + +static float +get_logical_dpi (void) +{ + ComPtr properties; + HRESULT hr; + HStringReference str_ref = + HStringReference (RuntimeClass_Windows_Graphics_Display_DisplayProperties); + + hr = GetActivationFactory (str_ref.Get(), properties.GetAddressOf()); + + if (gst_d3d11_result (hr, NULL)) { + float dpi = 96.0f; + + hr = properties->get_LogicalDpi (&dpi); + if (gst_d3d11_result (hr, NULL)) + return dpi; + } + + return 96.0f; +} + +static inline float dip_to_pixel (float dip) +{ + /* https://docs.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels */ + return dip * get_logical_dpi() / 96.0f; +} + +class CoreResizeHandler + : public RuntimeClass, + IWindowSizeChangedEventHandler> +{ +public: + CoreResizeHandler () {} + HRESULT RuntimeClassInitialize (GstD3D11WindowCoreWindow * listener) + { + if (!listener) + return E_INVALIDARG; + + window = listener; + return S_OK; + } + + IFACEMETHOD(Invoke) + (Core::ICoreWindow * sender, Core::IWindowSizeChangedEventArgs * args) + { + if (window) { + Size new_size; + HRESULT hr = args->get_Size(&new_size); + if (gst_d3d11_result (hr, NULL)) { + guint width, height; + + width = (guint) dip_to_pixel (new_size.Width); + height = (guint) dip_to_pixel (new_size.Height); + + gst_d3d11_window_core_window_on_resize (window, width, height); + } + } + + return S_OK; + } + +private: + GstD3D11WindowCoreWindow * window; +}; + +static void +gst_d3d11_window_core_window_class_init (GstD3D11WindowCoreWindowClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass); + + gobject_class->constructed = gst_d3d11_window_core_window_constructed; + gobject_class->dispose = gst_d3d11_window_core_window_dispose; + + window_class->update_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_update_swap_chain); + window_class->change_fullscreen_mode = + GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_change_fullscreen_mode); + window_class->create_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_create_swap_chain); + window_class->present = + GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_present); +} + +static void +gst_d3d11_window_core_window_init (GstD3D11WindowCoreWindow * self) +{ + self->storage = new CoreWindowWinRTStorage; +} + +static void +gst_d3d11_window_core_window_constructed (GObject * object) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (object); + GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (object); + CoreWindowWinRTStorage *storage = self->storage; + HRESULT hr; + ComPtr inspectable; + ComPtr resize_handler; + Rect bounds; + + if (!window->external_handle) { + GST_ERROR_OBJECT (self, "No external window handle"); + return; + } + + inspectable = reinterpret_cast (window->external_handle); + + hr = inspectable.As (&storage->core_window); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = storage->core_window->get_Bounds (&bounds); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + window->surface_width = dip_to_pixel (bounds.Width); + window->surface_height = dip_to_pixel (bounds.Height); + + GST_DEBUG_OBJECT (self, + "client size %dx%d", window->surface_width, window->surface_height); + + hr = MakeAndInitialize(&resize_handler, self); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = storage->core_window->add_SizeChanged (resize_handler.Get(), + &storage->event_token); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + window->initialized = TRUE; + return; + +error: + GST_ERROR_OBJECT (self, "Invalid window handle"); + return; +} + +static void +gst_d3d11_window_core_window_dispose (GObject * object) +{ + GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (object); + CoreWindowWinRTStorage *storage = self->storage; + + if (storage) { + if (storage->core_window) + storage->core_window->remove_SizeChanged (storage->event_token); + + delete storage; + } + + self->storage = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gboolean +gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, guint swapchain_flags, + IDXGISwapChain ** swap_chain) +{ + GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window); + ComPtr new_swapchain; + GstD3D11Device *device = window->device; + DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, }; + + desc1.Width = width; + desc1.Height = height; + desc1.Format = format; + desc1.Stereo = FALSE; + desc1.SampleDesc.Count = 1; + desc1.SampleDesc.Quality = 0; + desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc1.BufferCount = 2; + desc1.Scaling = DXGI_SCALING_NONE; + desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + desc1.Flags = swapchain_flags; + + new_swapchain = + gst_d3d11_device_create_swap_chain_for_core_window (device, + window->external_handle, &desc1, NULL); + + if (!new_swapchain) { + GST_ERROR_OBJECT (self, "Cannot create swapchain"); + return FALSE; + } + + new_swapchain.CopyTo (swap_chain); + + gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self), + window->surface_width, window->surface_height); + + return TRUE; +} + +static GstFlowReturn +gst_d3d11_window_core_window_present (GstD3D11Window * window, + guint present_flags) +{ + GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window); + HRESULT hr; + DXGI_PRESENT_PARAMETERS present_params = { 0, }; + IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; + + /* the first present should not specify dirty-rect */ + if (!window->first_present) { + present_params.DirtyRectsCount = 1; + present_params.pDirtyRects = &window->render_rect; + } + + hr = swap_chain->Present1 (0, present_flags, &present_params); + + if (!gst_d3d11_result (hr, window->device)) { + GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x", + (guint) hr); + } + + return GST_FLOW_OK; +} + +static void +gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window * window) +{ + gst_d3d11_window_on_resize (window, + window->surface_width, window->surface_height); + + return; +} + +static void +gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window * window) +{ + GST_FIXME_OBJECT (window, "Implement fullscreen mode change"); + + return; +} + +static void +gst_d3d11_window_core_window_on_resize (GstD3D11WindowCoreWindow * self, + guint width, guint height) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + + window->surface_width = width; + window->surface_height = height; + + GST_LOG_OBJECT (self, "New size %dx%d", width, height); + + gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self), width, height); +} + +GstD3D11Window * +gst_d3d11_window_core_window_new (GstD3D11Device * device, guintptr handle) +{ + GstD3D11Window *window; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + g_return_val_if_fail (handle, NULL); + + window = (GstD3D11Window *) + g_object_new (GST_TYPE_D3D11_WINDOW_CORE_WINDOW, + "d3d11device", device, "window-handle", handle, NULL); + + if (!window->initialized) { + gst_object_unref (window); + return NULL; + } + + g_object_ref_sink (window); + + return window; +} \ No newline at end of file diff --git a/sys/d3d11/gstd3d11window_corewindow.h b/sys/d3d11/gstd3d11window_corewindow.h new file mode 100644 index 0000000..c4670f0 --- /dev/null +++ b/sys/d3d11/gstd3d11window_corewindow.h @@ -0,0 +1,40 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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. + */ + +#ifndef __GST_D3D11_WINDOW_CORE_WINDOW_H__ +#define __GST_D3D11_WINDOW_CORE_WINDOW_H__ + +#include +#include +#include "gstd3d11_fwd.h" +#include "gstd3d11window.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D11_WINDOW_CORE_WINDOW (gst_d3d11_window_core_window_get_type()) +G_DECLARE_FINAL_TYPE (GstD3D11WindowCoreWindow, gst_d3d11_window_core_window, + GST, D3D11_WINDOW_CORE_WINDOW, GstD3D11Window); + +GstD3D11Window * gst_d3d11_window_core_window_new (GstD3D11Device * device, + guintptr handle); + +G_END_DECLS + +#endif /* __GST_D3D11_WINDOW_CORE_WINDOW_H__ */ diff --git a/sys/d3d11/gstd3d11window_swapchainpanel.cpp b/sys/d3d11/gstd3d11window_swapchainpanel.cpp new file mode 100644 index 0000000..c780d04 --- /dev/null +++ b/sys/d3d11/gstd3d11window_swapchainpanel.cpp @@ -0,0 +1,499 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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 "gstd3d11window.h" +#include "gstd3d11device.h" +#include "gstd3d11memory.h" +#include "gstd3d11utils.h" +#include "gstd3d11window_swapchainpanel.h" + +/* workaround for GetCurrentTime collision */ +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::UI; +using namespace ABI::Windows::Foundation; + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug); +#define GST_CAT_DEFAULT gst_d3d11_window_debug +} + +/* timeout to wait busy UI thread */ +#define DEFAULT_ASYNC_TIMEOUT (5 * 1000) + +typedef struct _SwapChainPanelWinRTStorage +{ + ComPtr panel; + ComPtr dispatcher; + ComPtr swapchain; + HANDLE cancellable; + EventRegistrationToken event_token; +} SwapChainPanelWinRTStorage; + +struct _GstD3D11WindowSwapChainPanel +{ + GstD3D11Window parent; + + SwapChainPanelWinRTStorage *storage; +}; + +#define gst_d3d11_window_swap_chain_panel_parent_class parent_class +G_DEFINE_TYPE (GstD3D11WindowSwapChainPanel, + gst_d3d11_window_swap_chain_panel, GST_TYPE_D3D11_WINDOW); + +static void gst_d3d11_window_swap_chain_panel_constructed (GObject * object); +static void gst_d3d11_window_swap_chain_panel_dispose (GObject * object); + +static void +gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window); +static void +gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window * + window); +static gboolean +gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, guint swapchain_flags, + IDXGISwapChain ** swap_chain); +static GstFlowReturn +gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window, + guint present_flags); +static gboolean +gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window); +static gboolean +gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window); +static void +gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11WindowSwapChainPanel * + self, guint width, guint height); + +class PanelResizeHandler + : public RuntimeClass, + Xaml::ISizeChangedEventHandler> +{ +public: + PanelResizeHandler () {} + HRESULT RuntimeClassInitialize (GstD3D11WindowSwapChainPanel * listener) + { + if (!listener) + return E_INVALIDARG; + + window = listener; + return S_OK; + } + + IFACEMETHOD(Invoke) + (IInspectable * sender, Xaml::ISizeChangedEventArgs * args) + { + if (window) { + Size new_size; + HRESULT hr = args->get_NewSize(&new_size); + if (SUCCEEDED(hr)) { + gst_d3d11_window_swap_chain_panel_on_resize (window, + new_size.Width, new_size.Height); + } + } + + return S_OK; + } + +private: + GstD3D11WindowSwapChainPanel * window; +}; + +template +static HRESULT +run_async (const ComPtr &dispatcher, HANDLE cancellable, + DWORD timeout, CB &&cb) +{ + ComPtr async_action; + HRESULT hr; + HRESULT async_hr; + boolean can_now; + DWORD wait_ret; + HANDLE event_handle[2]; + + hr = dispatcher->get_HasThreadAccess (&can_now); + + if (!gst_d3d11_result (hr, NULL)) + return hr; + + if (can_now) + return cb (); + + Event event (CreateEventEx (NULL, NULL, CREATE_EVENT_MANUAL_RESET, + EVENT_ALL_ACCESS)); + + if (!event.IsValid()) + return E_FAIL; + + auto handler = + Callback, + Core::IDispatchedHandler, FtmBase>>([&async_hr, &cb, &event] { + async_hr = cb (); + SetEvent (event.Get()); + return S_OK; + }); + + hr = dispatcher->RunAsync (Core::CoreDispatcherPriority_Normal, handler.Get(), + async_action.GetAddressOf()); + + if (!gst_d3d11_result (hr, NULL)) + return hr; + + event_handle[0] = event.Get(); + event_handle[1] = cancellable; + + wait_ret = WaitForMultipleObjects (2, event_handle, FALSE, timeout); + if (wait_ret != WAIT_OBJECT_0) + return E_FAIL; + + return async_hr; +} + +static HRESULT +get_panel_size (const ComPtr &dispatcher, + HANDLE cancellable, + const ComPtr &panel, Size *size) +{ + ComPtr ui; + HRESULT hr = panel.As (&ui); + + if (!gst_d3d11_result (hr, NULL)) + return hr; + + hr = run_async (dispatcher, cancellable, DEFAULT_ASYNC_TIMEOUT, + [ui, size] { + return ui->get_RenderSize (size); + }); + + return hr; +} + +static void +gst_d3d11_window_swap_chain_panel_class_init (GstD3D11WindowSwapChainPanelClass + * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass); + + gobject_class->constructed = gst_d3d11_window_swap_chain_panel_constructed; + gobject_class->dispose = gst_d3d11_window_swap_chain_panel_dispose; + + window_class->update_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_update_swap_chain); + window_class->change_fullscreen_mode = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_change_fullscreen_mode); + window_class->create_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_create_swap_chain); + window_class->present = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_present); + window_class->unlock = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock); + window_class->unlock_stop = + GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock_stop); +} + +static void +gst_d3d11_window_swap_chain_panel_init (GstD3D11WindowSwapChainPanel * self) +{ + self->storage = new SwapChainPanelWinRTStorage; + self->storage->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL); +} + +static void +gst_d3d11_window_swap_chain_panel_constructed (GObject * object) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (object); + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (object); + SwapChainPanelWinRTStorage *storage = self->storage; + HRESULT hr; + ComPtr inspectable; + ComPtr dependency_obj; + Size size; + ComPtr resize_handler; + ComPtr framework; + + if (!window->external_handle) { + GST_ERROR_OBJECT (self, "No external window handle"); + return; + } + + inspectable = reinterpret_cast (window->external_handle); + + hr = inspectable.As (&storage->panel); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = storage->panel.As (&dependency_obj); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = dependency_obj->get_Dispatcher(storage->dispatcher.GetAddressOf()); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = get_panel_size (storage->dispatcher, + storage->cancellable, storage->panel, &size); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + GST_DEBUG_OBJECT (self, "client size %dx%d", size.Width, size.Height); + window->surface_width = size.Width; + window->surface_height = size.Height; + + hr = MakeAndInitialize(&resize_handler, self); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = storage->panel.As (&framework); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + hr = run_async (storage->dispatcher, + storage->cancellable, DEFAULT_ASYNC_TIMEOUT, + [self, framework, resize_handler] { + return framework->add_SizeChanged (resize_handler.Get(), + &self->storage->event_token); + }); + if (!gst_d3d11_result (hr, NULL)) + goto error; + + window->initialized = TRUE; + + return; + +error: + GST_ERROR_OBJECT (self, "Invalid window handle"); + return; +} + +static void +gst_d3d11_window_swap_chain_panel_dispose (GObject * object) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (object); + SwapChainPanelWinRTStorage *storage = self->storage; + + if (storage) { + if (storage->panel && storage->dispatcher) { + ComPtr framework; + HRESULT hr; + + hr = storage->panel.As (&framework); + if (SUCCEEDED (hr)) { + run_async (storage->dispatcher, + storage->cancellable, DEFAULT_ASYNC_TIMEOUT, + [self, framework] { + return framework->remove_SizeChanged (self->storage->event_token); + }); + } + + CloseHandle (storage->cancellable); + } + + delete storage; + } + + self->storage = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gboolean +gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, guint swapchain_flags, + IDXGISwapChain ** swap_chain) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window); + SwapChainPanelWinRTStorage *storage = self->storage; + ComPtr new_swapchain; + ComPtr panel_native; + GstD3D11Device *device = window->device; + DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, }; + HRESULT hr; + + desc1.Width = width; + desc1.Height = height; + desc1.Format = format; + desc1.Stereo = FALSE; + desc1.SampleDesc.Count = 1; + desc1.SampleDesc.Quality = 0; + desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc1.BufferCount = 2; + desc1.Scaling = DXGI_SCALING_STRETCH; + desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc1.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + desc1.Flags = swapchain_flags; + + new_swapchain = + gst_d3d11_device_create_swap_chain_for_composition (device, &desc1, NULL); + + if (!new_swapchain) { + GST_ERROR_OBJECT (self, "Cannot create swapchain"); + return FALSE; + } + + hr = storage->panel.As (&panel_native); + if (!gst_d3d11_result (hr, device)) + return FALSE; + + hr = run_async (storage->dispatcher, + storage->cancellable, DEFAULT_ASYNC_TIMEOUT, + [panel_native, new_swapchain] { + return panel_native->SetSwapChain(new_swapchain.Get()); + }); + + if (!gst_d3d11_result (hr, device)) + return FALSE; + + new_swapchain.CopyTo (swap_chain); + + run_async (storage->dispatcher, storage->cancellable, DEFAULT_ASYNC_TIMEOUT, + [window] { + gst_d3d11_window_on_resize (window, + window->surface_width, window->surface_height); + return S_OK; + }); + + return TRUE; +} + +static GstFlowReturn +gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window, + guint present_flags) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window); + HRESULT hr; + DXGI_PRESENT_PARAMETERS present_params = { 0, }; + IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain; + + /* the first present should not specify dirty-rect */ + if (!window->first_present) { + present_params.DirtyRectsCount = 1; + present_params.pDirtyRects = &window->render_rect; + } + + hr = swap_chain->Present1 (0, present_flags, &present_params); + + if (!gst_d3d11_result (hr, window->device)) { + GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x", + (guint) hr); + } + + return GST_FLOW_OK; +} + +static gboolean +gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window); + SwapChainPanelWinRTStorage *storage = self->storage; + + SetEvent (storage->cancellable); + + return TRUE; +} + +static gboolean +gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window); + SwapChainPanelWinRTStorage *storage = self->storage; + + ResetEvent (storage->cancellable); + + return TRUE; +} + +static void +gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window) +{ + GstD3D11WindowSwapChainPanel *self = + GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window); + SwapChainPanelWinRTStorage *storage = self->storage; + + run_async (storage->dispatcher, storage->cancellable, DEFAULT_ASYNC_TIMEOUT, + [window] { + gst_d3d11_window_on_resize (window, + window->surface_width, window->surface_height); + return S_OK; + }); + + return; +} + +static void +gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window * + window) +{ + GST_FIXME_OBJECT (window, "Implement fullscreen mode change"); + + return; +} + +static void +gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11WindowSwapChainPanel * + self, guint width, guint height) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + window->surface_width = width; + window->surface_height = height; + + GST_LOG_OBJECT (self, "New size %dx%d", width, height); + + gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self), width, height); +} + +GstD3D11Window * +gst_d3d11_window_swap_chain_panel_new (GstD3D11Device * device, guintptr handle) +{ + GstD3D11Window *window; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + g_return_val_if_fail (handle, NULL); + + window = (GstD3D11Window *) + g_object_new (GST_TYPE_D3D11_WINDOW_SWAP_CHAIN_PANEL, + "d3d11device", device, "window-handle", handle, NULL); + if (!window->initialized) { + gst_object_unref (window); + return NULL; + } + + g_object_ref_sink (window); + + return window; +} \ No newline at end of file diff --git a/sys/d3d11/gstd3d11window_swapchainpanel.h b/sys/d3d11/gstd3d11window_swapchainpanel.h new file mode 100644 index 0000000..d953d70 --- /dev/null +++ b/sys/d3d11/gstd3d11window_swapchainpanel.h @@ -0,0 +1,41 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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. + */ + +#ifndef __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__ +#define __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__ + +#include +#include +#include "gstd3d11_fwd.h" +#include "gstd3d11window.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D11_WINDOW_SWAP_CHAIN_PANEL (gst_d3d11_window_swap_chain_panel_get_type()) +G_DECLARE_FINAL_TYPE (GstD3D11WindowSwapChainPanel, + gst_d3d11_window_swap_chain_panel, + GST, D3D11_WINDOW_SWAP_CHAIN_PANEL, GstD3D11Window); + +GstD3D11Window * gst_d3d11_window_swap_chain_panel_new (GstD3D11Device * device, + guintptr handle); + +G_END_DECLS + +#endif /* __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__ */ diff --git a/sys/d3d11/gstd3d11window_win32.cpp b/sys/d3d11/gstd3d11window_win32.cpp new file mode 100644 index 0000000..e9f6302 --- /dev/null +++ b/sys/d3d11/gstd3d11window_win32.cpp @@ -0,0 +1,924 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2012 Matthew Waters + * Copyright (C) 2019 Seungha Yang + * + * 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 "gstd3d11device.h" +#include "gstd3d11memory.h" +#include "gstd3d11utils.h" +#include "gstd3d11window_win32.h" + +extern "C" { +GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug); +#define GST_CAT_DEFAULT gst_d3d11_window_debug +} + +G_LOCK_DEFINE_STATIC (create_lock); + +#define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc" +#define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_win32_object" + +#define WM_GST_D3D11_FULLSCREEN (WM_USER + 1) + +static LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); +static LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); + +typedef enum +{ + GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE = 0, + GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED, + GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED, +} GstD3D11WindowWin32OverlayState; + +struct _GstD3D11WindowWin32 +{ + GstD3D11Window parent; + + GMutex lock; + GCond cond; + + GMainContext *main_context; + GMainLoop *loop; + + gboolean visible; + + GSource *msg_source; + GIOChannel *msg_io_channel; + + GThread *thread; + + HWND internal_hwnd; + HWND external_hwnd; + GstD3D11WindowWin32OverlayState overlay_state; + + HDC device_handle; + gboolean first_present; + gboolean have_swapchain1; + + /* atomic */ + volatile gint pending_fullscreen_count; + + /* fullscreen related */ + RECT restore_rect; + LONG restore_style; +}; + +#define gst_d3d11_window_win32_parent_class parent_class +G_DEFINE_TYPE (GstD3D11WindowWin32, gst_d3d11_window_win32, + GST_TYPE_D3D11_WINDOW); + +static void gst_d3d11_window_win32_constructed (GObject * object); +static void gst_d3d11_window_win32_dispose (GObject * object); +static void gst_d3d11_window_win32_finalize (GObject * object); + +static void gst_d3d11_window_win32_show (GstD3D11Window * window); +static void gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window); +static void +gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window); +static gboolean +gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, + guint swapchain_flags, IDXGISwapChain ** swap_chain); +static GstFlowReturn gst_d3d11_window_win32_present (GstD3D11Window * window, + guint present_flags); + +static gpointer gst_d3d11_window_win32_thread_func (gpointer data); +static gboolean +gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self); +static void gst_d3d11_window_win32_close_internal_window (GstD3D11WindowWin32 * + self); +static void gst_d3d11_window_win32_release_external_handle (GstD3D11WindowWin32 + * self); +static void +gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self, + guintptr handle); + +static void +gst_d3d11_window_win32_class_init (GstD3D11WindowWin32Class * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass); + + gobject_class->constructed = gst_d3d11_window_win32_constructed; + gobject_class->dispose = gst_d3d11_window_win32_dispose; + gobject_class->finalize = gst_d3d11_window_win32_finalize; + + window_class->show = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_show); + window_class->update_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_update_swap_chain); + window_class->change_fullscreen_mode = + GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_change_fullscreen_mode); + window_class->create_swap_chain = + GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_create_swap_chain); + window_class->present = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_present); +} + +static void +gst_d3d11_window_win32_init (GstD3D11WindowWin32 * self) +{ + g_mutex_init (&self->lock); + g_cond_init (&self->cond); + + self->main_context = g_main_context_new (); +} + +static void +gst_d3d11_window_win32_constructed (GObject * object) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (object); + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object); + + g_mutex_lock (&self->lock); + self->loop = g_main_loop_new (self->main_context, FALSE); + self->thread = g_thread_new ("GstD3D11WindowWin32Win32", + (GThreadFunc) gst_d3d11_window_win32_thread_func, self); + while (!g_main_loop_is_running (self->loop)) + g_cond_wait (&self->cond, &self->lock); + g_mutex_unlock (&self->lock); + + if (window->external_handle) + gst_d3d11_window_win32_set_window_handle (self, window->external_handle); +} + +static void +gst_d3d11_window_win32_dispose (GObject * object) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object); + + gst_d3d11_window_win32_release_external_handle (self); + + if (self->loop) { + g_main_loop_quit (self->loop); + } + + if (self->thread) { + g_thread_join (self->thread); + self->thread = NULL; + } + + if (self->loop) { + g_main_loop_unref (self->loop); + self->loop = NULL; + } + + if (self->main_context) { + g_main_context_unref (self->main_context); + self->main_context = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_d3d11_window_win32_finalize (GObject * object) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object); + + g_mutex_clear (&self->lock); + g_cond_clear (&self->cond); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +running_cb (gpointer user_data) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (user_data); + + GST_TRACE_OBJECT (self, "Main loop running now"); + + g_mutex_lock (&self->lock); + g_cond_signal (&self->cond); + g_mutex_unlock (&self->lock); + + return G_SOURCE_REMOVE; +} + +static gpointer +gst_d3d11_window_win32_thread_func (gpointer data) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (data); + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (data); + GSource *source; + + GST_DEBUG_OBJECT (self, "Enter loop"); + g_main_context_push_thread_default (self->main_context); + + window->initialized = gst_d3d11_window_win32_create_internal_window (self); + + source = g_idle_source_new (); + g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL); + g_source_attach (source, self->main_context); + g_source_unref (source); + + g_main_loop_run (self->loop); + + gst_d3d11_window_win32_close_internal_window (self); + + g_main_context_pop_thread_default (self->main_context); + + GST_DEBUG_OBJECT (self, "Exit loop"); + + return NULL; +} + +static gboolean +msg_cb (GIOChannel * source, GIOCondition condition, gpointer data) +{ + MSG msg; + + if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + return G_SOURCE_CONTINUE; + + TranslateMessage (&msg); + DispatchMessage (&msg); + + return G_SOURCE_CONTINUE; +} + +static void +gst_d3d11_window_win32_close_internal_window (GstD3D11WindowWin32 * self) +{ + if (self->internal_hwnd) { + RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME); + ShowWindow (self->internal_hwnd, SW_HIDE); + SetParent (self->internal_hwnd, NULL); + if (!DestroyWindow (self->internal_hwnd)) + GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT + ", 0x%x", (guintptr) self->internal_hwnd, (guint) GetLastError ()); + self->internal_hwnd = NULL; + } + + if (self->msg_source) { + g_source_destroy (self->msg_source); + g_source_unref (self->msg_source); + self->msg_source = NULL; + } + + if (self->msg_io_channel) { + g_io_channel_unref (self->msg_io_channel); + self->msg_io_channel = NULL; + } +} + +static void +gst_d3d11_window_win32_set_external_handle (GstD3D11WindowWin32 * self) +{ + WNDPROC external_window_proc; + RECT rect; + + if (!self->external_hwnd) { + /* no parent so the internal window needs borders and system menu */ + SetWindowLongPtr (self->internal_hwnd, GWL_STYLE, + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW); + SetParent (self->internal_hwnd, NULL); + + return; + } + + external_window_proc = + (WNDPROC) GetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC); + + GST_DEBUG ("set external window %" G_GUINTPTR_FORMAT, + (guintptr) self->external_hwnd); + + SetProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME, + (HANDLE) external_window_proc); + SetProp (self->external_hwnd, D3D11_WINDOW_PROP_NAME, self); + SetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC, + (LONG_PTR) sub_class_proc); + + SetWindowLongPtr (self->internal_hwnd, GWL_STYLE, WS_CHILD | WS_MAXIMIZE); + SetParent (self->internal_hwnd, self->external_hwnd); + + /* take changes into account: SWP_FRAMECHANGED */ + GetClientRect (self->external_hwnd, &rect); + SetWindowPos (self->internal_hwnd, HWND_TOP, rect.left, rect.top, + rect.right, rect.bottom, + SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_FRAMECHANGED | SWP_NOACTIVATE); + MoveWindow (self->internal_hwnd, rect.left, rect.top, rect.right, + rect.bottom, FALSE); +} + +static void +gst_d3d11_window_win32_release_external_handle (GstD3D11WindowWin32 * self) +{ + WNDPROC external_proc; + + if (!self->external_hwnd) + return; + + external_proc = + (WNDPROC) GetProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME); + if (!external_proc) + return; + + GST_DEBUG ("release external window %" G_GUINTPTR_FORMAT, + (guintptr) self->external_hwnd); + + SetWindowLongPtr (self->external_hwnd, + GWLP_WNDPROC, (LONG_PTR) external_proc); + + RemoveProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME); + RemoveProp (self->external_hwnd, D3D11_WINDOW_PROP_NAME); + self->external_hwnd = NULL; +} + +static gboolean +gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self) +{ + WNDCLASSEX wc; + ATOM atom = 0; + HINSTANCE hinstance = GetModuleHandle (NULL); + + GST_LOG_OBJECT (self, "Attempting to create a win32 window"); + + G_LOCK (create_lock); + atom = GetClassInfoEx (hinstance, "GSTD3D11", &wc); + if (atom == 0) { + GST_LOG_OBJECT (self, "Register internal window class"); + ZeroMemory (&wc, sizeof (WNDCLASSEX)); + + wc.cbSize = sizeof (WNDCLASSEX); + wc.lpfnWndProc = window_proc; + wc.hInstance = hinstance; + wc.hIcon = LoadIcon (NULL, IDI_WINLOGO); + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); + wc.lpszClassName = "GSTD3D11"; + + atom = RegisterClassEx (&wc); + + if (atom == 0) { + G_UNLOCK (create_lock); + GST_ERROR_OBJECT (self, "Failed to register window class 0x%x", + (unsigned int) GetLastError ()); + return FALSE; + } + } else { + GST_LOG_OBJECT (self, "window class was already registered"); + } + + self->device_handle = 0; + self->internal_hwnd = 0; + self->visible = FALSE; + + self->internal_hwnd = CreateWindowEx (0, + "GSTD3D11", + "Direct3D11 renderer", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, (HWND) NULL, (HMENU) NULL, hinstance, self); + + G_UNLOCK (create_lock); + + if (!self->internal_hwnd) { + GST_ERROR_OBJECT (self, "Failed to create d3d11 window"); + return FALSE; + } + + GST_DEBUG_OBJECT (self, "d3d11 window created: %" G_GUINTPTR_FORMAT, + (guintptr) self->internal_hwnd); + + /* device_handle is set in the window_proc */ + if (!self->device_handle) { + GST_ERROR_OBJECT (self, "device handle is not available"); + return FALSE; + } + + GST_LOG_OBJECT (self, + "Created a internal d3d11 window %p", self->internal_hwnd); + + self->msg_io_channel = + g_io_channel_win32_new_messages ((guintptr) self->internal_hwnd); + self->msg_source = g_io_create_watch (self->msg_io_channel, G_IO_IN); + g_source_set_callback (self->msg_source, (GSourceFunc) msg_cb, self, NULL); + g_source_attach (self->msg_source, self->main_context); + + return TRUE; +} + +/* always called from window thread */ +static void +gst_d3d11_window_win32_change_fullscreen_mode_internal (GstD3D11WindowWin32 * + self) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + HWND hwnd = self->external_hwnd ? self->external_hwnd : self->internal_hwnd; + + if (!window->swap_chain) + return; + + if (window->requested_fullscreen == window->fullscreen) + return; + + GST_DEBUG_OBJECT (self, "Change mode to %s", + window->requested_fullscreen ? "fullscreen" : "windowed"); + + window->fullscreen = !window->fullscreen; + + if (!window->fullscreen) { + /* Restore the window's attributes and size */ + SetWindowLong (hwnd, GWL_STYLE, self->restore_style); + + SetWindowPos (hwnd, HWND_NOTOPMOST, + self->restore_rect.left, + self->restore_rect.top, + self->restore_rect.right - self->restore_rect.left, + self->restore_rect.bottom - self->restore_rect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE); + + ShowWindow (hwnd, SW_NORMAL); + } else { + IDXGIOutput *output; + DXGI_OUTPUT_DESC output_desc; + IDXGISwapChain *swap_chain = window->swap_chain; + + /* show window before change style */ + ShowWindow (hwnd, SW_SHOW); + + /* Save the old window rect so we can restore it when exiting + * fullscreen mode */ + GetWindowRect (hwnd, &self->restore_rect); + self->restore_style = GetWindowLong (hwnd, GWL_STYLE); + + /* Make the window borderless so that the client area can fill the screen */ + SetWindowLong (hwnd, GWL_STYLE, + self->restore_style & + ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | + WS_THICKFRAME)); + + swap_chain->GetContainingOutput (&output); + output->GetDesc (&output_desc); + output->Release (); + + SetWindowPos (hwnd, HWND_TOPMOST, + output_desc.DesktopCoordinates.left, + output_desc.DesktopCoordinates.top, + output_desc.DesktopCoordinates.right, + output_desc.DesktopCoordinates.bottom, + SWP_FRAMECHANGED | SWP_NOACTIVATE); + + ShowWindow (hwnd, SW_MAXIMIZE); + } + + GST_DEBUG_OBJECT (self, "Fullscreen mode change done"); +} + +static void +gst_d3d11_window_win32_on_key_event (GstD3D11WindowWin32 * self, + HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + gunichar2 wcrep[128]; + const gchar *event; + + if (!window->enable_navigation_events) + return; + + if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) { + gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL); + if (utfrep) { + if (uMsg == WM_KEYDOWN) + event = "key-press"; + else + event = "key-release"; + + gst_d3d11_window_on_key_event (window, event, utfrep); + g_free (utfrep); + } + } +} + +static void +gst_d3d11_window_win32_on_mouse_event (GstD3D11WindowWin32 * self, + HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + gint button; + const gchar *event = NULL; + + if (!window->enable_navigation_events) + return; + + /* FIXME: convert to render coordinate */ + switch (uMsg) { + case WM_MOUSEMOVE: + button = 0; + event = "mouse-move"; + break; + case WM_LBUTTONDOWN: + button = 1; + event = "mouse-button-press"; + break; + case WM_LBUTTONUP: + button = 1; + event = "mouse-button-release"; + break; + case WM_RBUTTONDOWN: + button = 2; + event = "mouse-button-press"; + break; + case WM_RBUTTONUP: + button = 2; + event = "mouse-button-release"; + break; + case WM_MBUTTONDOWN: + button = 3; + event = "mouse-button-press"; + break; + case WM_MBUTTONUP: + button = 3; + event = "mouse-button-release"; + break; + default: + break; + } + + if (event) + gst_d3d11_window_on_mouse_event (window, + event, button, (gdouble) LOWORD (lParam), (gdouble) HIWORD (lParam)); +} + +static void +gst_d3d11_window_win32_handle_window_proc (GstD3D11WindowWin32 * self, + HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + GstD3D11Window *window = GST_D3D11_WINDOW (self); + + switch (uMsg) { + case WM_SIZE: + gst_d3d11_window_on_resize (window, 0, 0); + break; + case WM_CLOSE: + if (self->internal_hwnd) { + ShowWindow (self->internal_hwnd, SW_HIDE); + gst_d3d11_window_win32_close_internal_window (self); + } + break; + case WM_KEYDOWN: + case WM_KEYUP: + gst_d3d11_window_win32_on_key_event (self, hWnd, uMsg, wParam, lParam); + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + gst_d3d11_window_win32_on_mouse_event (self, hWnd, uMsg, wParam, lParam); + break; + case WM_SYSKEYDOWN: + if ((window->fullscreen_toggle_mode & + GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) + == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) { + WORD state = GetKeyState (VK_RETURN); + BYTE high = HIBYTE (state); + + if (high & 0x1) { + window->requested_fullscreen = !window->fullscreen; + gst_d3d11_window_win32_change_fullscreen_mode_internal (self); + } + } + break; + case WM_GST_D3D11_FULLSCREEN: + if (g_atomic_int_get (&self->pending_fullscreen_count)) { + g_atomic_int_dec_and_test (&self->pending_fullscreen_count); + if ((window->fullscreen_toggle_mode & + GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY) + == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY) + gst_d3d11_window_win32_change_fullscreen_mode_internal (self); + } + break; + default: + break; + } + + return; +} + +static LRESULT CALLBACK +window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + GstD3D11WindowWin32 *self; + + if (uMsg == WM_CREATE) { + self = GST_D3D11_WINDOW_WIN32 (((LPCREATESTRUCT) lParam)->lpCreateParams); + + GST_LOG_OBJECT (self, "WM_CREATE"); + + self->device_handle = GetDC (hWnd); + /* Do this, otherwise we hang on exit. We can still use it (due to the + * CS_OWNDC flag in the WindowClass) after we have Released. + */ + ReleaseDC (hWnd, self->device_handle); + + SetProp (hWnd, D3D11_WINDOW_PROP_NAME, self); + } else if (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)) { + self = GST_D3D11_WINDOW_WIN32 (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)); + + g_assert (self->internal_hwnd == hWnd); + + gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam, + lParam); + } + + if (uMsg == WM_SIZE) + return 0; + + return DefWindowProc (hWnd, uMsg, wParam, lParam); +} + +static LRESULT FAR PASCAL +sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + WNDPROC external_window_proc = + (WNDPROC) GetProp (hWnd, EXTERNAL_PROC_PROP_NAME); + GstD3D11WindowWin32 *self = + (GstD3D11WindowWin32 *) GetProp (hWnd, D3D11_WINDOW_PROP_NAME); + + if (uMsg == WM_SIZE) { + MoveWindow (self->internal_hwnd, 0, 0, LOWORD (lParam), HIWORD (lParam), + FALSE); + } else if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) { + g_mutex_lock (&self->lock); + GST_WARNING_OBJECT (self, "external window is closing"); + gst_d3d11_window_win32_release_external_handle (self); + self->external_hwnd = NULL; + self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED; + g_mutex_unlock (&self->lock); + } else { + gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam, + lParam); + } + + return CallWindowProc (external_window_proc, hWnd, uMsg, wParam, lParam); +} + +static void +gst_d3d11_window_win32_disable_alt_enter (GstD3D11WindowWin32 * self, + GstD3D11Device * device, IDXGISwapChain * swap_chain, HWND hwnd) +{ + IDXGIFactory1 *factory = NULL; + HRESULT hr; + + hr = swap_chain->GetParent (IID_IDXGIFactory1, (void **) &factory); + if (!gst_d3d11_result (hr, device) || !factory) { + GST_WARNING_OBJECT (self, + "Cannot get parent dxgi factory for swapchain %p, hr: 0x%x", + swap_chain, (guint) hr); + return; + } + + hr = factory->MakeWindowAssociation (hwnd, DXGI_MWA_NO_ALT_ENTER); + if (!gst_d3d11_result (hr, device)) { + GST_WARNING_OBJECT (self, + "MakeWindowAssociation failure, hr: 0x%x", (guint) hr); + } + + factory->Release (); +} + +static gboolean +gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window, + DXGI_FORMAT format, guint width, guint height, + guint swapchain_flags, IDXGISwapChain ** swap_chain) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window); + DXGI_SWAP_CHAIN_DESC desc = { 0, }; + DXGI_SWAP_EFFECT swap_effect = DXGI_SWAP_EFFECT_DISCARD; + IDXGISwapChain *new_swapchain = NULL; + GstD3D11Device *device = window->device; + + if (gst_d3d11_is_windows_8_or_greater ()) + swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + + self->have_swapchain1 = FALSE; + +#if (DXGI_HEADER_VERSION >= 2) + { + DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, }; + desc1.Width = 0; + desc1.Height = 0; + desc1.Format = format; + /* FIXME: add support stereo */ + desc1.Stereo = FALSE; + desc1.SampleDesc.Count = 1; + desc1.SampleDesc.Quality = 0; + desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc1.BufferCount = 2; + desc1.Scaling = DXGI_SCALING_STRETCH; + + /* scaling-stretch would break aspect-ratio so we prefer to use scaling-none, + * but Windows7 does not support this method */ + if (gst_d3d11_is_windows_8_or_greater ()) + desc1.Scaling = DXGI_SCALING_NONE; + desc1.SwapEffect = swap_effect; + desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + desc1.Flags = swapchain_flags; + + new_swapchain = (IDXGISwapChain *) + gst_d3d11_device_create_swap_chain_for_hwnd (device, + self->internal_hwnd, &desc1, NULL, NULL); + + if (!new_swapchain) { + GST_WARNING_OBJECT (self, "Failed to create swapchain1"); + } else { + self->have_swapchain1 = TRUE; + } + } +#endif + + if (!new_swapchain) { + /* we will get client area at on_resize */ + desc.BufferDesc.Width = 0; + desc.BufferDesc.Height = 0; + /* don't care refresh rate */ + desc.BufferDesc.RefreshRate.Numerator = 0; + desc.BufferDesc.RefreshRate.Denominator = 1; + desc.BufferDesc.Format = format; + desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.SwapEffect = swap_effect; + desc.OutputWindow = self->internal_hwnd; + desc.Windowed = TRUE; + desc.Flags = swapchain_flags; + + new_swapchain = gst_d3d11_device_create_swap_chain (device, &desc); + } + + if (!new_swapchain) { + GST_ERROR_OBJECT (self, "Cannot create swapchain"); + return FALSE; + } + + /* disable alt+enter here. It should be manually handled */ + gst_d3d11_device_lock (device); + gst_d3d11_window_win32_disable_alt_enter (self, + device, new_swapchain, desc.OutputWindow); + gst_d3d11_device_unlock (device); + + *swap_chain = new_swapchain; + + gst_d3d11_window_on_resize (window, width, height); + + return TRUE; +} + +static void +gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self, + guintptr handle) +{ + self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE; + + if (self->visible) { + ShowWindow (self->internal_hwnd, SW_HIDE); + self->visible = FALSE; + } + + gst_d3d11_window_win32_release_external_handle (self); + self->external_hwnd = (HWND) handle; + gst_d3d11_window_win32_set_external_handle (self); + + if (self->external_hwnd) + self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED; +} + +static void +gst_d3d11_window_win32_show (GstD3D11Window * window) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window); + gint width, height; + + width = window->width; + height = window->height; + + if (!self->visible) { + /* if no parent the real size has to be set now because this has not been done + * when at window creation */ + if (!self->external_hwnd) { + RECT rect; + GetClientRect (self->internal_hwnd, &rect); + width += 2 * GetSystemMetrics (SM_CXSIZEFRAME); + height += + 2 * GetSystemMetrics (SM_CYSIZEFRAME) + + GetSystemMetrics (SM_CYCAPTION); + MoveWindow (self->internal_hwnd, rect.left, rect.top, width, + height, FALSE); + } + + ShowWindow (self->internal_hwnd, SW_SHOW); + self->visible = TRUE; + } +} + +static GstFlowReturn +gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window); + HRESULT hr; + + if ((!self->external_hwnd && + self->overlay_state == GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED) + || !self->internal_hwnd) { + GST_ERROR_OBJECT (self, "Output window was closed"); + + return GST_D3D11_WINDOW_FLOW_CLOSED; + } + +#if (DXGI_HEADER_VERSION >= 2) + if (self->have_swapchain1) { + IDXGISwapChain1 *swap_chain1 = (IDXGISwapChain1 *) window->swap_chain; + DXGI_PRESENT_PARAMETERS present_params = { 0, }; + + /* the first present should not specify dirty-rect */ + if (!window->first_present) { + present_params.DirtyRectsCount = 1; + present_params.pDirtyRects = &window->render_rect; + } + + hr = swap_chain1->Present1 (0, present_flags, &present_params); + } else +#endif + { + hr = window->swap_chain->Present (0, present_flags); + } + + if (!gst_d3d11_result (hr, window->device)) { + GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x", + (guint) hr); + } + + return GST_FLOW_OK; +} + +static void +gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window); + + if (self->internal_hwnd) + PostMessage (self->internal_hwnd, WM_SIZE, 0, 0); + + return; +} + +static void +gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window) +{ + GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window); + + if (self->internal_hwnd) { + g_atomic_int_add (&self->pending_fullscreen_count, 1); + PostMessage (self->internal_hwnd, WM_GST_D3D11_FULLSCREEN, 0, 0); + } +} + +GstD3D11Window * +gst_d3d11_window_win32_new (GstD3D11Device * device, guintptr handle) +{ + GstD3D11Window *window; + + g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL); + + window = (GstD3D11Window *) g_object_new (GST_TYPE_D3D11_WINDOW_WIN32, + "d3d11device", device, "window-handle", handle, NULL); + if (!window->initialized) { + gst_object_unref (window); + return NULL; + } + + g_object_ref_sink (window); + + return window; +} \ No newline at end of file diff --git a/sys/d3d11/gstd3d11window_win32.h b/sys/d3d11/gstd3d11window_win32.h new file mode 100644 index 0000000..6759beb --- /dev/null +++ b/sys/d3d11/gstd3d11window_win32.h @@ -0,0 +1,40 @@ +/* + * GStreamer + * Copyright (C) 2019 Seungha Yang + * + * 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. + */ + +#ifndef __GST_D3D11_WINDOW_WIN32_H__ +#define __GST_D3D11_WINDOW_WIN32_H__ + +#include +#include +#include "gstd3d11_fwd.h" +#include "gstd3d11window.h" + +G_BEGIN_DECLS + +#define GST_TYPE_D3D11_WINDOW_WIN32 (gst_d3d11_window_win32_get_type()) +G_DECLARE_FINAL_TYPE (GstD3D11WindowWin32, + gst_d3d11_window_win32, GST, D3D11_WINDOW_WIN32, GstD3D11Window); + +GstD3D11Window * gst_d3d11_window_win32_new (GstD3D11Device * device, + guintptr handle); + +G_END_DECLS + +#endif /* __GST_D3D11_WINDOW_WIN32_H__ */ diff --git a/sys/d3d11/meson.build b/sys/d3d11/meson.build index 52e235c..fb7141a 100644 --- a/sys/d3d11/meson.build +++ b/sys/d3d11/meson.build @@ -4,7 +4,7 @@ d3d11_sources = [ 'gstd3d11memory.c', 'gstd3d11utils.c', 'gstd3d11videosink.c', - 'gstd3d11window.c', + 'gstd3d11window.cpp', 'plugin.c', 'gstd3d11format.c', 'gstd3d11basefilter.c', @@ -44,6 +44,8 @@ extra_c_args = ['-DCOBJMACROS'] have_dxgi_header = false have_d3d11sdk_h = false have_dxgidebug_h = false +winapi_desktop = false +winapi_app = false extra_dep = [] d3d11_conf = configuration_data() @@ -55,6 +57,7 @@ endif d3d11_lib = cc.find_library('d3d11', required : d3d11_option) dxgi_lib = cc.find_library('dxgi', required : d3d11_option) d3dcompiler_lib = cc.find_library('d3dcompiler', required: d3d11_option) +runtimeobject_lib = cc.find_library('runtimeobject', required : false) foreach dxgi_h: dxgi_headers if not have_dxgi_header and cc.has_header(dxgi_h[0]) @@ -112,14 +115,52 @@ if cc.has_header('dxva.h') and cc.has_header('d3d9.h') extra_dep += [gstcodecparsers_dep] endif +winapi_desktop = cxx.compiles('''#include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + #error "not win32" + #endif''', + dependencies: [d3d11_lib, dxgi_lib], + name: 'checking if building for Win32') + +if runtimeobject_lib.found() + winapi_app = cxx.compiles('''#include + #include + #include + #include + #include + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #error "not winrt" + #endif''', + dependencies: [d3d11_lib, dxgi_lib, runtimeobject_lib], + name: 'checking if building for WinRT') +endif + +if not winapi_desktop and not winapi_app + error('Neither Desktop partition nor App partition') +endif + +winapi_app_only = winapi_app and not winapi_desktop + +if winapi_app_only + d3d11_sources += ['gstd3d11window_corewindow.cpp', + 'gstd3d11window_swapchainpanel.cpp'] + extra_dep += [runtimeobject_lib] +else + d3d11_sources += ['gstd3d11window_win32.cpp'] +endif + +d3d11_conf.set10('GST_D3D11_WINAPI_ONLY_APP', winapi_app_only) + configure_file( - output: 'd3d11config.h', + output: 'gstd3d11config.h', configuration: d3d11_conf, ) gstd3d11 = library('gstd3d11', d3d11_sources, c_args : gst_plugins_bad_args + extra_c_args, + cpp_args: gst_plugins_bad_args, include_directories : [configinc], dependencies : [gstbase_dep, gstvideo_dep, d3d11_lib, dxgi_lib, d3dcompiler_lib] + extra_dep, install : true, diff --git a/sys/d3d11/plugin.c b/sys/d3d11/plugin.c index cd9f7e6..b57c780 100644 --- a/sys/d3d11/plugin.c +++ b/sys/d3d11/plugin.c @@ -21,7 +21,7 @@ #include "config.h" #endif -#include "d3d11config.h" +#include "gstd3d11config.h" #include #include "gstd3d11videosink.h" @@ -42,6 +42,7 @@ GST_DEBUG_CATEGORY (gst_d3d11_utils_debug); GST_DEBUG_CATEGORY (gst_d3d11_format_debug); GST_DEBUG_CATEGORY (gst_d3d11_device_debug); GST_DEBUG_CATEGORY (gst_d3d11_overlay_compositor_debug); +GST_DEBUG_CATEGORY (gst_d3d11_window_debug); #if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H) GST_DEBUG_CATEGORY (gst_d3d11_debug_layer_debug); @@ -68,6 +69,8 @@ plugin_init (GstPlugin * plugin) "d3d11device", 0, "d3d11 device object"); GST_DEBUG_CATEGORY_INIT (gst_d3d11_overlay_compositor_debug, "d3d11overlaycompositor", 0, "d3d11overlaycompositor"); + GST_DEBUG_CATEGORY_INIT (gst_d3d11_window_debug, + "d3d11window", 0, "d3d11window"); #if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H) /* NOTE: enabled only for debug build */ -- 2.7.4