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: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/745>
This commit is contained in:
Seungha Yang 2020-07-09 22:23:33 +09:00 committed by GStreamer Merge Bot
parent ab89746f55
commit bdf73569f8
3 changed files with 569 additions and 5 deletions

View file

@ -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

View file

@ -1,6 +1,7 @@
/*
* GStreamer
* Copyright (C) 2019 Nirbheek Chauhan <nirbheek@centricular.com>
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -27,37 +28,530 @@
#include "gstglwindow_winrt_egl.h"
#include "../gstglwindow_private.h"
/* workaround for GetCurrentTime collision */
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.media.dxinterop.h>
#include <windows.applicationmodel.core.h>
#include <windows.graphics.display.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
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 <typename CB>
static HRESULT
run_async (const ComPtr<Core::ICoreDispatcher> &dispatcher, DWORD timeout,
CB &&cb)
{
ComPtr<IAsyncAction> 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<Implements<RuntimeClassFlags<ClassicCom>,
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<Display::IDisplayPropertiesStatics> 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<RuntimeClassFlags<ClassicCom>,
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<RuntimeClassFlags<ClassicCom>,
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<IInspectable> 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<Xaml::IDependencyObject> 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<IWindowSizeChangedEventHandler> resize_handler;
HRESULT hr;
hr = MakeAndInitialize<CoreResizeHandler>(&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<Xaml::ISizeChangedEventHandler> resize_handler;
ComPtr<Xaml::IFrameworkElement> framework;
HRESULT hr;
hr = MakeAndInitialize<PanelResizeHandler>(&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<Xaml::IFrameworkElement> 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<Xaml::IUIElement> 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<Core::ICoreDispatcher> dispatcher_;
ComPtr<Core::ICoreWindow> core_window_;
ComPtr<Xaml::Controls::ISwapChainPanel> 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<IInspectable*> (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);
}

View file

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