d3d11: Add support for Universal Windows Platform
authorSeungha Yang <seungha.yang@navercorp.com>
Mon, 30 Dec 2019 09:58:59 +0000 (18:58 +0900)
committerSeungha Yang <seungha.yang@navercorp.com>
Mon, 6 Jan 2020 11:14:51 +0000 (20:14 +0900)
Initial UWP support via new window (CoreWindow and SwapChainPanel) implementation.

16 files changed:
sys/d3d11/gstd3d11_fwd.h
sys/d3d11/gstd3d11device.c
sys/d3d11/gstd3d11device.h
sys/d3d11/gstd3d11utils.c
sys/d3d11/gstd3d11videosink.c
sys/d3d11/gstd3d11window.c [deleted file]
sys/d3d11/gstd3d11window.cpp [new file with mode: 0644]
sys/d3d11/gstd3d11window.h
sys/d3d11/gstd3d11window_corewindow.cpp [new file with mode: 0644]
sys/d3d11/gstd3d11window_corewindow.h [new file with mode: 0644]
sys/d3d11/gstd3d11window_swapchainpanel.cpp [new file with mode: 0644]
sys/d3d11/gstd3d11window_swapchainpanel.h [new file with mode: 0644]
sys/d3d11/gstd3d11window_win32.cpp [new file with mode: 0644]
sys/d3d11/gstd3d11window_win32.h [new file with mode: 0644]
sys/d3d11/meson.build
sys/d3d11/plugin.c

index 6d8c372..1a1dc08 100644 (file)
@@ -21,7 +21,7 @@
 #define __GST_D3D11_FWD_H__
 
 #include <gst/gst.h>
-#include "d3d11config.h"
+#include "gstd3d11config.h"
 
 #ifndef INITGUID
 #include <initguid.h>
index 7f09631..56553d5 100644 (file)
@@ -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:
index 5cffd8a..7827943 100644 (file)
@@ -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);
 
index cf933b7..e396f3c 100644 (file)
@@ -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);
   }
index 5fceabf..237a280 100644 (file)
 #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 (file)
index 561b5c6..0000000
+++ /dev/null
@@ -1,1598 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
- * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
- * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
- *
- * 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 <windows.h>
-
-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 (file)
index 0000000..dde2882
--- /dev/null
@@ -0,0 +1,940 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <windows.ui.xaml.h>
+#include <windows.applicationmodel.core.h>
+#include <wrl.h>
+#include <wrl/wrappers/corewrappers.h>
+
+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<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
+    ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
+    ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> 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
index b9c7366..6118fd7 100644 (file)
@@ -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 (file)
index 0000000..7432609
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <windows.ui.xaml.h>
+#include <windows.ui.xaml.media.dxinterop.h>
+#include <windows.applicationmodel.core.h>
+#include <wrl.h>
+#include <wrl/wrappers/corewrappers.h>
+#include <windows.graphics.display.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;
+
+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::ICoreWindow> 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<Display::IDisplayPropertiesStatics> 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<RuntimeClassFlags<ClassicCom>,
+        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<IInspectable> inspectable;
+  ComPtr<IWindowSizeChangedEventHandler> resize_handler;
+  Rect bounds;
+
+  if (!window->external_handle) {
+    GST_ERROR_OBJECT (self, "No external window handle");
+    return;
+  }
+
+  inspectable = reinterpret_cast<IInspectable*> (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<CoreResizeHandler>(&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<IDXGISwapChain1> 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 (file)
index 0000000..c4670f0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/video/video.h>
+#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 (file)
index 0000000..c780d04
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <windows.ui.xaml.h>
+#include <windows.ui.xaml.media.dxinterop.h>
+#include <windows.applicationmodel.core.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;
+
+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<Xaml::Controls::ISwapChainPanel> panel;
+  ComPtr<Core::ICoreDispatcher> dispatcher;
+  ComPtr<IDXGISwapChain1> 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<RuntimeClassFlags<ClassicCom>,
+        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 <typename CB>
+static HRESULT
+run_async (const ComPtr<Core::ICoreDispatcher> &dispatcher, HANDLE cancellable,
+    DWORD timeout, CB &&cb)
+{
+  ComPtr<IAsyncAction> 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<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.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<Core::ICoreDispatcher> &dispatcher,
+    HANDLE cancellable,
+    const ComPtr<Xaml::Controls::ISwapChainPanel> &panel, Size *size)
+{
+  ComPtr<Xaml::IUIElement> 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<IInspectable> inspectable;
+  ComPtr<Xaml::IDependencyObject> dependency_obj;
+  Size size;
+  ComPtr<Xaml::ISizeChangedEventHandler> resize_handler;
+  ComPtr<Xaml::IFrameworkElement> framework;
+
+  if (!window->external_handle) {
+    GST_ERROR_OBJECT (self, "No external window handle");
+    return;
+  }
+
+  inspectable = reinterpret_cast<IInspectable*> (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<PanelResizeHandler>(&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<Xaml::IFrameworkElement> 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<IDXGISwapChain1> new_swapchain;
+  ComPtr<ISwapChainPanelNative> 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 (file)
index 0000000..d953d70
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/video/video.h>
+#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 (file)
index 0000000..e9f6302
--- /dev/null
@@ -0,0 +1,924 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
+ * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 (file)
index 0000000..6759beb
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * GStreamer
+ * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/video/video.h>
+#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__ */
index 52e235c..fb7141a 100644 (file)
@@ -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 <winapifamily.h>
+    #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 <winapifamily.h>
+      #include <windows.applicationmodel.core.h>
+      #include <wrl.h>
+      #include <wrl/wrappers/corewrappers.h>
+      #include <d3d11.h>
+      #include <dxgi1_2.h>
+      #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,
index cd9f7e6..b57c780 100644 (file)
@@ -21,7 +21,7 @@
 #include "config.h"
 #endif
 
-#include "d3d11config.h"
+#include "gstd3d11config.h"
 
 #include <gst/gst.h>
 #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 */