From bdf73569f834ace8ef7a2e68dbf3ad86528c54f1 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Thu, 9 Jul 2020 22:23:33 +0900 Subject: [PATCH] glwindow/winrt: Add window resize event handler GstGLWindow implmentaion should be able to report native window size and also it need to handle resize event for glimagesink. Note that GstD3D11Window implementation was referenced for this change. Part-of: --- gst-libs/gst/gl/meson.build | 4 +- .../gst/gl/winrt/gstglwindow_winrt_egl.cpp | 567 +++++++++++++++++- gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.h | 3 + 3 files changed, 569 insertions(+), 5 deletions(-) diff --git a/gst-libs/gst/gl/meson.build b/gst-libs/gst/gl/meson.build index 5c43bd3cb2..ef48507a1d 100644 --- a/gst-libs/gst/gl/meson.build +++ b/gst-libs/gst/gl/meson.build @@ -718,13 +718,15 @@ if need_win_winrt != 'no' and host_system == 'windows' if egl_dep.found() windows_graphics_h = cc.has_header('windows.graphics.h') windows_app_dep = cc.find_library('WindowsApp', required: false) + runtimeobject_lib = cc.find_library('runtimeobject', required: false) - if windows_graphics_h and windows_app_dep.found() + if windows_graphics_h and windows_app_dep.found() and runtimeobject_lib.found() enabled_gl_winsys += 'winrt' glconf.set10('GST_GL_HAVE_WINDOW_WINRT', 1) gl_sources += [ 'winrt/gstglwindow_winrt_egl.cpp' ] + gl_winsys_deps += runtimeobject_lib elif need_win_winrt == 'yes' error('WinRT is enabled, but headers/libraries were not found') endif diff --git a/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.cpp b/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.cpp index 9e8d386302..4dbeaa9f3a 100644 --- a/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.cpp +++ b/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.cpp @@ -1,6 +1,7 @@ /* * GStreamer * Copyright (C) 2019 Nirbheek Chauhan + * Copyright (C) 2020 Seungha Yang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,37 +28,530 @@ #include "gstglwindow_winrt_egl.h" #include "../gstglwindow_private.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; + #define GST_CAT_DEFAULT gst_gl_window_debug -#define GST_GL_WINDOW_WINRT_EGL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - GST_TYPE_GL_WINDOW_WINRT_EGL, GstGLWindowWinRTEGLPrivate)) +/* timeout to wait busy UI thread */ +#define DEFAULT_ASYNC_TIMEOUT (5 * 1000) +static void gst_gl_window_winrt_egl_on_resize (GstGLWindow * window, + guint width, guint height); -G_DEFINE_TYPE (GstGLWindowWinRTEGL, gst_gl_window_winrt_egl, +typedef enum +{ + GST_GL_WINDOW_WINRT_NATIVE_TYPE_NONE = 0, + GST_GL_WINDOW_WINRT_NATIVE_TYPE_CORE_WINDOW, + GST_GL_WINDOW_WINRT_NATIVE_TYPE_SWAP_CHAIN_PANEL, +} GstGLWindowWinRTNativeType; + +template +static HRESULT +run_async (const ComPtr &dispatcher, DWORD timeout, + CB &&cb) +{ + ComPtr async_action; + HRESULT hr; + HRESULT async_hr; + boolean can_now; + DWORD wait_ret; + + hr = dispatcher->get_HasThreadAccess (&can_now); + + if (FAILED (hr)) + 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); + + if (FAILED (hr)) + return hr; + + wait_ret = WaitForSingleObjectEx (event.Get (), timeout, true); + if (wait_ret != WAIT_OBJECT_0) + return E_FAIL; + + return async_hr; +} + +/* ICoreWindow resize event handler */ +typedef ABI::Windows::Foundation:: + __FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowSizeChangedEventArgs_t + IWindowSizeChangedEventHandler; + +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 (SUCCEEDED (hr)) { + float dpi = 96.0f; + + hr = properties->get_LogicalDpi (&dpi); + if (SUCCEEDED (hr)) + return dpi; + } + + return 96.0f; +} + +static inline float dip_to_pixel (float dip, float logical_dpi) +{ + /* https://docs.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels */ + return dip * logical_dpi / 96.0f; +} + +class CoreResizeHandler + : public RuntimeClass, + IWindowSizeChangedEventHandler> +{ +public: + CoreResizeHandler () {} + HRESULT RuntimeClassInitialize (GstGLWindow * window) + { + if (!window) + return E_INVALIDARG; + + listener_ = window; + return S_OK; + } + + IFACEMETHOD(Invoke) + (Core::ICoreWindow * sender, Core::IWindowSizeChangedEventArgs * args) + { + Size new_size; + HRESULT hr; + + if (!listener_) + return S_OK; + + hr = args->get_Size(&new_size); + if (SUCCEEDED (hr)) { + gint width, height; + float dpi; + + dpi = get_logical_dpi (); + + width = (guint) dip_to_pixel (new_size.Width, dpi); + height = (guint) dip_to_pixel (new_size.Height, dpi); + + gst_gl_window_winrt_egl_on_resize (listener_, width, height); + } + + return S_OK; + } + +private: + GstGLWindow * listener_; +}; + +class PanelResizeHandler + : public RuntimeClass, + Xaml::ISizeChangedEventHandler> +{ +public: + PanelResizeHandler () {} + HRESULT RuntimeClassInitialize (GstGLWindow * window) + { + if (!window) + return E_INVALIDARG; + + listener_ = window; + return S_OK; + } + + IFACEMETHOD(Invoke) + (IInspectable * sender, Xaml::ISizeChangedEventArgs * args) + { + Size new_size; + HRESULT hr; + + if (!listener_) + return S_OK; + + hr = args->get_NewSize(&new_size); + if (SUCCEEDED(hr)) { + gst_gl_window_winrt_egl_on_resize (listener_, + (guint) new_size.Width, (guint) new_size.Height); + } + + return S_OK; + } + +private: + GstGLWindow * listener_; +}; + +class GstGLWindowWinRTEGLResizeHandler +{ +public: + GstGLWindowWinRTEGLResizeHandler(IInspectable * native_handle, + GstGLWindow * listener) + : native_type_ (GST_GL_WINDOW_WINRT_NATIVE_TYPE_NONE) + , isValid_ (false) + { + ComPtr window; + HRESULT hr = E_FAIL; + + if (!native_handle) { + GST_WARNING ("Null handler"); + return; + } + + window = native_handle; + if (SUCCEEDED (window.As (&core_window_))) { + GST_INFO ("Valid ICoreWindow"); + native_type_ = GST_GL_WINDOW_WINRT_NATIVE_TYPE_CORE_WINDOW; + core_window_->get_Dispatcher (&dispatcher_); + } else if (SUCCEEDED (window.As (&panel_))) { + ComPtr dependency_obj; + + GST_INFO ("Valid ISwapChainPanel"); + native_type_ = GST_GL_WINDOW_WINRT_NATIVE_TYPE_SWAP_CHAIN_PANEL; + hr = panel_.As (&dependency_obj); + if (FAILED (hr)) { + GST_WARNING ("Couldn't get IDependencyObject interface"); + return; + } + + dependency_obj->get_Dispatcher (&dispatcher_); + } else { + GST_ERROR ("Invalid window handle"); + return; + } + + if (!dispatcher_) { + GST_WARNING ("ICoreDispatcher is unavailable"); + return; + } + + switch (native_type_) { + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_CORE_WINDOW: + if (!registerSizeChangedHandlerForCoreWindow (listener)) { + GST_WARNING + ("Couldn't install size changed event handler for corewindow"); + return; + } + break; + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_SWAP_CHAIN_PANEL: + if (!registerSizeChangedHandlerForSwapChainPanel (listener)) { + GST_WARNING + ("Couldn't install size changed event handler for swapchainpanel"); + return; + } + break; + default: + g_assert_not_reached (); + return; + } + + isValid_ = true; + } + + ~GstGLWindowWinRTEGLResizeHandler() + { + if (!isValid_ || !dispatcher_) + return; + + switch (native_type_) { + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_CORE_WINDOW: + unregisterSizeChangedHandlerForCoreWindow (); + break; + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_SWAP_CHAIN_PANEL: + unregisterSizeChangedHandlerForSwapChainPanel (); + break; + default: + g_assert_not_reached (); + return; + } + } + + bool + IsValid (void) + { + return isValid_; + } + + bool + GetWindowSize (guint * width, guint * height) + { + bool ret = true; + + if (!isValid_) + return false; + + switch (native_type_) { + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_CORE_WINDOW: + return getWindowSizeForCoreWindow (width, height); + case GST_GL_WINDOW_WINRT_NATIVE_TYPE_SWAP_CHAIN_PANEL: + return getWindowSizeForSwapChainPanel (width, height); + default: + g_assert_not_reached (); + break; + } + + return false; + } + +private: + bool + registerSizeChangedHandlerForCoreWindow (GstGLWindow * window) + { + ComPtr resize_handler; + HRESULT hr; + + hr = MakeAndInitialize(&resize_handler, window); + if (FAILED (hr)) { + GST_WARNING ("Couldn't creat resize handler object"); + return false; + } + + hr = run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [this, resize_handler] { + return core_window_->add_SizeChanged (resize_handler.Get(), + &event_token_); + }); + + if (FAILED (hr)) { + GST_WARNING ("Couldn't install resize handler"); + return false; + } + + return true; + } + + bool + registerSizeChangedHandlerForSwapChainPanel (GstGLWindow * window) + { + ComPtr resize_handler; + ComPtr framework; + HRESULT hr; + + hr = MakeAndInitialize(&resize_handler, window); + if (FAILED (hr)) { + GST_WARNING ("Couldn't creat resize handler object"); + return false; + } + + hr = panel_.As (&framework); + if (FAILED (hr)) { + GST_WARNING ("Couldn't get IFrameworkElement interface"); + return false; + } + + hr = run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [this, framework, resize_handler] { + return framework->add_SizeChanged (resize_handler.Get(), + &event_token_); + }); + + if (FAILED (hr)) { + GST_WARNING ("Couldn't install resize handler"); + return false; + } + + return true; + } + + void + unregisterSizeChangedHandlerForCoreWindow (void) + { + run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [this] { + core_window_->remove_SizeChanged(event_token_); + return S_OK; + }); + } + + void + unregisterSizeChangedHandlerForSwapChainPanel (void) + { + ComPtr framework; + HRESULT hr; + + hr = panel_.As (&framework); + if (SUCCEEDED (hr)) { + run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [this, framework] { + return framework->remove_SizeChanged (event_token_); + }); + } + } + + bool + getWindowSizeForCoreWindow (guint * width, guint * height) + { + HRESULT hr; + + hr = run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [this, width, height] { + HRESULT async_hr; + Rect bounds; + + async_hr = core_window_->get_Bounds (&bounds); + if (SUCCEEDED (async_hr)) { + float dpi; + + dpi = get_logical_dpi (); + + *width = (guint) dip_to_pixel (bounds.Width, dpi); + *height = (guint) dip_to_pixel (bounds.Height, dpi); + } + + return async_hr; + }); + + return SUCCEEDED (hr); + } + + bool + getWindowSizeForSwapChainPanel (guint * width, guint * height) + { + HRESULT hr; + ComPtr ui; + + hr = panel_.As (&ui); + if (FAILED (hr)) + return false; + + hr = run_async (dispatcher_, DEFAULT_ASYNC_TIMEOUT, + [ui, width, height] { + Size size; + HRESULT async_hr; + + async_hr = ui->get_RenderSize (&size); + if (SUCCEEDED (async_hr)) { + *width = (guint) size.Width; + *height = (guint) size.Height; + } + + return async_hr; + }); + + return SUCCEEDED (hr); + } + +private: + GstGLWindowWinRTNativeType native_type_; + ComPtr dispatcher_; + + ComPtr core_window_; + ComPtr panel_; + + EventRegistrationToken event_token_; + bool isValid_; +}; + +struct _GstGLWindowWinRTEGLPrivate +{ + GstGLWindowWinRTEGLResizeHandler *resize_handler; + guint surface_width; + guint surface_height; + GMutex event_lock; +}; + +#define gst_gl_window_winrt_egl_parent_class parent_class +G_DEFINE_TYPE_WITH_PRIVATE (GstGLWindowWinRTEGL, gst_gl_window_winrt_egl, GST_TYPE_GL_WINDOW); +static void gst_gl_window_winrt_egl_dispose (GObject * object); +static void gst_gl_window_winrt_egl_finalize (GObject * object); static guintptr gst_gl_window_winrt_egl_get_display (GstGLWindow * window); static guintptr gst_gl_window_winrt_egl_get_window_handle (GstGLWindow * window); static void gst_gl_window_winrt_egl_set_window_handle (GstGLWindow * window, guintptr handle); +static void gst_gl_window_winrt_egl_show (GstGLWindow * window); static void gst_gl_window_winrt_egl_class_init (GstGLWindowWinRTEGLClass * klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstGLWindowClass *window_class = (GstGLWindowClass *) klass; + gobject_class->dispose = gst_gl_window_winrt_egl_dispose; + gobject_class->finalize = gst_gl_window_winrt_egl_finalize; + window_class->get_display = GST_DEBUG_FUNCPTR (gst_gl_window_winrt_egl_get_display); window_class->get_window_handle = GST_DEBUG_FUNCPTR (gst_gl_window_winrt_egl_get_window_handle); window_class->set_window_handle = GST_DEBUG_FUNCPTR (gst_gl_window_winrt_egl_set_window_handle); + window_class->show = + GST_DEBUG_FUNCPTR (gst_gl_window_winrt_egl_show); } static void gst_gl_window_winrt_egl_init (GstGLWindowWinRTEGL * window_winrt) { + window_winrt->priv = (GstGLWindowWinRTEGLPrivate *) + gst_gl_window_winrt_egl_get_instance_private (window_winrt); + + g_mutex_init (&window_winrt->priv->event_lock); +} + +static void +gst_gl_window_winrt_egl_dispose (GObject * object) +{ + GstGLWindowWinRTEGL *window_egl = GST_GL_WINDOW_WINRT_EGL (object); + GstGLWindowWinRTEGLPrivate *priv = window_egl->priv; + + if (priv->resize_handler) { + delete priv->resize_handler; + priv->resize_handler = nullptr; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_gl_window_winrt_egl_finalize (GObject * object) +{ + GstGLWindowWinRTEGL *window_egl = GST_GL_WINDOW_WINRT_EGL (object); + GstGLWindowWinRTEGLPrivate *priv = window_egl->priv; + + g_mutex_clear (&priv->event_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); } static void @@ -65,10 +559,31 @@ gst_gl_window_winrt_egl_set_window_handle (GstGLWindow * window, guintptr handle) { GstGLWindowWinRTEGL *window_egl = GST_GL_WINDOW_WINRT_EGL (window); + GstGLWindowWinRTEGLPrivate *priv = window_egl->priv; GST_INFO_OBJECT (window, "Setting WinRT EGL window handle: %p", handle); window_egl->window = (EGLNativeWindowType) handle; + + if (priv->resize_handler) + delete priv->resize_handler; + priv->resize_handler = nullptr; + + if (!handle) { + GST_WARNING_OBJECT (window, "NULL window handle"); + return; + } + + priv->resize_handler = + new GstGLWindowWinRTEGLResizeHandler + (reinterpret_cast (handle), window); + + if (!priv->resize_handler->IsValid ()) { + GST_WARNING_OBJECT (window, + "Invalid window handle %" G_GUINTPTR_FORMAT, handle); + delete priv->resize_handler; + priv->resize_handler = nullptr; + } } static guintptr @@ -95,7 +610,8 @@ gst_gl_window_winrt_egl_new (GstGLDisplay * display) GST_INFO_OBJECT (display, "Creating WinRT EGL window"); - window_egl = g_object_new (GST_TYPE_GL_WINDOW_WINRT_EGL, NULL); + window_egl = (GstGLWindowWinRTEGL *) + g_object_new (GST_TYPE_GL_WINDOW_WINRT_EGL, NULL); return window_egl; } @@ -106,3 +622,46 @@ gst_gl_window_winrt_egl_get_display (GstGLWindow * window) /* EGL_DEFAULT_DISPLAY */ return 0; } + +static void +gst_gl_window_winrt_egl_show (GstGLWindow * window) +{ + GstGLWindowWinRTEGL *window_egl = GST_GL_WINDOW_WINRT_EGL (window); + GstGLWindowWinRTEGLPrivate *priv = window_egl->priv; + guint width, height; + gboolean resize = FALSE; + + if (!priv->resize_handler) + return; + + g_mutex_lock (&priv->event_lock); + if (!priv->surface_width || !priv->surface_height) { + if (priv->resize_handler->GetWindowSize (&width, &height)) { + GST_INFO_OBJECT (window, "Client window size %dx%d", width, height); + priv->surface_width = width; + priv->surface_height = height; + resize = TRUE; + } + } + g_mutex_unlock (&priv->event_lock); + + if (resize) + gst_gl_window_resize (window, width, height); +} + +static void +gst_gl_window_winrt_egl_on_resize (GstGLWindow * window, + guint width, guint height) +{ + GstGLWindowWinRTEGL *window_egl = GST_GL_WINDOW_WINRT_EGL (window); + GstGLWindowWinRTEGLPrivate *priv = window_egl->priv; + + GST_DEBUG_OBJECT (window, "New client window size %dx%d", width, height); + + g_mutex_lock (&priv->event_lock); + priv->surface_width = width; + priv->surface_height = height; + g_mutex_unlock (&priv->event_lock); + + gst_gl_window_resize (window, width, height); +} \ No newline at end of file diff --git a/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.h b/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.h index 08bff31ac3..7c95528b64 100644 --- a/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.h +++ b/gst-libs/gst/gl/winrt/gstglwindow_winrt_egl.h @@ -37,6 +37,7 @@ GType gst_gl_window_winrt_egl_get_type (void); typedef struct _GstGLWindowWinRTEGL GstGLWindowWinRTEGL; typedef struct _GstGLWindowWinRTEGLClass GstGLWindowWinRTEGLClass; +typedef struct _GstGLWindowWinRTEGLPrivate GstGLWindowWinRTEGLPrivate; struct _GstGLWindowWinRTEGL { /*< private >*/ @@ -46,6 +47,8 @@ struct _GstGLWindowWinRTEGL { * ICoreWindow, ISwapChainPanel, IPropertySet */ EGLNativeWindowType window; + GstGLWindowWinRTEGLPrivate *priv; + gpointer _reserved[GST_PADDING]; };