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]; };