d3d11videosink: Add new Direct3D11 video render plugin
authorSeungha Yang <seungha.yang@navercorp.com>
Wed, 30 Jan 2019 11:07:29 +0000 (20:07 +0900)
committerSebastian Dröge <slomo@coaxion.net>
Mon, 8 Jul 2019 08:31:47 +0000 (08:31 +0000)
Direct3D11 was shipped as part of Windows7 and it's obviously
primary graphics API on Windows.

This plugin includes HDR10 rendering if following requirements are satisfied
* IDXGISwapChain4::SetHDRMetaData is available (decleared in dxgi1_5.h)
* Display can support DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 color space
* Upstream provides 10 bitdepth format with smpte-st 2084 static metadata

17 files changed:
meson_options.txt
sys/d3d11/gstd3d11_fwd.h [new file with mode: 0644]
sys/d3d11/gstd3d11bufferpool.c [new file with mode: 0644]
sys/d3d11/gstd3d11bufferpool.h [new file with mode: 0644]
sys/d3d11/gstd3d11device.c [new file with mode: 0644]
sys/d3d11/gstd3d11device.h [new file with mode: 0644]
sys/d3d11/gstd3d11memory.c [new file with mode: 0644]
sys/d3d11/gstd3d11memory.h [new file with mode: 0644]
sys/d3d11/gstd3d11utils.c [new file with mode: 0644]
sys/d3d11/gstd3d11utils.h [new file with mode: 0644]
sys/d3d11/gstd3d11videosink.c [new file with mode: 0644]
sys/d3d11/gstd3d11videosink.h [new file with mode: 0644]
sys/d3d11/gstd3d11window.c [new file with mode: 0644]
sys/d3d11/gstd3d11window.h [new file with mode: 0644]
sys/d3d11/meson.build [new file with mode: 0644]
sys/d3d11/plugin.c [new file with mode: 0644]
sys/meson.build

index 2a6dca3..d6db86f 100644 (file)
@@ -87,6 +87,7 @@ option('colormanagement', type : 'feature', value : 'auto', description : 'Color
 option('curl', type : 'feature', value : 'auto', description : 'cURL network source and sink plugin')
 option('curl-ssh2', type : 'feature', value : 'auto', description : 'cURL network source and sink plugin libssh2 support')
 option('d3dvideosink', type : 'feature', value : 'auto', description : 'Direct3D video sink plugin')
+option('d3d11', type : 'feature', value : 'auto', description : 'Direct3D11 plugin')
 option('dash', type : 'feature', value : 'auto', description : 'DASH demuxer plugin')
 option('dc1394', type : 'feature', value : 'auto', description : 'libdc1394 IIDC camera source plugin')
 option('decklink', type : 'feature', value : 'auto', description : 'DeckLink audio/video source/sink plugin')
diff --git a/sys/d3d11/gstd3d11_fwd.h b/sys/d3d11/gstd3d11_fwd.h
new file mode 100644 (file)
index 0000000..8643fd9
--- /dev/null
@@ -0,0 +1,59 @@
+/* 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_FWD_H__
+#define __GST_D3D11_FWD_H__
+
+#include <gst/gst.h>
+
+/* define COBJMACROS to use d3d11 C APIs */
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif
+
+#ifndef INITGUID
+#include <initguid.h>
+#endif
+
+#include <d3d11.h>
+#ifdef HAVE_DXGI_1_5_H
+#include <dxgi1_5.h>
+#else
+#include <dxgi.h>
+#endif
+
+G_BEGIN_DECLS
+
+typedef struct _GstD3D11Device GstD3D11Device;
+typedef struct _GstD3D11DeviceClass GstD3D11DeviceClass;
+typedef struct _GstD3D11DevicePrivate GstD3D11DevicePrivate;
+
+typedef struct _GstD3D11AllocationParams GstD3D11AllocationParams;
+typedef struct _GstD3D11Memory GstD3D11Memory;
+typedef struct _GstD3D11Allocator GstD3D11Allocator;
+typedef struct _GstD3D11AllocatorClass GstD3D11AllocatorClass;
+typedef struct _GstD3D11AllocatorPrivate GstD3D11AllocatorPrivate;
+
+typedef struct _GstD3D11BufferPool GstD3D11BufferPool;
+typedef struct _GstD3D11BufferPoolClass GstD3D11BufferPoolClass;
+typedef struct _GstD3D11BufferPoolPrivate GstD3D11BufferPoolPrivate;
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_FWD_H__ */
diff --git a/sys/d3d11/gstd3d11bufferpool.c b/sys/d3d11/gstd3d11bufferpool.c
new file mode 100644 (file)
index 0000000..7e1058c
--- /dev/null
@@ -0,0 +1,308 @@
+/* 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 "gstd3d11bufferpool.h"
+#include "gstd3d11memory.h"
+#include "gstd3d11device.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_d3d11_buffer_pool_debug);
+#define GST_CAT_DEFAULT gst_d3d11_buffer_pool_debug
+
+struct _GstD3D11BufferPoolPrivate
+{
+  GstD3D11Device *device;
+  GstD3D11Allocator *allocator;
+  GstCaps *caps;
+
+  gboolean add_videometa;
+  GstD3D11AllocationParams *d3d11_params;
+};
+
+#define gst_d3d11_buffer_pool_parent_class parent_class
+G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11BufferPool,
+    gst_d3d11_buffer_pool, GST_TYPE_BUFFER_POOL);
+
+static void gst_d3d11_buffer_pool_dispose (GObject * object);
+static const gchar **gst_d3d11_buffer_pool_get_options (GstBufferPool * pool);
+static gboolean gst_d3d11_buffer_pool_set_config (GstBufferPool * pool,
+    GstStructure * config);
+static GstFlowReturn gst_d3d11_buffer_pool_alloc (GstBufferPool * pool,
+    GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
+
+static void
+gst_d3d11_buffer_pool_class_init (GstD3D11BufferPoolClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstBufferPoolClass *bufferpool_class = GST_BUFFER_POOL_CLASS (klass);
+
+  gobject_class->dispose = gst_d3d11_buffer_pool_dispose;
+
+  bufferpool_class->get_options = gst_d3d11_buffer_pool_get_options;
+  bufferpool_class->set_config = gst_d3d11_buffer_pool_set_config;
+  bufferpool_class->alloc_buffer = gst_d3d11_buffer_pool_alloc;
+
+  GST_DEBUG_CATEGORY_INIT (gst_d3d11_buffer_pool_debug, "d3d11bufferpool", 0,
+      "d3d11bufferpool object");
+}
+
+static void
+gst_d3d11_buffer_pool_init (GstD3D11BufferPool * self)
+{
+  self->priv = gst_d3d11_buffer_pool_get_instance_private (self);
+}
+
+static void
+gst_d3d11_buffer_pool_dispose (GObject * object)
+{
+  GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (object);
+  GstD3D11BufferPoolPrivate *priv = self->priv;
+
+  if (priv->d3d11_params)
+    gst_d3d11_allocation_params_free (priv->d3d11_params);
+  priv->d3d11_params = NULL;
+  gst_clear_object (&priv->device);
+  gst_clear_object (&priv->allocator);
+  gst_clear_caps (&priv->caps);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static const gchar **
+gst_d3d11_buffer_pool_get_options (GstBufferPool * pool)
+{
+  static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
+    GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT,
+    NULL
+  };
+  return options;
+}
+
+static gboolean
+gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
+{
+  GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
+  GstD3D11BufferPoolPrivate *priv = self->priv;
+  GstVideoInfo info;
+  GstCaps *caps = NULL;
+  guint min_buffers, max_buffers;
+  guint max_align, n;
+  GstAllocator *allocator = NULL;
+  GstAllocationParams alloc_params;
+  gboolean ret = TRUE;
+  D3D11_TEXTURE2D_DESC *desc;
+
+  if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min_buffers,
+          &max_buffers))
+    goto wrong_config;
+
+  if (caps == NULL)
+    goto no_caps;
+
+  /* now parse the caps from the config */
+  if (!gst_video_info_from_caps (&info, caps))
+    goto wrong_caps;
+
+  GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
+      caps);
+
+  if (!gst_buffer_pool_config_get_allocator (config, &allocator, &alloc_params))
+    goto wrong_config;
+
+  gst_caps_replace (&priv->caps, caps);
+
+  if (priv->allocator)
+    gst_object_unref (priv->allocator);
+
+  if (allocator) {
+    if (!GST_IS_D3D11_ALLOCATOR (allocator)) {
+      gst_object_unref (allocator);
+      goto wrong_allocator;
+    } else {
+      priv->allocator = gst_object_ref (allocator);
+    }
+  } else {
+    priv->allocator = gst_d3d11_allocator_new (priv->device);
+    g_assert (priv->allocator);
+  }
+
+  priv->add_videometa = gst_buffer_pool_config_has_option (config,
+      GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+  if (priv->d3d11_params)
+    gst_d3d11_allocation_params_free (priv->d3d11_params);
+  priv->d3d11_params =
+      gst_buffer_pool_config_get_d3d11_allocation_params (config);
+  if (!priv->d3d11_params)
+    priv->d3d11_params = gst_d3d11_allocation_params_new (&alloc_params,
+        &info, NULL);
+
+  desc = &priv->d3d11_params->desc;
+
+  GST_LOG_OBJECT (self, "Direct3D11 Allocation params");
+  GST_LOG_OBJECT (self, "\t%dx%d, DXGI format %d",
+      desc->Width, desc->Height, desc->Format);
+  GST_LOG_OBJECT (self, "\tMipLevel %d, ArraySize %d",
+      desc->MipLevels, desc->ArraySize);
+  GST_LOG_OBJECT (self, "\tSampleDesc.Count %d, SampleDesc.Quality %d",
+      desc->SampleDesc.Count, desc->SampleDesc.Quality);
+  GST_LOG_OBJECT (self, "\tUsage %d", desc->Usage);
+  GST_LOG_OBJECT (self, "\tBindFlags 0x%x", desc->BindFlags);
+  GST_LOG_OBJECT (self, "\tCPUAccessFlags 0x%x", desc->CPUAccessFlags);
+  GST_LOG_OBJECT (self, "\tMiscFlags 0x%x", desc->MiscFlags);
+
+  max_align = alloc_params.align;
+
+  if (gst_buffer_pool_config_has_option (config,
+          GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
+    priv->add_videometa = TRUE;
+
+    gst_buffer_pool_config_get_video_alignment (config,
+        &priv->d3d11_params->align);
+
+    for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
+      max_align |= priv->d3d11_params->align.stride_align[n];
+
+    for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
+      priv->d3d11_params->align.stride_align[n] = max_align;
+
+    gst_video_info_align (&priv->d3d11_params->info,
+        &priv->d3d11_params->align);
+
+    gst_buffer_pool_config_set_video_alignment (config,
+        &priv->d3d11_params->align);
+  }
+
+  if (alloc_params.align < max_align) {
+    GST_WARNING_OBJECT (pool, "allocation params alignment %u is smaller "
+        "than the max specified video stride alignment %u, fixing",
+        (guint) alloc_params.align, max_align);
+
+    alloc_params.align = priv->d3d11_params->parent.align = max_align;
+    gst_buffer_pool_config_set_allocator (config, allocator, &alloc_params);
+    gst_allocation_params_copy (&alloc_params);
+  }
+
+  gst_buffer_pool_config_set_params (config,
+      caps, GST_VIDEO_INFO_SIZE (&priv->d3d11_params->info), min_buffers,
+      max_buffers);
+
+  return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config) && ret;
+
+  /* ERRORS */
+wrong_config:
+  {
+    GST_WARNING_OBJECT (pool, "invalid config");
+    return FALSE;
+  }
+no_caps:
+  {
+    GST_WARNING_OBJECT (pool, "no caps in config");
+    return FALSE;
+  }
+wrong_caps:
+  {
+    GST_WARNING_OBJECT (pool,
+        "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+    return FALSE;
+  }
+wrong_allocator:
+  {
+    GST_WARNING_OBJECT (pool, "Incorrect allocator type for this pool");
+    return FALSE;
+  }
+}
+
+static GstFlowReturn
+gst_d3d11_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
+    GstBufferPoolAcquireParams * params)
+{
+  GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
+  GstD3D11BufferPoolPrivate *priv = self->priv;
+  GstMemory *mem;
+  GstBuffer *buf;
+  GstVideoInfo *info = &priv->d3d11_params->info;
+
+  mem = gst_d3d11_allocator_alloc (priv->allocator, priv->d3d11_params);
+
+  if (!mem) {
+    GST_ERROR_OBJECT (self, "cannot create texture memory");
+    return GST_FLOW_ERROR;
+  }
+
+  buf = gst_buffer_new ();
+  gst_buffer_append_memory (buf, mem);
+
+  if (priv->add_videometa) {
+    GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
+
+    GST_DEBUG_OBJECT (self, "adding GstVideoMeta");
+    gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
+        GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
+        GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
+        dmem->offset, dmem->stride);
+  }
+
+  *buffer = buf;
+
+  return GST_FLOW_OK;
+}
+
+GstD3D11BufferPool *
+gst_d3d11_buffer_pool_new (GstD3D11Device * device)
+{
+  GstD3D11BufferPool *pool;
+  GstD3D11Allocator *alloc;
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  pool = g_object_new (GST_TYPE_D3D11_BUFFER_POOL, NULL);
+  alloc = gst_d3d11_allocator_new (device);
+
+  pool->priv->device = gst_object_ref (device);
+  pool->priv->allocator = alloc;
+
+  return pool;
+}
+
+GstD3D11AllocationParams *
+gst_buffer_pool_config_get_d3d11_allocation_params (GstStructure * config)
+{
+  GstD3D11AllocationParams *ret;
+
+  if (!gst_structure_get (config, "d3d11-allocation-params",
+          GST_TYPE_D3D11_ALLOCATION_PARAMS, &ret, NULL))
+    ret = NULL;
+
+  return ret;
+}
+
+void
+gst_buffer_pool_config_set_d3d11_allocation_params (GstStructure * config,
+    GstD3D11AllocationParams * params)
+{
+  g_return_if_fail (config != NULL);
+  g_return_if_fail (params != NULL);
+
+  gst_structure_set (config, "d3d11-allocation-params",
+      GST_TYPE_D3D11_ALLOCATION_PARAMS, params, NULL);
+}
diff --git a/sys/d3d11/gstd3d11bufferpool.h b/sys/d3d11/gstd3d11bufferpool.h
new file mode 100644 (file)
index 0000000..bb86e16
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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_BUFFER_POOL_H__
+#define __GST_D3D11_BUFFER_POOL_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstd3d11_fwd.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_BUFFER_POOL            (gst_d3d11_buffer_pool_get_type())
+#define GST_D3D11_BUFFER_POOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPool))
+#define GST_D3D11_BUFFER_POOL_CLASS(klass)    (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPoolClass))
+#define GST_IS_D3D11_BUFFER_POOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_BUFFER_POOL))
+#define GST_IS_D3D11_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_BUFFER_POOL))
+#define GST_D3D11_BUFFER_POOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPoolClass))
+
+struct _GstD3D11BufferPool
+{
+  GstBufferPool parent;
+
+  /*< private >*/
+  GstD3D11BufferPoolPrivate *priv;
+
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstD3D11BufferPoolClass
+{
+  GstBufferPoolClass bufferpool_class;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType                gst_d3d11_buffer_pool_get_type  (void);
+
+GstD3D11BufferPool * gst_d3d11_buffer_pool_new       (GstD3D11Device *device);
+
+GstD3D11AllocationParams * gst_buffer_pool_config_get_d3d11_allocation_params (GstStructure * config);
+
+void                 gst_buffer_pool_config_set_d3d11_allocation_params (GstStructure * config,
+                                                                         GstD3D11AllocationParams * params);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_BUFFER_POOL_H__ */
diff --git a/sys/d3d11/gstd3d11device.c b/sys/d3d11/gstd3d11device.c
new file mode 100644 (file)
index 0000000..a586d1c
--- /dev/null
@@ -0,0 +1,898 @@
+/* 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 "gstd3d11device.h"
+#include "gmodule.h"
+
+#ifdef HAVE_D3D11SDKLAYER_H
+#include <d3d11sdklayers.h>
+#endif
+
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
+GST_DEBUG_CATEGORY_STATIC (gst_d3d11_device_debug);
+#define GST_CAT_DEFAULT gst_d3d11_device_debug
+
+#ifdef HAVE_D3D11SDKLAYER_H
+static GModule *sdk_layer = NULL;
+#endif
+
+enum
+{
+  PROP_0,
+  PROP_ADAPTER
+};
+
+#define DEFAULT_ADAPTER -1
+
+struct _GstD3D11DevicePrivate
+{
+  gint adapter;
+
+  ID3D11Device *device;
+  ID3D11DeviceContext *device_context;
+
+  IDXGIFactory1 *factory;
+  GstD3D11DXGIFactoryVersion factory_ver;
+
+  ID3D11VideoDevice *video_device;
+  ID3D11VideoContext *video_context;
+
+  GMutex lock;
+  GCond cond;
+  GThread *thread;
+  GThread *active_thread;
+  GMainLoop *loop;
+  GMainContext *main_context;
+
+#ifdef HAVE_D3D11SDKLAYER_H
+  ID3D11Debug *debug;
+  ID3D11InfoQueue *info_queue;
+#endif
+};
+
+#define gst_d3d11_device_parent_class parent_class
+G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11Device, gst_d3d11_device, GST_TYPE_OBJECT);
+
+static void gst_d3d11_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_d3d11_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_d3d11_device_constructed (GObject * object);
+static void gst_d3d11_device_dispose (GObject * object);
+static void gst_d3d11_device_finalize (GObject * object);
+
+static gpointer gst_d3d11_device_thread_func (gpointer data);
+
+#ifdef HAVE_D3D11SDKLAYER_H
+static gboolean
+gst_d3d11_device_enable_debug_layer (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    sdk_layer = g_module_open ("d3d11sdklayers.dll", G_MODULE_BIND_LAZY);
+
+    if (!sdk_layer)
+      sdk_layer = g_module_open ("d3d11_1sdklayers.dll", G_MODULE_BIND_LAZY);
+
+    g_once_init_leave (&_init, 1);
+  }
+
+  return ! !sdk_layer;
+}
+
+static gboolean
+gst_d3d11_device_get_message (GstD3D11Device * self)
+{
+  GstD3D11DevicePrivate *priv = self->priv;
+  D3D11_MESSAGE *msg;
+  SIZE_T msg_len = 0;
+  HRESULT hr;
+  UINT64 num_msg, i;
+
+  num_msg = ID3D11InfoQueue_GetNumStoredMessages (priv->info_queue);
+
+  for (i = 0; i < num_msg; i++) {
+    hr = ID3D11InfoQueue_GetMessage (priv->info_queue, i, NULL, &msg_len);
+
+    if (FAILED (hr) || msg_len == 0) {
+      return G_SOURCE_CONTINUE;
+    }
+
+    msg = (D3D11_MESSAGE *) g_malloc0 (msg_len);
+    hr = ID3D11InfoQueue_GetMessage (priv->info_queue, i, msg, &msg_len);
+
+    GST_TRACE_OBJECT (self, "D3D11 Message - %s", msg->pDescription);
+    g_free (msg);
+  }
+
+  ID3D11InfoQueue_ClearStoredMessages (priv->info_queue);
+
+  return G_SOURCE_CONTINUE;
+}
+#endif
+
+static void
+gst_d3d11_device_class_init (GstD3D11DeviceClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gst_d3d11_device_set_property;
+  gobject_class->get_property = gst_d3d11_device_get_property;
+  gobject_class->constructed = gst_d3d11_device_constructed;
+  gobject_class->dispose = gst_d3d11_device_dispose;
+  gobject_class->finalize = gst_d3d11_device_finalize;
+
+  g_object_class_install_property (gobject_class, PROP_ADAPTER,
+      g_param_spec_int ("adapter", "Adapter",
+          "Adapter index for creating device (-1 for default)",
+          -1, G_MAXINT32, DEFAULT_ADAPTER,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug,
+      "d3d11device", 0, "d3d11 device");
+  GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
+}
+
+static void
+gst_d3d11_device_init (GstD3D11Device * self)
+{
+  GstD3D11DevicePrivate *priv;
+
+  priv = gst_d3d11_device_get_instance_private (self);
+  priv->adapter = DEFAULT_ADAPTER;
+
+  g_mutex_init (&priv->lock);
+  g_cond_init (&priv->cond);
+
+  priv->main_context = g_main_context_new ();
+  priv->loop = g_main_loop_new (priv->main_context, FALSE);
+
+  self->priv = priv;
+}
+
+static void
+_relase_adapter (IDXGIAdapter1 * adapter)
+{
+  IDXGIAdapter1_Release (adapter);
+}
+
+static void
+gst_d3d11_device_constructed (GObject * object)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (object);
+  GstD3D11DevicePrivate *priv = self->priv;
+  IDXGIAdapter1 *adapter = NULL;
+  GList *adapter_list = NULL;
+  GList *hw_adapter_list = NULL;
+  IDXGIFactory1 *factory = NULL;
+  HRESULT hr;
+  guint i;
+  guint num_adapter = 0;
+  UINT d3d11_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+
+  static const D3D_DRIVER_TYPE driver_types[] = {
+    D3D_DRIVER_TYPE_HARDWARE,
+    D3D_DRIVER_TYPE_WARP,
+    D3D_DRIVER_TYPE_UNKNOWN
+  };
+  static const D3D_FEATURE_LEVEL feature_levels[] = {
+    D3D_FEATURE_LEVEL_11_1,
+    D3D_FEATURE_LEVEL_11_0,
+    D3D_FEATURE_LEVEL_10_1,
+    D3D_FEATURE_LEVEL_10_0,
+    D3D_FEATURE_LEVEL_9_3,
+    D3D_FEATURE_LEVEL_9_2,
+    D3D_FEATURE_LEVEL_9_1
+  };
+  D3D_FEATURE_LEVEL selected_level;
+
+#ifdef HAVE_DXGI_1_5_H
+  hr = CreateDXGIFactory1 (&IID_IDXGIFactory5, (void **) &factory);
+  if (FAILED (hr)) {
+    GST_INFO_OBJECT (self, "IDXGIFactory5 was unavailable");
+    factory = NULL;
+  }
+
+  priv->factory_ver = GST_D3D11_DXGI_FACTORY_5;
+#endif
+
+  if (!factory) {
+    priv->factory_ver = GST_D3D11_DXGI_FACTORY_1;
+    hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &factory);
+  }
+
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (self, "cannot create dxgi factory, hr: 0x%x", (guint) hr);
+    goto error;
+  }
+
+  while (IDXGIFactory1_EnumAdapters1 (factory, num_adapter,
+          &adapter) != DXGI_ERROR_NOT_FOUND) {
+    DXGI_ADAPTER_DESC1 desc;
+
+    hr = IDXGIAdapter1_GetDesc1 (adapter, &desc);
+    if (SUCCEEDED (hr)) {
+      gchar *vender = NULL;
+
+      vender = g_utf16_to_utf8 (desc.Description, -1, NULL, NULL, NULL);
+      GST_DEBUG_OBJECT (self,
+          "adapter index %d: D3D11 device vendor-id: 0x%04x, device-id: 0x%04x, "
+          "Flags: 0x%x, %s",
+          num_adapter, desc.VendorId, desc.DeviceId, desc.Flags, vender);
+      g_free (vender);
+
+      /* DXGI_ADAPTER_FLAG_SOFTWARE is missing in dxgi.h of mingw */
+      if ((desc.Flags & 0x2) != 0x2) {
+        hw_adapter_list = g_list_append (hw_adapter_list, adapter);
+        IDXGIAdapter1_AddRef (adapter);
+      }
+    }
+
+    adapter_list = g_list_append (adapter_list, adapter);
+
+    num_adapter++;
+
+    if (priv->adapter >= 0 && priv->adapter < num_adapter)
+      break;
+  }
+
+  adapter = NULL;
+  if (priv->adapter >= 0) {
+    if (priv->adapter >= num_adapter) {
+      GST_WARNING_OBJECT (self,
+          "Requested index %d is out of scope for adapter", priv->adapter);
+    } else {
+      adapter = (IDXGIAdapter1 *) g_list_nth_data (adapter_list, priv->adapter);
+    }
+  } else if (hw_adapter_list) {
+    adapter = (IDXGIAdapter1 *) g_list_nth_data (hw_adapter_list, 0);
+  } else if (adapter_list) {
+    adapter = (IDXGIAdapter1 *) g_list_nth_data (adapter_list, 0);
+  }
+
+  if (adapter)
+    IDXGIAdapter1_AddRef (adapter);
+
+#ifdef HAVE_D3D11SDKLAYER_H
+  if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_TRACE) {
+    /* DirectX SDK should be installed on system for this */
+    if (gst_d3d11_device_enable_debug_layer ()) {
+      GST_INFO_OBJECT (self, "sdk layer library was loaded");
+      d3d11_flags |= D3D11_CREATE_DEVICE_DEBUG;
+    }
+  }
+#endif
+
+  if (adapter) {
+    hr = D3D11CreateDevice ((IDXGIAdapter *) adapter, D3D_DRIVER_TYPE_UNKNOWN,
+        NULL, d3d11_flags, feature_levels, G_N_ELEMENTS (feature_levels),
+        D3D11_SDK_VERSION, &priv->device, &selected_level,
+        &priv->device_context);
+
+    if (FAILED (hr)) {
+      /* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
+      hr = D3D11CreateDevice ((IDXGIAdapter *) adapter, D3D_DRIVER_TYPE_UNKNOWN,
+          NULL, d3d11_flags, &feature_levels[1],
+          G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &priv->device,
+          &selected_level, &priv->device_context);
+    }
+
+    if (SUCCEEDED (hr)) {
+      GST_DEBUG_OBJECT (self, "Selected feature level 0x%x", selected_level);
+    }
+  } else {
+    for (i = 0; i < G_N_ELEMENTS (driver_types); i++) {
+      hr = D3D11CreateDevice (NULL, driver_types[i], NULL,
+          d3d11_flags,
+          feature_levels, G_N_ELEMENTS (feature_levels),
+          D3D11_SDK_VERSION, &priv->device, &selected_level,
+          &priv->device_context);
+
+      if (FAILED (hr)) {
+        /* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
+        hr = D3D11CreateDevice (NULL, driver_types[i], NULL,
+            d3d11_flags,
+            &feature_levels[1], G_N_ELEMENTS (feature_levels) - 1,
+            D3D11_SDK_VERSION, &priv->device, &selected_level,
+            &priv->device_context);
+      }
+
+      if (SUCCEEDED (hr)) {
+        GST_DEBUG_OBJECT (self, "Selected driver type 0x%x, feature level 0x%x",
+            driver_types[i], selected_level);
+        break;
+      }
+    }
+  }
+
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (self, "cannot create d3d11 device, hr: 0x%x", (guint) hr);
+    goto error;
+  }
+
+  priv->factory = factory;
+
+  if (adapter)
+    IDXGIAdapter1_Release (adapter);
+
+  if (adapter_list)
+    g_list_free_full (adapter_list, (GDestroyNotify) _relase_adapter);
+
+  if (hw_adapter_list)
+    g_list_free_full (hw_adapter_list, (GDestroyNotify) _relase_adapter);
+
+#ifdef HAVE_D3D11SDKLAYER_H
+  if ((d3d11_flags & D3D11_CREATE_DEVICE_DEBUG) == D3D11_CREATE_DEVICE_DEBUG) {
+    ID3D11Debug *debug;
+    ID3D11InfoQueue *info_queue;
+
+    hr = ID3D11Device_QueryInterface (priv->device,
+        &IID_ID3D11Debug, (void **) &debug);
+
+    if (SUCCEEDED (hr)) {
+      GST_DEBUG_OBJECT (self, "D3D11Debug interface available");
+      ID3D11Debug_ReportLiveDeviceObjects (debug, D3D11_RLDO_DETAIL);
+      priv->debug = debug;
+    }
+
+    hr = ID3D11Device_QueryInterface (priv->device,
+        &IID_ID3D11InfoQueue, (void **) &info_queue);
+    if (SUCCEEDED (hr)) {
+      GSource *source;
+
+      GST_DEBUG_OBJECT (self, "D3D11InfoQueue interface available");
+      priv->info_queue = info_queue;
+
+      source = g_idle_source_new ();
+      g_source_set_callback (source, (GSourceFunc) gst_d3d11_device_get_message,
+          self, NULL);
+
+      g_source_attach (source, priv->main_context);
+      g_source_unref (source);
+    }
+  }
+#endif
+
+  g_mutex_lock (&priv->lock);
+  priv->thread = g_thread_new ("GstD3D11Device",
+      (GThreadFunc) gst_d3d11_device_thread_func, self);
+  while (!g_main_loop_is_running (priv->loop))
+    g_cond_wait (&priv->cond, &priv->lock);
+  g_mutex_unlock (&priv->lock);
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  return;
+
+error:
+  if (factory)
+    IDXGIFactory1_Release (factory);
+
+  if (adapter)
+    IDXGIAdapter1_Release (adapter);
+
+  if (adapter_list)
+    g_list_free_full (adapter_list, (GDestroyNotify) _relase_adapter);
+
+  if (hw_adapter_list)
+    g_list_free_full (hw_adapter_list, (GDestroyNotify) _relase_adapter);
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  return;
+}
+
+static void
+gst_d3d11_device_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (object);
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  switch (prop_id) {
+    case PROP_ADAPTER:
+      priv->adapter = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_d3d11_device_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (object);
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  switch (prop_id) {
+    case PROP_ADAPTER:
+      g_value_set_int (value, priv->adapter);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_d3d11_device_dispose (GObject * object)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (object);
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  GST_LOG_OBJECT (self, "dispose");
+
+  if (priv->loop) {
+    g_main_loop_quit (priv->loop);
+  }
+
+  if (priv->thread) {
+    g_thread_join (priv->thread);
+    priv->thread = NULL;
+  }
+
+  if (priv->loop) {
+    g_main_loop_unref (priv->loop);
+    priv->loop = NULL;
+  }
+
+  if (priv->main_context) {
+    g_main_context_unref (priv->main_context);
+    priv->main_context = NULL;
+  }
+#ifdef HAVE_D3D11SDKLAYER_H
+  if (priv->debug) {
+    ID3D11Debug_Release (priv->debug);
+    priv->debug = NULL;
+  }
+
+  if (priv->info_queue) {
+    ID3D11InfoQueue_ClearStoredMessages (priv->info_queue);
+    ID3D11InfoQueue_Release (priv->info_queue);
+    priv->info_queue = NULL;
+  }
+#endif
+
+  if (priv->device) {
+    ID3D11Device_Release (priv->device);
+    priv->device = NULL;
+  }
+
+  if (priv->device_context) {
+    ID3D11DeviceContext_Release (priv->device_context);
+    priv->device_context = NULL;
+  }
+
+  if (priv->video_device) {
+    ID3D11VideoDevice_Release (priv->video_device);
+    priv->video_device = NULL;
+  }
+
+  if (priv->video_context) {
+    ID3D11VideoContext_Release (priv->video_context);
+    priv->video_context = NULL;
+  }
+
+  if (priv->factory) {
+    IDXGIFactory1_Release (priv->factory);
+    priv->factory = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_d3d11_device_finalize (GObject * object)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (object);
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  GST_LOG_OBJECT (self, "finalize");
+
+  g_mutex_clear (&priv->lock);
+  g_cond_clear (&priv->cond);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+running_cb (gpointer user_data)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (user_data);
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  GST_TRACE_OBJECT (self, "Main loop running now");
+
+  g_mutex_lock (&priv->lock);
+  g_cond_signal (&priv->cond);
+  g_mutex_unlock (&priv->lock);
+
+  return G_SOURCE_REMOVE;
+}
+
+static gpointer
+gst_d3d11_device_thread_func (gpointer data)
+{
+  GstD3D11Device *self = GST_D3D11_DEVICE (data);
+  GstD3D11DevicePrivate *priv = self->priv;
+  GSource *source;
+
+  GST_DEBUG_OBJECT (self, "Enter loop");
+  g_main_context_push_thread_default (priv->main_context);
+
+  source = g_idle_source_new ();
+  g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL);
+  g_source_attach (source, priv->main_context);
+  g_source_unref (source);
+
+  priv->active_thread = g_thread_self ();
+  g_main_loop_run (priv->loop);
+
+  g_main_context_pop_thread_default (priv->main_context);
+
+  GST_DEBUG_OBJECT (self, "Exit loop");
+
+  return NULL;
+}
+
+/**
+ * gst_d3d11_device_new:
+ * @adapter: the index of adapter for creating d3d11 device (-1 for default)
+ *
+ * Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter or %NULL
+ * when failed to create D3D11 device with given adapter index.
+ */
+GstD3D11Device *
+gst_d3d11_device_new (gint adapter)
+{
+  GstD3D11Device *device = NULL;
+  GstD3D11DevicePrivate *priv;
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug, "d3d11device", 0,
+        "d3d11 device");
+    g_once_init_leave (&_init, 1);
+  }
+
+  device = g_object_new (GST_TYPE_D3D11_DEVICE, "adapter", adapter, NULL);
+
+  priv = device->priv;
+
+  if (!priv->device || !priv->device_context) {
+    GST_ERROR ("Cannot create d3d11 device");
+    g_object_unref (device);
+    device = NULL;
+  }
+
+  return device;
+}
+
+/**
+ * gst_d3d11_device_get_device:
+ * @device: a #GstD3D11Device
+ *
+ * Used for various D3D11 APIs directly.
+ * Caller must not destroy returned device object.
+ *
+ * Returns: (transfer none): the ID3D11Device
+ */
+ID3D11Device *
+gst_d3d11_device_get_device (GstD3D11Device * device)
+{
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  return device->priv->device;
+}
+
+/**
+ * gst_d3d11_device_get_device_context:
+ * @device: a #GstD3D11Device
+ *
+ * Used for various D3D11 APIs directly.
+ * Caller must not destroy returned device object.
+ *
+ * Returns: (transfer none): the ID3D11DeviceContext
+ */
+ID3D11DeviceContext *
+gst_d3d11_device_get_device_context (GstD3D11Device * device)
+{
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  return device->priv->device_context;
+}
+
+GstD3D11DXGIFactoryVersion
+gst_d3d11_device_get_chosen_dxgi_factory_version (GstD3D11Device * device)
+{
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device),
+      GST_D3D11_DXGI_FACTORY_UNKNOWN);
+
+  return device->priv->factory_ver;
+}
+
+typedef struct
+{
+  GstD3D11Device *device;
+  GstD3D11DeviceThreadFunc func;
+
+  gpointer data;
+  gboolean fired;
+} MessageData;
+
+static gboolean
+gst_d3d11_device_message_callback (MessageData * msg)
+{
+  GstD3D11Device *self = msg->device;
+  GstD3D11DevicePrivate *priv = self->priv;
+
+  msg->func (self, msg->data);
+
+  g_mutex_lock (&priv->lock);
+  msg->fired = TRUE;
+  g_cond_signal (&priv->cond);
+  g_mutex_unlock (&priv->lock);
+
+  return G_SOURCE_REMOVE;
+}
+
+/**
+ * gst_d3d11_device_thread_add:
+ * @device: a #GstD3D11Device
+ * @func: (scope call): a #GstD3D11DeviceThreadFunc
+ * @data: (closure): user data to call @func with
+ *
+ * Execute @func in the D3DDevice thread of @device with @data
+ *
+ * MT-safe
+ */
+void
+gst_d3d11_device_thread_add (GstD3D11Device * device,
+    GstD3D11DeviceThreadFunc func, gpointer data)
+{
+  GstD3D11DevicePrivate *priv;
+  MessageData msg = { 0, };
+
+  g_return_if_fail (GST_IS_D3D11_DEVICE (device));
+  g_return_if_fail (func != NULL);
+
+  priv = device->priv;
+
+  if (priv->active_thread == g_thread_self ()) {
+    func (device, data);
+    return;
+  }
+
+  msg.device = gst_object_ref (device);
+  msg.func = func;
+  msg.data = data;
+  msg.fired = FALSE;
+
+  g_main_context_invoke (priv->main_context,
+      (GSourceFunc) gst_d3d11_device_message_callback, &msg);
+
+  g_mutex_lock (&priv->lock);
+  while (!msg.fired)
+    g_cond_wait (&priv->cond, &priv->lock);
+  g_mutex_unlock (&priv->lock);
+
+  gst_object_unref (device);
+}
+
+typedef struct
+{
+  IDXGISwapChain *swap_chain;
+  const DXGI_SWAP_CHAIN_DESC *desc;
+} CreateSwapChainData;
+
+static void
+gst_d3d11_device_create_swap_chain_internal (GstD3D11Device * device,
+    CreateSwapChainData * data)
+{
+  GstD3D11DevicePrivate *priv = device->priv;
+  HRESULT hr;
+
+  hr = IDXGIFactory1_CreateSwapChain (priv->factory, (IUnknown *) priv->device,
+      (DXGI_SWAP_CHAIN_DESC *) data->desc, &data->swap_chain);
+
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
+        (guint) hr);
+    data->swap_chain = NULL;
+  }
+}
+
+/**
+ * gst_d3d11_device_create_swap_chain:
+ * @device: a #GstD3D11Device
+ * @desc: a DXGI_SWAP_CHAIN_DESC structure for swapchain
+ *
+ * Creat a IDXGISwapChain object. Caller must release returned swap chain object
+ * via IDXGISwapChain_Release()
+ *
+ * Returns: (transfer full) (nullable): a new IDXGISwapChain or %NULL
+ * when failed to create swap chain with given @desc
+ */
+IDXGISwapChain *
+gst_d3d11_device_create_swap_chain (GstD3D11Device * device,
+    const DXGI_SWAP_CHAIN_DESC * desc)
+{
+  CreateSwapChainData data = { 0, };
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  data.swap_chain = NULL;
+  data.desc = desc;
+
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      gst_d3d11_device_create_swap_chain_internal, &data);
+
+  return data.swap_chain;
+}
+
+static void
+gst_d3d11_device_release_swap_chain_internal (GstD3D11Device * device,
+    IDXGISwapChain * swap_chain)
+{
+  IDXGISwapChain_Release (swap_chain);
+}
+
+/**
+ * gst_d3d11_device_release_swap_chain:
+ * @device: a #GstD3D11Device
+ * @swap_chain: a IDXGISwapChain
+ *
+ * Release a @swap_chain from device thread
+ *
+ */
+void
+gst_d3d11_device_release_swap_chain (GstD3D11Device * device,
+    IDXGISwapChain * swap_chain)
+{
+  g_return_if_fail (GST_IS_D3D11_DEVICE (device));
+
+  gst_d3d11_device_thread_add (device,
+      (GstD3D11DeviceThreadFunc) gst_d3d11_device_release_swap_chain_internal,
+      swap_chain);
+}
+
+typedef struct
+{
+  ID3D11Texture2D *texture;
+  const D3D11_TEXTURE2D_DESC *desc;
+  const D3D11_SUBRESOURCE_DATA *inital_data;
+} CreateTextureData;
+
+static void
+gst_d3d11_device_create_texture_internal (GstD3D11Device * device,
+    CreateTextureData * data)
+{
+  GstD3D11DevicePrivate *priv = device->priv;
+  HRESULT hr;
+
+  hr = ID3D11Device_CreateTexture2D (priv->device, data->desc,
+      data->inital_data, &data->texture);
+  if (FAILED (hr)) {
+    GST_ERROR ("Failed to create staging texture (0x%x)", (guint) hr);
+    data->texture = NULL;
+  }
+}
+
+ID3D11Texture2D *
+gst_d3d11_device_create_texture (GstD3D11Device * device,
+    const D3D11_TEXTURE2D_DESC * desc,
+    const D3D11_SUBRESOURCE_DATA * inital_data)
+{
+  CreateTextureData data;
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+  g_return_val_if_fail (desc != NULL, NULL);
+
+  data.texture = NULL;
+  data.desc = desc;
+  data.inital_data = inital_data;
+
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      gst_d3d11_device_create_texture_internal, &data);
+
+  return data.texture;
+}
+
+static void
+gst_d3d11_device_release_texture_internal (GstD3D11Device * device,
+    ID3D11Texture2D * texture)
+{
+  ID3D11Texture2D_Release (texture);
+}
+
+void
+gst_d3d11_device_release_texture (GstD3D11Device * device,
+    ID3D11Texture2D * texture)
+{
+  g_return_if_fail (GST_IS_D3D11_DEVICE (device));
+  g_return_if_fail (texture != NULL);
+
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      gst_d3d11_device_release_texture_internal, texture);
+}
+
+/**
+ * gst_context_set_d3d11_device:
+ * @context: a #GstContext
+ * @device: (transfer none): resulting #GstD3D11Device
+ *
+ * Sets @device on @context
+ */
+void
+gst_context_set_d3d11_device (GstContext * context, GstD3D11Device * device)
+{
+  GstStructure *s;
+  const gchar *context_type;
+
+  g_return_if_fail (GST_IS_CONTEXT (context));
+  g_return_if_fail (GST_IS_D3D11_DEVICE (device));
+
+  context_type = gst_context_get_context_type (context);
+  if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
+    return;
+
+  GST_CAT_LOG (GST_CAT_CONTEXT,
+      "setting GstD3DDevice(%" GST_PTR_FORMAT ") on context(%" GST_PTR_FORMAT
+      ")", device, context);
+
+  s = gst_context_writable_structure (context);
+  gst_structure_set (s, "device", GST_TYPE_D3D11_DEVICE, device, NULL);
+}
+
+/**
+ * gst_context_get_d3d11_device:
+ * @context: a #GstContext
+ * @device: (out) (transfer full): resulting #GstD3D11Device
+ *
+ * Returns: Whether @device was in @context
+ */
+gboolean
+gst_context_get_d3d11_device (GstContext * context, GstD3D11Device ** device)
+{
+  const GstStructure *s;
+  const gchar *context_type;
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_CONTEXT (context), FALSE);
+  g_return_val_if_fail (device != NULL, FALSE);
+
+  context_type = gst_context_get_context_type (context);
+  if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
+    return FALSE;
+
+  s = gst_context_get_structure (context);
+  ret = gst_structure_get (s, "device", GST_TYPE_D3D11_DEVICE, device, NULL);
+
+  GST_CAT_LOG (GST_CAT_CONTEXT, "got GstD3DDevice(%p) from context(%p)",
+      *device, context);
+
+  return ret;
+}
diff --git a/sys/d3d11/gstd3d11device.h b/sys/d3d11/gstd3d11device.h
new file mode 100644 (file)
index 0000000..4344903
--- /dev/null
@@ -0,0 +1,113 @@
+/* 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_DEVICE_H__
+#define __GST_D3D11_DEVICE_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstd3d11_fwd.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_DEVICE             (gst_d3d11_device_get_type())
+#define GST_D3D11_DEVICE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_DEVICE,GstD3D11Device))
+#define GST_D3D11_DEVICE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D11_DEVICE,GstD3D11DeviceClass))
+#define GST_D3D11_DEVICE_GET_CLASS(obj)   (GST_D3D11_DEVICE_CLASS(G_OBJECT_GET_CLASS(obj)))
+#define GST_IS_D3D11_DEVICE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_DEVICE))
+#define GST_IS_D3D11_DEVICE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_DEVICE))
+#define GST_D3D11_DEVICE_CAST(obj)        ((GstD3D11Device*)(obj))
+
+#define GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE "gst.d3d11.device.handle"
+
+/**
+ * GstD3D11DeviceThreadFunc:
+ * @device: a #GstD3D11Device
+ * @data: user data
+ *
+ * Represents a function to run in the D3D11 device thread with @device and @data
+ */
+typedef void (*GstD3D11DeviceThreadFunc) (GstD3D11Device * device, gpointer data);
+
+typedef enum
+{
+  GST_D3D11_DXGI_FACTORY_UNKNOWN = 0,
+  GST_D3D11_DXGI_FACTORY_1,
+  GST_D3D11_DXGI_FACTORY_2,
+  GST_D3D11_DXGI_FACTORY_3,
+  GST_D3D11_DXGI_FACTORY_4,
+  GST_D3D11_DXGI_FACTORY_5,
+} GstD3D11DXGIFactoryVersion;
+
+
+struct _GstD3D11Device
+{
+  GstObject parent;
+
+  GstD3D11DevicePrivate *priv;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstD3D11DeviceClass
+{
+  GstObjectClass parent_class;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType                 gst_d3d11_device_get_type           (void);
+
+GstD3D11Device *      gst_d3d11_device_new                (gint adapter);
+
+ID3D11Device *        gst_d3d11_device_get_device         (GstD3D11Device * device);
+
+ID3D11DeviceContext * gst_d3d11_device_get_device_context (GstD3D11Device * device);
+
+GstD3D11DXGIFactoryVersion gst_d3d11_device_get_chosen_dxgi_factory_version (GstD3D11Device * device);
+
+IDXGISwapChain *      gst_d3d11_device_create_swap_chain  (GstD3D11Device * device,
+                                                           const DXGI_SWAP_CHAIN_DESC * desc);
+
+void                  gst_d3d11_device_release_swap_chain (GstD3D11Device * device,
+                                                           IDXGISwapChain * swap_chain);
+
+void                  gst_d3d11_device_thread_add         (GstD3D11Device * device,
+                                                           GstD3D11DeviceThreadFunc func,
+                                                           gpointer data);
+
+ID3D11Texture2D *     gst_d3d11_device_create_texture     (GstD3D11Device * device,
+                                                           const D3D11_TEXTURE2D_DESC * desc,
+                                                           const D3D11_SUBRESOURCE_DATA *inital_data);
+
+void                  gst_d3d11_device_release_texture    (GstD3D11Device * device,
+                                                           ID3D11Texture2D * texture);
+
+void                  gst_context_set_d3d11_device        (GstContext * context,
+                                                           GstD3D11Device * device);
+
+gboolean              gst_context_get_d3d11_device        (GstContext * context,
+                                                           GstD3D11Device ** device);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_DEVICE_H__ */
diff --git a/sys/d3d11/gstd3d11memory.c b/sys/d3d11/gstd3d11memory.c
new file mode 100644 (file)
index 0000000..6dcc52c
--- /dev/null
@@ -0,0 +1,405 @@
+/* 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 <string.h>
+#include "gstd3d11memory.h"
+#include "gstd3d11device.h"
+#include "gstd3d11utils.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_d3d11_allocator_debug);
+#define GST_CAT_DEFAULT gst_d3d11_allocator_debug
+
+GstD3D11AllocationParams *
+gst_d3d11_allocation_params_new (GstAllocationParams * alloc_params,
+    GstVideoInfo * info, GstVideoAlignment * align)
+{
+  GstD3D11AllocationParams *ret;
+
+  g_return_val_if_fail (alloc_params != NULL, NULL);
+  g_return_val_if_fail (info != NULL, NULL);
+
+  ret = g_new0 (GstD3D11AllocationParams, 1);
+
+  memcpy (&ret->info, info, sizeof (GstVideoInfo));
+  if (align) {
+    ret->align = *align;
+  } else {
+    gst_video_alignment_reset (&ret->align);
+  }
+
+  ret->desc.Width = GST_VIDEO_INFO_WIDTH (info);
+  ret->desc.Height = GST_VIDEO_INFO_HEIGHT (info);
+  ret->desc.MipLevels = 1;
+  ret->desc.ArraySize = 1;
+  ret->desc.Format =
+      gst_d3d11_dxgi_format_from_gst (GST_VIDEO_INFO_FORMAT (info));
+  ret->desc.SampleDesc.Count = 1;
+  ret->desc.SampleDesc.Quality = 0;
+  ret->desc.Usage = D3D11_USAGE_DEFAULT;
+  /* User must set proper BindFlags and MiscFlags manually */
+
+  return ret;
+}
+
+GstD3D11AllocationParams *
+gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src)
+{
+  GstD3D11AllocationParams *dst;
+
+  g_return_val_if_fail (src != NULL, NULL);
+
+  dst = g_new0 (GstD3D11AllocationParams, 1);
+  memcpy (dst, src, sizeof (GstD3D11AllocationParams));
+
+  return dst;
+}
+
+void
+gst_d3d11_allocation_params_free (GstD3D11AllocationParams * params)
+{
+  g_free (params);
+}
+
+G_DEFINE_BOXED_TYPE (GstD3D11AllocationParams, gst_d3d11_allocation_params,
+    (GBoxedCopyFunc) gst_d3d11_allocation_params_copy,
+    (GBoxedFreeFunc) gst_d3d11_allocation_params_free);
+
+#define gst_d3d11_allocator_parent_class parent_class
+G_DEFINE_TYPE (GstD3D11Allocator, gst_d3d11_allocator, GST_TYPE_ALLOCATOR);
+
+static inline D3D11_MAP
+_gst_map_flags_to_d3d11 (GstMapFlags flags)
+{
+  if ((flags & GST_MAP_READWRITE) == GST_MAP_READWRITE)
+    return D3D11_MAP_READ_WRITE;
+  else if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
+    return D3D11_MAP_WRITE;
+  else if ((flags & GST_MAP_READ) == GST_MAP_READ)
+    return D3D11_MAP_READ;
+  else
+    g_assert_not_reached ();
+
+  return D3D11_MAP_READ;
+}
+
+static ID3D11Texture2D *
+_create_staging_texture (GstD3D11Device * device,
+    const D3D11_TEXTURE2D_DESC * ref)
+{
+  D3D11_TEXTURE2D_DESC desc = { 0, };
+
+  desc.Width = ref->Width;
+  desc.Height = ref->Height;
+  desc.MipLevels = 1;
+  desc.Format = ref->Format;
+  desc.SampleDesc.Count = 1;
+  desc.ArraySize = 1;
+  desc.Usage = D3D11_USAGE_STAGING;
+  desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
+
+  return gst_d3d11_device_create_texture (device, &desc, NULL);
+}
+
+typedef struct
+{
+  GstD3D11Memory *mem;
+  D3D11_MAP map_flag;
+
+  gboolean ret;
+} D3D11MapData;
+
+static void
+_map_cpu_access_data (GstD3D11Device * device, gpointer data)
+{
+  D3D11MapData *map_data = (D3D11MapData *) data;
+  GstD3D11Memory *dmem = map_data->mem;
+  HRESULT hr;
+  ID3D11Resource *texture = (ID3D11Resource *) dmem->texture;
+  ID3D11Resource *staging = (ID3D11Resource *) dmem->staging;
+  ID3D11DeviceContext *device_context =
+      gst_d3d11_device_get_device_context (device);
+
+  ID3D11DeviceContext_CopySubresourceRegion (device_context,
+      staging, 0, 0, 0, 0, texture, 0, NULL);
+
+  hr = ID3D11DeviceContext_Map (device_context,
+      staging, 0, map_data->map_flag, 0, &dmem->map);
+
+  if (FAILED (hr)) {
+    GST_ERROR ("Failed to map staging texture (0x%x)", (guint) hr);
+    map_data->ret = FALSE;
+  }
+
+  map_data->ret = TRUE;
+}
+
+static gpointer
+gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
+{
+  GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
+
+  if ((flags & GST_MAP_D3D11) == GST_MAP_D3D11)
+    return dmem->texture;
+
+  if (dmem->cpu_map_count == 0) {
+    D3D11MapData map_data;
+    GstD3D11Device *device = GST_D3D11_ALLOCATOR (mem->allocator)->device;
+
+    map_data.mem = dmem;
+    map_data.map_flag = _gst_map_flags_to_d3d11 (flags);
+
+    gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+        _map_cpu_access_data, &map_data);
+
+    if (!map_data.ret)
+      return NULL;
+  }
+
+  if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
+    dmem->need_upload = TRUE;
+
+  dmem->cpu_map_count++;
+
+  return dmem->map.pData;
+}
+
+static void
+_unmap_cpu_access_data (GstD3D11Device * device, gpointer data)
+{
+  GstD3D11Memory *dmem = (GstD3D11Memory *) data;
+  ID3D11Resource *texture = (ID3D11Resource *) dmem->texture;
+  ID3D11Resource *staging = (ID3D11Resource *) dmem->staging;
+  ID3D11DeviceContext *device_context =
+      gst_d3d11_device_get_device_context (device);
+
+  ID3D11DeviceContext_Unmap (device_context, staging, 0);
+
+  if (dmem->need_upload) {
+    ID3D11DeviceContext_CopySubresourceRegion (device_context, texture,
+        0, 0, 0, 0, staging, 0, NULL);
+  }
+  dmem->need_upload = FALSE;
+}
+
+static void
+gst_d3d11_memory_unmap_full (GstMemory * mem, GstMapInfo * info)
+{
+  GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
+  GstD3D11Device *device = GST_D3D11_ALLOCATOR (mem->allocator)->device;
+
+  if ((info->flags & GST_MAP_D3D11) == GST_MAP_D3D11)
+    return;
+
+  dmem->cpu_map_count--;
+  if (dmem->cpu_map_count > 0)
+    return;
+
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      _unmap_cpu_access_data, dmem);
+}
+
+static GstMemory *
+gst_d3d11_memory_share (GstMemory * mem, gssize offset, gssize size)
+{
+  /* TODO: impl. */
+  return NULL;
+}
+
+static GstMemory *
+gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, gsize size,
+    GstAllocationParams * params)
+{
+  g_return_val_if_reached (NULL);
+}
+
+static void
+gst_d3d11_allocator_free (GstAllocator * allocator, GstMemory * mem)
+{
+  GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
+  GstD3D11Device *device = GST_D3D11_ALLOCATOR (allocator)->device;
+
+  if (dmem->texture)
+    gst_d3d11_device_release_texture (device, dmem->texture);
+
+  if (dmem->staging)
+    gst_d3d11_device_release_texture (device, dmem->staging);
+
+  g_slice_free (GstD3D11Memory, dmem);
+}
+
+static void
+gst_d3d11_allocator_dispose (GObject * object)
+{
+  GstD3D11Allocator *alloc = GST_D3D11_ALLOCATOR (object);
+
+  gst_clear_object (&alloc->device);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_d3d11_allocator_class_init (GstD3D11AllocatorClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
+
+  gobject_class->dispose = gst_d3d11_allocator_dispose;
+
+  allocator_class->alloc = gst_d3d11_allocator_dummy_alloc;
+  allocator_class->free = gst_d3d11_allocator_free;
+
+  GST_DEBUG_CATEGORY_INIT (gst_d3d11_allocator_debug, "d3d11allocator", 0,
+      "d3d11allocator object");
+}
+
+static void
+gst_d3d11_allocator_init (GstD3D11Allocator * allocator)
+{
+  GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
+
+  alloc->mem_type = GST_D3D11_MEMORY_NAME;
+  alloc->mem_map = gst_d3d11_memory_map;
+  alloc->mem_unmap_full = gst_d3d11_memory_unmap_full;
+  alloc->mem_share = gst_d3d11_memory_share;
+  /* fallback copy */
+
+  GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
+}
+
+GstD3D11Allocator *
+gst_d3d11_allocator_new (GstD3D11Device * device)
+{
+  GstD3D11Allocator *allocator;
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  allocator = g_object_new (GST_TYPE_D3D11_ALLOCATOR, NULL);
+  allocator->device = gst_object_ref (device);
+
+  return allocator;
+}
+
+typedef struct _CalculateSizeData
+{
+  ID3D11Texture2D *staging;
+  GstVideoInfo *info;
+  gsize offset[GST_VIDEO_MAX_PLANES];
+  gint stride[GST_VIDEO_MAX_PLANES];
+  gsize size;
+  gboolean ret;
+} CalculateSizeData;
+
+static void
+_calculate_buffer_size (GstD3D11Device * device, CalculateSizeData * data)
+{
+  HRESULT hr;
+  D3D11_MAPPED_SUBRESOURCE map;
+  ID3D11DeviceContext *device_context =
+      gst_d3d11_device_get_device_context (device);
+
+  hr = ID3D11DeviceContext_Map (device_context,
+      (ID3D11Resource *) data->staging, 0, GST_MAP_READWRITE, 0, &map);
+
+  if (FAILED (hr)) {
+    GST_ERROR ("Failed to map staging texture (0x%x)", (guint) hr);
+    data->ret = FALSE;
+    return;
+  }
+
+  ID3D11DeviceContext_Unmap (device_context, (ID3D11Resource *) data->staging,
+      0);
+
+  data->ret = gst_d3d11_calculate_buffer_size (data->info,
+      map.RowPitch, data->offset, data->stride, &data->size);
+}
+
+GstMemory *
+gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator,
+    GstD3D11AllocationParams * params)
+{
+  GstD3D11Memory *mem;
+  GstD3D11Device *device;
+  GstAllocationParams *alloc_params;
+  gsize size, maxsize;
+  ID3D11Texture2D *texture = NULL;
+  ID3D11Texture2D *staging = NULL;
+  CalculateSizeData data;
+  gint i;
+
+  g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL);
+  g_return_val_if_fail (params != NULL, NULL);
+
+  device = allocator->device;
+
+  texture = gst_d3d11_device_create_texture (device, &params->desc, NULL);
+  if (!texture) {
+    GST_ERROR_OBJECT (allocator, "Couldn't create texture");
+    goto error;
+  }
+
+  staging = _create_staging_texture (device, &params->desc);
+  if (!staging) {
+    GST_ERROR_OBJECT (allocator, "Couldn't create staging texture");
+    goto error;
+  }
+
+  /* try map staging texture to get actual stride and size */
+  memset (&data, 0, sizeof (CalculateSizeData));
+  data.staging = staging;
+  data.info = &params->info;
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      _calculate_buffer_size, &data);
+
+  if (!data.ret) {
+    GST_ERROR_OBJECT (allocator, "Couldn't calculate stride");
+    goto error;
+  }
+
+  alloc_params = (GstAllocationParams *) params;
+  maxsize = size = data.size;
+  maxsize += alloc_params->prefix + alloc_params->padding;
+
+  mem = g_slice_new0 (GstD3D11Memory);
+
+  gst_memory_init (GST_MEMORY_CAST (mem),
+      alloc_params->flags, GST_ALLOCATOR_CAST (allocator), NULL, maxsize,
+      alloc_params->align, 0, size);
+
+  mem->desc = params->desc;
+  mem->texture = texture;
+  mem->staging = staging;
+
+  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+    mem->offset[i] = data.offset[i];
+    mem->stride[i] = data.stride[i];
+  }
+
+  return GST_MEMORY_CAST (mem);
+
+error:
+  if (texture)
+    gst_d3d11_device_release_texture (device, texture);
+  if (staging)
+    gst_d3d11_device_release_texture (device, staging);
+  return NULL;
+}
diff --git a/sys/d3d11/gstd3d11memory.h b/sys/d3d11/gstd3d11memory.h
new file mode 100644 (file)
index 0000000..406dd1e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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_MEMORY_H__
+#define __GST_D3D11_MEMORY_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstd3d11_fwd.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_ALLOCATION_PARAMS    (gst_d3d11_allocation_params_get_type())
+#define GST_TYPE_D3D11_ALLOCATOR            (gst_d3d11_allocator_get_type())
+#define GST_D3D11_ALLOCATOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11Allocator))
+#define GST_D3D11_ALLOCATOR_CLASS(klass)    (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass))
+#define GST_IS_D3D11_ALLOCATOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_ALLOCATOR))
+#define GST_IS_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_ALLOCATOR))
+#define GST_D3D11_ALLOCATOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass))
+
+#define GST_D3D11_MEMORY_NAME "D3D11Memory"
+
+/**
+ * GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY:
+ *
+ * Name of the caps feature for indicating the use of #GstD3D11Memory
+ */
+#define GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY "memory:D3D11Memory"
+
+/**
+ * GST_MAP_D3D11:
+ *
+ * Flag indicating that we should map the D3D11 resource instead of to system memory.
+ */
+#define GST_MAP_D3D11 (GST_MAP_FLAG_LAST << 1)
+
+struct _GstD3D11Memory
+{
+  GstMemory mem;
+
+  GstMapFlags map_flags;
+  gint cpu_map_count;
+
+  ID3D11Texture2D *texture;
+  ID3D11Texture2D *staging;
+
+  D3D11_TEXTURE2D_DESC desc;
+  D3D11_MAPPED_SUBRESOURCE map;
+  gboolean need_upload;
+
+  gsize offset[GST_VIDEO_MAX_PLANES];
+  gint stride[GST_VIDEO_MAX_PLANES];
+};
+
+struct _GstD3D11AllocationParams
+{
+  GstAllocationParams parent;
+
+  D3D11_TEXTURE2D_DESC desc;
+
+  GstVideoInfo info;
+  GstVideoAlignment align;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+struct _GstD3D11Allocator
+{
+  GstAllocator parent;
+
+  /*< private >*/
+  GstD3D11Device *device;
+
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstD3D11AllocatorClass
+{
+  GstAllocatorClass allocator_class;
+
+  /*< private >*/
+  gpointer _gst_reserved[GST_PADDING];
+};
+
+GType               gst_d3d11_allocation_params_get_type (void);
+
+GstD3D11AllocationParams * gst_d3d11_allocation_params_new (GstAllocationParams * alloc_params,
+                                                            GstVideoInfo * info,
+                                                            GstVideoAlignment * align);
+
+GstD3D11AllocationParams * gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src);
+
+void                       gst_d3d11_allocation_params_free (GstD3D11AllocationParams * parms);
+
+GType               gst_d3d11_allocator_get_type  (void);
+
+GstD3D11Allocator * gst_d3d11_allocator_new       (GstD3D11Device *device);
+
+GstMemory *         gst_d3d11_allocator_alloc     (GstD3D11Allocator * allocator,
+                                                   GstD3D11AllocationParams * params);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_MEMORY_H__ */
diff --git a/sys/d3d11/gstd3d11utils.c b/sys/d3d11/gstd3d11utils.c
new file mode 100644 (file)
index 0000000..0c8d49a
--- /dev/null
@@ -0,0 +1,429 @@
+/* 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 "gstd3d11utils.h"
+#include "gstd3d11device.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_d3d11_utils_debug);
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
+
+static GstDebugCategory *
+_init_d3d11_utils_debug (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_INIT (gst_d3d11_utils_debug, "d3d11utils", 0,
+        "Direct3D11 Utilities");
+    g_once_init_leave (&_init, 1);
+  }
+
+  return gst_d3d11_utils_debug;
+}
+
+static void
+_init_context_debug (void)
+{
+  static volatile gsize _init = 0;
+
+  if (g_once_init_enter (&_init)) {
+    GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
+    g_once_init_leave (&_init, 1);
+  }
+}
+
+#define GST_CAT_DEFAULT _init_d3d11_utils_debug()
+
+static const struct
+{
+  GstVideoFormat gst_format;
+  DXGI_FORMAT dxgi_format;
+} gst_dxgi_format_map[] = {
+  /* TODO: add more formats */
+  {
+  GST_VIDEO_FORMAT_BGRA, DXGI_FORMAT_B8G8R8A8_UNORM}, {
+  GST_VIDEO_FORMAT_RGBA, DXGI_FORMAT_R8G8B8A8_UNORM}, {
+  GST_VIDEO_FORMAT_RGB10A2_LE, DXGI_FORMAT_R10G10B10A2_UNORM}
+};
+
+GstVideoFormat
+gst_d3d11_dxgi_format_to_gst (DXGI_FORMAT format)
+{
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
+    if (gst_dxgi_format_map[i].dxgi_format == format)
+      return gst_dxgi_format_map[i].gst_format;
+  }
+
+  return GST_VIDEO_FORMAT_UNKNOWN;
+}
+
+DXGI_FORMAT
+gst_d3d11_dxgi_format_from_gst (GstVideoFormat format)
+{
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
+    if (gst_dxgi_format_map[i].gst_format == format)
+      return gst_dxgi_format_map[i].dxgi_format;
+  }
+
+  return DXGI_FORMAT_UNKNOWN;
+}
+
+typedef struct
+{
+  GstCaps *caps;
+  D3D11_FORMAT_SUPPORT flags;
+} SupportCapsData;
+
+static void
+gst_d3d11_device_get_supported_caps_internal (GstD3D11Device * device,
+    SupportCapsData * data)
+{
+  ID3D11Device *d3d11_device;
+  HRESULT hr;
+  gint i;
+  GValue v_list = G_VALUE_INIT;
+  GstCaps *supported_caps;
+
+  d3d11_device = gst_d3d11_device_get_device (device);
+  g_value_init (&v_list, GST_TYPE_LIST);
+
+  for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
+    UINT format_support = 0;
+    GstVideoFormat format = gst_dxgi_format_map[i].gst_format;
+
+    hr = ID3D11Device_CheckFormatSupport (d3d11_device,
+        gst_dxgi_format_map[i].dxgi_format, &format_support);
+
+    if (SUCCEEDED (hr) && ((format_support & data->flags) == data->flags)) {
+      GValue v_str = G_VALUE_INIT;
+      g_value_init (&v_str, G_TYPE_STRING);
+
+      GST_LOG_OBJECT (device, "d3d11 device can support %s with flags 0x%x",
+          gst_video_format_to_string (format), data->flags);
+      g_value_set_string (&v_str, gst_video_format_to_string (format));
+      gst_value_list_append_and_take_value (&v_list, &v_str);
+    }
+  }
+
+  supported_caps = gst_caps_new_simple ("video/x-raw",
+      "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
+  gst_caps_set_value (supported_caps, "format", &v_list);
+  g_value_unset (&v_list);
+
+  data->caps = supported_caps;
+}
+
+/**
+ * gst_d3d11_device_get_supported_caps:
+ * @device: a #GstD3DDevice
+ * @flags: D3D11_FORMAT_SUPPORT flags
+ *
+ * Check supported format with given flags
+ *
+ * Returns: a #GstCaps representing supported format
+ */
+GstCaps *
+gst_d3d11_device_get_supported_caps (GstD3D11Device * device,
+    D3D11_FORMAT_SUPPORT flags)
+{
+  SupportCapsData data;
+
+  g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
+
+  data.caps = NULL;
+  data.flags = flags;
+
+  gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
+      gst_d3d11_device_get_supported_caps_internal, &data);
+
+  return data.caps;
+}
+
+gboolean
+gst_d3d11_calculate_buffer_size (GstVideoInfo * info, guint pitch,
+    gsize offset[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES],
+    gsize * size)
+{
+  g_return_val_if_fail (info != NULL, FALSE);
+
+  switch (GST_VIDEO_INFO_FORMAT (info)) {
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_RGB10A2_LE:
+      offset[0] = 0;
+      stride[0] = pitch;
+      *size = pitch * GST_VIDEO_INFO_HEIGHT (info);
+      break;
+    case GST_VIDEO_FORMAT_NV12:
+      offset[0] = 0;
+      stride[0] = pitch;
+      offset[1] = offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
+      stride[1] = pitch;
+      *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
+      break;
+    default:
+      return FALSE;
+  }
+
+  GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
+      " (%s %dx%d, Pitch %d)", *size,
+      gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)),
+      GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), pitch);
+
+  return TRUE;
+}
+
+/**
+ * gst_d3d11_handle_set_context:
+ * @element: a #GstElement
+ * @context: a #GstContext
+ * @device: (inout) (transfer full): location of a #GstD3DDevice
+ *
+ * Helper function for implementing #GstElementClass.set_context() in
+ * D3D11 capable elements.
+ *
+ * Retrieve's the #GstD3D11Device in @context and places the result in @device.
+ *
+ * Returns: whether the @device could be set successfully
+ */
+gboolean
+gst_d3d11_handle_set_context (GstElement * element, GstContext * context,
+    GstD3D11Device ** device)
+{
+  GstD3D11Device *device_replacement = NULL;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (device != NULL, FALSE);
+
+  if (!context)
+    return FALSE;
+
+  if (!gst_context_get_d3d11_device (context, &device_replacement))
+    return FALSE;
+
+  if (*device)
+    gst_object_unref (*device);
+  *device = device_replacement;
+
+  return TRUE;
+}
+
+/**
+ * gst_d3d11_handle_context_query:
+ * @element: a #GstElement
+ * @query: a #GstQuery of type %GST_QUERY_CONTEXT
+ * @device: (transfer none) (nullable): a #GstD3D11Device
+ *
+ * Returns: Whether the @query was successfully responded to from the passed
+ *          @device.
+ */
+gboolean
+gst_d3d11_handle_context_query (GstElement * element, GstQuery * query,
+    GstD3D11Device * device)
+{
+  const gchar *context_type;
+  GstContext *context, *old_context;
+
+  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+  g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
+
+  GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
+
+  if (!device)
+    return FALSE;
+
+  gst_query_parse_context_type (query, &context_type);
+  if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
+    return FALSE;
+
+  gst_query_parse_context (query, &old_context);
+  if (old_context)
+    context = gst_context_copy (old_context);
+  else
+    context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
+
+  gst_context_set_d3d11_device (context, device);
+  gst_query_set_context (query, context);
+  gst_context_unref (context);
+
+  GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
+      " on %" GST_PTR_FORMAT, device, query);
+
+  return TRUE;
+}
+
+static gboolean
+pad_query (const GValue * item, GValue * value, gpointer user_data)
+{
+  GstPad *pad = g_value_get_object (item);
+  GstQuery *query = user_data;
+  gboolean res;
+
+  res = gst_pad_peer_query (pad, query);
+
+  if (res) {
+    g_value_set_boolean (value, TRUE);
+    return FALSE;
+  }
+
+  GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
+  return TRUE;
+}
+
+static gboolean
+run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
+{
+  GstIterator *it;
+  GstIteratorFoldFunction func = pad_query;
+  GValue res = { 0 };
+
+  g_value_init (&res, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&res, FALSE);
+
+  /* Ask neighbor */
+  if (direction == GST_PAD_SRC)
+    it = gst_element_iterate_src_pads (element);
+  else
+    it = gst_element_iterate_sink_pads (element);
+
+  while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
+    gst_iterator_resync (it);
+
+  gst_iterator_free (it);
+
+  return g_value_get_boolean (&res);
+}
+
+static void
+run_d3d11_context_query (GstElement * element)
+{
+  GstQuery *query;
+  GstContext *ctxt;
+
+  /*  2a) Query downstream with GST_QUERY_CONTEXT for the context and
+   *      check if downstream already has a context of the specific type
+   *  2b) Query upstream as above.
+   */
+  query = gst_query_new_context (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
+  if (run_query (element, query, GST_PAD_SRC)) {
+    gst_query_parse_context (query, &ctxt);
+    GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
+        "found context (%" GST_PTR_FORMAT ") in downstream query", ctxt);
+    gst_element_set_context (element, ctxt);
+  } else if (run_query (element, query, GST_PAD_SINK)) {
+    gst_query_parse_context (query, &ctxt);
+    GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
+        "found context (%" GST_PTR_FORMAT ") in upstream query", ctxt);
+    gst_element_set_context (element, ctxt);
+  } else {
+    /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
+     *    the required context type and afterwards check if a
+     *    usable context was set now as in 1). The message could
+     *    be handled by the parent bins of the element and the
+     *    application.
+     */
+    GstMessage *msg;
+
+    GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
+        "posting need context message");
+    msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
+        GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
+    gst_element_post_message (element, msg);
+  }
+
+  /*
+   * Whomever responds to the need-context message performs a
+   * GstElement::set_context() with the required context in which the element
+   * is required to update the display_ptr or call gst_gl_handle_set_context().
+   */
+
+  gst_query_unref (query);
+}
+
+/**
+ * gst_d3d11_ensure_element_data:
+ * @element: the #GstElement running the query
+ * @device: (inout): the resulting #GstD3D11Device
+ * @preferred_adapter: the index of preferred adapter
+ *
+ * Perform the steps necessary for retrieving a #GstD3D11Device
+ * from the surrounding elements or from the application using the #GstContext mechanism.
+ *
+ * If the contents of @device is not %NULL, then no #GstContext query is
+ * necessary for #GstD3D11Device retrieval is performed.
+ *
+ * Returns: whether a #GstD3D11Device exists in @device
+ */
+gboolean
+gst_d3d11_ensure_element_data (GstElement * element, GstD3D11Device ** device,
+    gint preferred_adapter)
+{
+  GstD3D11Device *new_device;
+  GstContext *context;
+
+  g_return_val_if_fail (element != NULL, FALSE);
+  g_return_val_if_fail (device != NULL, FALSE);
+  _init_context_debug ();
+
+  if (*device) {
+    GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device);
+    return TRUE;
+  }
+
+  run_d3d11_context_query (element);
+
+  /* Neighbour found and it updated the devicey */
+  if (*device) {
+    return TRUE;
+  }
+
+  new_device = gst_d3d11_device_new (preferred_adapter);
+
+  if (!new_device) {
+    GST_ERROR_OBJECT (element,
+        "Couldn't create new device with adapter index %d", preferred_adapter);
+    return FALSE;
+  }
+
+  *device = new_device;
+
+  context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
+  gst_context_set_d3d11_device (context, new_device);
+
+  gst_element_set_context (element, context);
+
+  GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
+      "posting have context (%" GST_PTR_FORMAT
+      ") message with device (%" GST_PTR_FORMAT ")", context, device);
+
+  gst_element_post_message (GST_ELEMENT_CAST (element),
+      gst_message_new_have_context (GST_OBJECT_CAST (element), context));
+
+  return TRUE;
+}
diff --git a/sys/d3d11/gstd3d11utils.h b/sys/d3d11/gstd3d11utils.h
new file mode 100644 (file)
index 0000000..2fcd0f3
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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_UTILS_H__
+#define __GST_D3D11_UTILS_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstd3d11_fwd.h"
+
+G_BEGIN_DECLS
+
+GstVideoFormat  gst_d3d11_dxgi_format_to_gst        (DXGI_FORMAT format);
+
+DXGI_FORMAT     gst_d3d11_dxgi_format_from_gst      (GstVideoFormat format);
+
+GstCaps *       gst_d3d11_device_get_supported_caps (GstD3D11Device * device,
+                                                     D3D11_FORMAT_SUPPORT flags);
+
+gboolean        gst_d3d11_calculate_buffer_size     (GstVideoInfo * info,
+                                                     guint pitch,
+                                                     gsize offset[GST_VIDEO_MAX_PLANES],
+                                                     gint stride[GST_VIDEO_MAX_PLANES],
+                                                     gsize *size);
+
+gboolean        gst_d3d11_handle_set_context        (GstElement * element,
+                                                     GstContext * context,
+                                                     GstD3D11Device ** device);
+
+gboolean        gst_d3d11_handle_context_query      (GstElement * element,
+                                                     GstQuery * query,
+                                                     GstD3D11Device * device);
+
+gboolean        gst_d3d11_ensure_element_data       (GstElement * element,
+                                                     GstD3D11Device ** device,
+                                                     gint preferred_adapter);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_UTILS_H__ */
diff --git a/sys/d3d11/gstd3d11videosink.c b/sys/d3d11/gstd3d11videosink.c
new file mode 100644 (file)
index 0000000..976c2fd
--- /dev/null
@@ -0,0 +1,815 @@
+/* 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 "gstd3d11videosink.h"
+#include "gstd3d11memory.h"
+#include "gstd3d11utils.h"
+#include "gstd3d11device.h"
+#include "gstd3d11bufferpool.h"
+
+enum
+{
+  PROP_0,
+  PROP_ADAPTER,
+  PROP_FORCE_ASPECT_RATIO,
+  PROP_ENABLE_NAVIGATION_EVENTS
+};
+
+#define DEFAULT_ADAPTER                   -1
+#define DEFAULT_FORCE_ASPECT_RATIO        TRUE
+#define DEFAULT_ENABLE_NAVIGATION_EVENTS  TRUE
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw, "
+        "format = (string) { BGRA, RGBA, RGB10A2_LE }, "
+        "framerate = (fraction) [ 0, MAX ], "
+        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
+    );
+
+GST_DEBUG_CATEGORY (d3d11_video_sink_debug);
+#define GST_CAT_DEFAULT d3d11_video_sink_debug
+
+static void gst_d3d11_videosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_d3d11_videosink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void
+gst_d3d11_video_sink_video_overlay_init (GstVideoOverlayInterface * iface);
+static void
+gst_d3d11_video_sink_navigation_init (GstNavigationInterface * iface);
+
+static void gst_d3d11_video_sink_set_context (GstElement * element,
+    GstContext * context);
+static GstCaps *gst_d3d11_video_sink_get_caps (GstBaseSink * sink,
+    GstCaps * filter);
+static gboolean gst_d3d11_video_sink_set_caps (GstBaseSink * sink,
+    GstCaps * caps);
+
+static gboolean gst_d3d11_video_sink_start (GstBaseSink * sink);
+static gboolean gst_d3d11_video_sink_stop (GstBaseSink * sink);
+static gboolean gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink,
+    GstQuery * query);
+
+static GstFlowReturn
+gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf);
+
+#define gst_d3d11_video_sink_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstD3D11VideoSink, gst_d3d11_video_sink,
+    GST_TYPE_VIDEO_SINK,
+    G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
+        gst_d3d11_video_sink_video_overlay_init);
+    G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
+        gst_d3d11_video_sink_navigation_init);
+    GST_DEBUG_CATEGORY_INIT (d3d11_video_sink_debug,
+        "d3d11videosink", 0, "Direct3D11 Video Sink"));
+
+static void
+gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
+  GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass);
+
+  gobject_class->set_property = gst_d3d11_videosink_set_property;
+  gobject_class->get_property = gst_d3d11_videosink_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_ADAPTER,
+      g_param_spec_int ("adapter", "Adapter",
+          "Adapter index for creating device (-1 for default)",
+          -1, G_MAXINT32, DEFAULT_ADAPTER,
+          G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
+          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, navigation events are sent upstream",
+          DEFAULT_ENABLE_NAVIGATION_EVENTS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  element_class->set_context =
+      GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context);
+
+  gst_element_class_set_static_metadata (element_class,
+      "Direct3D11 video sink", "Sink/Video",
+      "A Direct3D11 based videosink",
+      "Seungha Yang <seungha.yang@navercorp.com>");
+
+  gst_element_class_add_static_pad_template (element_class, &sink_template);
+
+  basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_get_caps);
+  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_caps);
+  basesink_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_start);
+  basesink_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_stop);
+  basesink_class->propose_allocation =
+      GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_propose_allocation);
+
+  videosink_class->show_frame =
+      GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_show_frame);
+}
+
+static void
+gst_d3d11_video_sink_init (GstD3D11VideoSink * self)
+{
+  self->adapter = DEFAULT_ADAPTER;
+  self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+  self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
+}
+
+static void
+gst_d3d11_videosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (object);
+
+  GST_OBJECT_LOCK (self);
+  switch (prop_id) {
+    case PROP_ADAPTER:
+      self->adapter = g_value_get_int (value);
+      break;
+    case PROP_FORCE_ASPECT_RATIO:
+      self->force_aspect_ratio = g_value_get_boolean (value);
+      if (self->window)
+        g_object_set (self->window,
+            "force-aspect-ratio", self->force_aspect_ratio, NULL);
+      break;
+    case PROP_ENABLE_NAVIGATION_EVENTS:
+      self->enable_navigation_events = g_value_get_boolean (value);
+      if (self->window) {
+        g_object_set (self->window,
+            "enable-navigation-events", self->enable_navigation_events, NULL);
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  GST_OBJECT_UNLOCK (self);
+}
+
+static void
+gst_d3d11_videosink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (object);
+
+  switch (prop_id) {
+    case PROP_ADAPTER:
+      g_value_set_int (value, self->adapter);
+      break;
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, self->force_aspect_ratio);
+      break;
+    case PROP_ENABLE_NAVIGATION_EVENTS:
+      g_value_set_boolean (value, self->enable_navigation_events);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_d3d11_video_sink_set_context (GstElement * element, GstContext * context)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (element);
+
+  gst_d3d11_handle_set_context (element, context, &self->device);
+
+  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
+}
+
+static GstCaps *
+gst_d3d11_video_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
+  GstCaps *caps = NULL;
+
+  if (self->device)
+    caps = gst_d3d11_device_get_supported_caps (self->device,
+        D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY |
+        D3D11_FORMAT_SUPPORT_RENDER_TARGET);
+
+  if (!caps)
+    caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
+
+  if (caps && filter) {
+    GstCaps *isect;
+    isect = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (caps);
+    caps = isect;
+  }
+
+  return caps;
+}
+
+static gboolean
+gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
+  GstCaps *sink_caps = NULL;
+  gint video_width, video_height;
+  gint video_par_n, video_par_d;        /* video's PAR */
+  gint display_par_n = 1, display_par_d = 1;    /* display's PAR */
+  guint num, den;
+  D3D11_TEXTURE2D_DESC desc = { 0, };
+  ID3D11Texture2D *staging;
+
+  GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
+
+  sink_caps = gst_d3d11_device_get_supported_caps (self->device,
+      D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY |
+      D3D11_FORMAT_SUPPORT_RENDER_TARGET);
+
+  GST_DEBUG_OBJECT (self, "supported caps %" GST_PTR_FORMAT, sink_caps);
+
+  if (!gst_caps_can_intersect (sink_caps, caps))
+    goto incompatible_caps;
+
+  gst_clear_caps (&sink_caps);
+
+  if (!gst_video_info_from_caps (&self->info, caps))
+    goto invalid_format;
+
+  video_width = GST_VIDEO_INFO_WIDTH (&self->info);
+  video_height = GST_VIDEO_INFO_HEIGHT (&self->info);
+  video_par_n = GST_VIDEO_INFO_PAR_N (&self->info);
+  video_par_d = GST_VIDEO_INFO_PAR_D (&self->info);
+
+  /* get aspect ratio from caps if it's present, and
+   * convert video width and height to a display width and height
+   * using wd / hd = wv / hv * PARv / PARd */
+
+  /* TODO: Get display PAR */
+
+  if (!gst_video_calculate_display_ratio (&num, &den, video_width,
+          video_height, video_par_n, video_par_d, display_par_n, display_par_d))
+    goto no_disp_ratio;
+
+  GST_DEBUG_OBJECT (sink,
+      "video width/height: %dx%d, calculated display ratio: %d/%d format: %s",
+      video_width, video_height, num, den,
+      gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&self->info)));
+
+  /* now find a width x height that respects this display ratio.
+   * prefer those that have one of w/h the same as the incoming video
+   * using wd / hd = num / den
+   */
+
+  /* start with same height, because of interlaced video
+   * check hd / den is an integer scale factor, and scale wd with the PAR
+   */
+  if (video_height % den == 0) {
+    GST_DEBUG_OBJECT (self, "keeping video height");
+    GST_VIDEO_SINK_WIDTH (self) = (guint)
+        gst_util_uint64_scale_int (video_height, num, den);
+    GST_VIDEO_SINK_HEIGHT (self) = video_height;
+  } else if (video_width % num == 0) {
+    GST_DEBUG_OBJECT (self, "keeping video width");
+    GST_VIDEO_SINK_WIDTH (self) = video_width;
+    GST_VIDEO_SINK_HEIGHT (self) = (guint)
+        gst_util_uint64_scale_int (video_width, den, num);
+  } else {
+    GST_DEBUG_OBJECT (self, "approximating while keeping video height");
+    GST_VIDEO_SINK_WIDTH (self) = (guint)
+        gst_util_uint64_scale_int (video_height, num, den);
+    GST_VIDEO_SINK_HEIGHT (self) = video_height;
+  }
+
+  GST_DEBUG_OBJECT (self, "scaling to %dx%d",
+      GST_VIDEO_SINK_WIDTH (self), GST_VIDEO_SINK_HEIGHT (self));
+
+  if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
+    goto no_display_size;
+
+  self->dxgi_format =
+      gst_d3d11_dxgi_format_from_gst (GST_VIDEO_INFO_FORMAT (&self->info));
+
+  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;
+    self->render_rect.y = 0;
+    self->render_rect.w = GST_VIDEO_SINK_WIDTH (self);
+    self->render_rect.h = GST_VIDEO_SINK_HEIGHT (self);
+  }
+
+  gst_d3d11_window_set_render_rectangle (self->window,
+      self->render_rect.x, self->render_rect.y, self->render_rect.w,
+      self->render_rect.h);
+  self->pending_render_rect = FALSE;
+
+  if (!self->force_aspect_ratio) {
+    g_object_set (self->window,
+        "force-aspect-ratio", self->force_aspect_ratio, NULL);
+  }
+
+  GST_OBJECT_UNLOCK (self);
+
+  if (!gst_d3d11_window_prepare (self->window, GST_VIDEO_SINK_WIDTH (self),
+          GST_VIDEO_SINK_HEIGHT (self), self->dxgi_format, caps)) {
+    GST_ERROR_OBJECT (self, "cannot create swapchain");
+    return FALSE;
+  }
+
+  if (self->fallback_staging) {
+    gst_d3d11_device_release_texture (self->device, self->fallback_staging);
+    self->fallback_staging = NULL;
+  }
+
+  desc.Width = GST_VIDEO_SINK_WIDTH (self);
+  desc.Height = GST_VIDEO_SINK_HEIGHT (self);
+  desc.MipLevels = 1;
+  desc.Format = self->dxgi_format;
+  desc.SampleDesc.Count = 1;
+  desc.ArraySize = 1;
+  desc.Usage = D3D11_USAGE_STAGING;
+  desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
+
+  staging = gst_d3d11_device_create_texture (self->device, &desc, NULL);
+  if (!staging) {
+    GST_ERROR_OBJECT (self, "cannot create fallback staging texture");
+    return FALSE;
+  }
+
+  self->fallback_staging = staging;
+
+  return TRUE;
+
+  /* ERRORS */
+incompatible_caps:
+  {
+    GST_ERROR_OBJECT (sink, "caps incompatible");
+    gst_clear_caps (&sink_caps);
+    return FALSE;
+  }
+invalid_format:
+  {
+    GST_DEBUG_OBJECT (sink,
+        "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
+    return FALSE;
+  }
+no_disp_ratio:
+  {
+    GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
+        ("Error calculating the output display ratio of the video."));
+    return FALSE;
+  }
+no_display_size:
+  {
+    GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
+        ("Error calculating the output display ratio of the video."));
+    return FALSE;
+  }
+}
+
+static void
+gst_d3d11_video_sink_key_event (GstD3D11Window * window, const gchar * event,
+    const gchar * key, GstD3D11VideoSink * self)
+{
+  if (self->enable_navigation_events) {
+    GST_LOG_OBJECT (self, "send key event %s, key %s", event, key);
+    gst_navigation_send_key_event (GST_NAVIGATION (self), event, key);
+  }
+}
+
+static void
+gst_d3d11_video_mouse_key_event (GstD3D11Window * window, const gchar * event,
+    gint button, gdouble x, gdouble y, GstD3D11VideoSink * self)
+{
+  if (self->enable_navigation_events) {
+    GST_LOG_OBJECT (self,
+        "send mouse event %s, button %d (%.1f, %.1f)", event, button, x, y);
+    gst_navigation_send_mouse_event (GST_NAVIGATION (self), event, button, x,
+        y);
+  }
+}
+
+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);
+
+  GST_DEBUG_OBJECT (self, "Start");
+
+  if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), &self->device,
+          self->adapter) || !self->device) {
+    GST_ERROR_OBJECT (sink, "Cannot create d3d11device");
+    return FALSE;
+  }
+
+  self->window = gst_d3d11_window_new (self->device);
+  if (!self->window) {
+    GST_ERROR_OBJECT (sink, "Cannot create d3d11window");
+    return FALSE;
+  }
+
+  g_object_set (self->window,
+      "enable-navigation-events", self->enable_navigation_events, NULL);
+
+  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);
+
+  return TRUE;
+}
+
+static gboolean
+gst_d3d11_video_sink_stop (GstBaseSink * sink)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
+
+  GST_DEBUG_OBJECT (self, "Stop");
+
+  if (self->fallback_staging) {
+    ID3D11Texture2D_Release (self->fallback_staging);
+    self->fallback_staging = NULL;
+  }
+
+  gst_clear_object (&self->device);
+  gst_clear_object (&self->window);
+
+  return TRUE;
+}
+
+static gboolean
+gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
+  GstStructure *config;
+  GstCaps *caps;
+  GstBufferPool *pool = NULL;
+  GstVideoInfo info;
+  guint size;
+  gboolean need_pool;
+
+  if (!self->device || !self->window)
+    return FALSE;
+
+  gst_query_parse_allocation (query, &caps, &need_pool);
+
+  if (caps == NULL)
+    goto no_caps;
+
+  if (!gst_video_info_from_caps (&info, caps))
+    goto invalid_caps;
+
+  /* the normal size of a frame */
+  size = info.size;
+
+  if (need_pool) {
+    GST_DEBUG_OBJECT (self, "create new pool");
+
+    pool = (GstBufferPool *) gst_d3d11_buffer_pool_new (self->device);
+    config = gst_buffer_pool_get_config (pool);
+    gst_buffer_pool_config_set_params (config, caps, size, 2,
+        DXGI_MAX_SWAP_CHAIN_BUFFERS);
+
+    if (!gst_buffer_pool_set_config (pool, config)) {
+      g_object_unref (pool);
+      goto config_failed;
+    }
+  }
+
+  gst_query_add_allocation_pool (query, pool, size, 2,
+      DXGI_MAX_SWAP_CHAIN_BUFFERS);
+  if (pool)
+    g_object_unref (pool);
+
+  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
+  gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
+
+  return TRUE;
+
+  /* ERRORS */
+no_caps:
+  {
+    GST_WARNING_OBJECT (self, "no caps specified");
+    return FALSE;
+  }
+invalid_caps:
+  {
+    GST_WARNING_OBJECT (self, "invalid caps specified");
+    return FALSE;
+  }
+config_failed:
+  {
+    GST_WARNING_OBJECT (self, "failed setting config");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+typedef struct
+{
+  GstD3D11VideoSink *sink;
+
+  GstVideoFrame *frame;
+  ID3D11Resource *resource;
+
+  GstFlowReturn ret;
+} FrameUploadData;
+
+static void
+_upload_frame (GstD3D11Device * device, gpointer data)
+{
+  GstD3D11VideoSink *self;
+  HRESULT hr;
+  ID3D11DeviceContext *device_context;
+  FrameUploadData *upload_data = (FrameUploadData *) data;
+  D3D11_MAPPED_SUBRESOURCE d3d11_map;
+  guint i;
+  guint8 *dst;
+
+  self = upload_data->sink;
+
+  device_context = gst_d3d11_device_get_device_context (device);
+
+  hr = ID3D11DeviceContext_Map (device_context,
+      upload_data->resource, 0, D3D11_MAP_WRITE, 0, &d3d11_map);
+
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (self, "cannot map d3d11 staging texture");
+    upload_data->ret = GST_FLOW_ERROR;
+    return;
+  }
+
+  dst = d3d11_map.pData;
+  for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (upload_data->frame); i++) {
+    guint w, h;
+    guint j;
+    guint8 *src;
+    gint src_stride;
+
+    w = GST_VIDEO_FRAME_COMP_WIDTH (upload_data->frame, i) *
+        GST_VIDEO_FRAME_COMP_PSTRIDE (upload_data->frame, i);
+    h = GST_VIDEO_FRAME_COMP_HEIGHT (upload_data->frame, i);
+    src = GST_VIDEO_FRAME_PLANE_DATA (upload_data->frame, i);
+    src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (upload_data->frame, i);
+
+    for (j = 0; j < h; j++) {
+      memcpy (dst, src, w);
+      dst += d3d11_map.RowPitch;
+      src += src_stride;
+    }
+  }
+
+  ID3D11DeviceContext_Unmap (device_context, upload_data->resource, 0);
+}
+
+static GstFlowReturn
+gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
+  GstVideoFrame frame;
+  FrameUploadData data;
+  ID3D11Texture2D *texture;
+  GstMapInfo map;
+  GstFlowReturn ret;
+  gboolean need_unmap = FALSE;
+  GstMemory *mem;
+  GstVideoRectangle rect = { 0, };
+  GstVideoCropMeta *crop;
+
+  if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
+      && gst_memory_is_type (mem, GST_D3D11_MEMORY_NAME)) {
+    /* If this buffer has been allocated using our buffer management we simply
+       put the ximage which is in the PRIVATE pointer */
+    GST_TRACE_OBJECT (self, "buffer %p from our pool, writing directly", buf);
+    if (!gst_memory_map (mem, &map, (GST_MAP_READ | GST_MAP_D3D11))) {
+      GST_ERROR_OBJECT (self, "cannot map d3d11 memory");
+      return GST_FLOW_ERROR;
+    }
+
+    texture = (ID3D11Texture2D *) map.data;
+    need_unmap = TRUE;
+  } else {
+    if (!gst_video_frame_map (&frame, &self->info, buf, GST_MAP_READ)) {
+      GST_ERROR_OBJECT (self, "cannot map video frame");
+      return GST_FLOW_ERROR;
+    }
+
+    GST_TRACE_OBJECT (self,
+        "buffer %p out of our pool, write to stage buffer", buf);
+
+    data.sink = self;
+    data.frame = &frame;
+    data.resource = (ID3D11Resource *) self->fallback_staging;
+    data.ret = GST_FLOW_OK;
+
+    gst_d3d11_device_thread_add (self->device, (GstD3D11DeviceThreadFunc)
+        _upload_frame, &data);
+
+    if (data.ret != GST_FLOW_OK)
+      return data.ret;
+
+    gst_video_frame_unmap (&frame);
+
+    texture = self->fallback_staging;
+  }
+
+  gst_d3d11_window_show (self->window);
+
+  crop = gst_buffer_get_video_crop_meta (buf);
+  if (crop) {
+    rect.x = crop->x;
+    rect.y = crop->y;
+    rect.w = crop->width;
+    rect.h = crop->height;
+  } else {
+    rect.w = GST_VIDEO_SINK_WIDTH (self);
+    rect.h = GST_VIDEO_SINK_HEIGHT (self);
+  }
+
+  ret = gst_d3d11_window_render (self->window, texture, &rect);
+
+  if (need_unmap)
+    gst_memory_unmap (mem, &map);
+
+  if (ret == GST_D3D11_WINDOW_FLOW_CLOSED) {
+    GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
+        ("Output window was closed"), (NULL));
+
+    ret = GST_FLOW_ERROR;
+  }
+
+  return ret;
+}
+
+/* VideoOverlay interface */
+static void
+gst_d3d11_video_sink_set_window_handle (GstVideoOverlay * overlay,
+    guintptr window_id)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
+
+  GST_DEBUG ("set window handle %" G_GUINTPTR_FORMAT, window_id);
+
+  self->window_id = window_id;
+}
+
+static void
+gst_d3d11_video_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
+    gint y, gint width, gint height)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
+
+  GST_DEBUG_OBJECT (self,
+      "render rect x: %d, y: %d, width: %d, height %d", x, y, width, height);
+
+  GST_OBJECT_LOCK (self);
+  if (self->window) {
+    gst_d3d11_window_set_render_rectangle (self->window, x, y, width, height);
+  } else {
+    self->render_rect.x = x;
+    self->render_rect.y = y;
+    self->render_rect.w = width;
+    self->render_rect.h = height;
+    self->pending_render_rect = TRUE;
+  }
+  GST_OBJECT_UNLOCK (self);
+}
+
+static void
+gst_d3d11_video_sink_expose (GstVideoOverlay * overlay)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
+
+  if (self->window && self->window->swap_chain) {
+    GstVideoRectangle rect = { 0, };
+    rect.w = GST_VIDEO_SINK_WIDTH (self);
+    rect.h = GST_VIDEO_SINK_HEIGHT (self);
+
+    gst_d3d11_window_render (self->window, NULL, &rect);
+  }
+}
+
+static void
+gst_d3d11_video_sink_video_overlay_init (GstVideoOverlayInterface * iface)
+{
+  iface->set_window_handle = gst_d3d11_video_sink_set_window_handle;
+  iface->set_render_rectangle = gst_d3d11_video_sink_set_render_rectangle;
+  iface->expose = gst_d3d11_video_sink_expose;
+}
+
+/* Navigation interface */
+static void
+gst_d3d11_video_sink_navigation_send_event (GstNavigation * navigation,
+    GstStructure * structure)
+{
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (navigation);
+  gboolean handled = FALSE;
+  GstEvent *event = NULL;
+  GstVideoRectangle src = { 0, };
+  GstVideoRectangle dst = { 0, };
+  GstVideoRectangle result;
+  gdouble x, y, xscale = 1.0, yscale = 1.0;
+
+  if (!self->window) {
+    gst_structure_free (structure);
+    return;
+  }
+
+  if (self->force_aspect_ratio) {
+    /* We get the frame position using the calculated geometry from _setcaps
+       that respect pixel aspect ratios */
+    src.w = GST_VIDEO_SINK_WIDTH (self);
+    src.h = GST_VIDEO_SINK_HEIGHT (self);
+    dst.w = self->render_rect.w;
+    dst.h = self->render_rect.h;
+
+    gst_video_sink_center_rect (src, dst, &result, TRUE);
+    result.x += self->render_rect.x;
+    result.y += self->render_rect.y;
+  } else {
+    memcpy (&result, &self->render_rect, sizeof (GstVideoRectangle));
+  }
+
+  xscale = (gdouble) GST_VIDEO_INFO_WIDTH (&self->info) / result.w;
+  yscale = (gdouble) GST_VIDEO_INFO_HEIGHT (&self->info) / result.h;
+
+  /* Converting pointer coordinates to the non scaled geometry */
+  if (gst_structure_get_double (structure, "pointer_x", &x)) {
+    x = MIN (x, result.x + result.w);
+    x = MAX (x - result.x, 0);
+    gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
+        (gdouble) x * xscale, NULL);
+  }
+  if (gst_structure_get_double (structure, "pointer_y", &y)) {
+    y = MIN (y, result.y + result.h);
+    y = MAX (y - result.y, 0);
+    gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
+        (gdouble) y * yscale, NULL);
+  }
+
+  event = gst_event_new_navigation (structure);
+  if (event) {
+    gst_event_ref (event);
+    handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (self), event);
+
+    if (!handled)
+      gst_element_post_message (GST_ELEMENT_CAST (self),
+          gst_navigation_message_new_event (GST_OBJECT_CAST (self), event));
+
+    gst_event_unref (event);
+  }
+}
+
+static void
+gst_d3d11_video_sink_navigation_init (GstNavigationInterface * iface)
+{
+  iface->send_event = gst_d3d11_video_sink_navigation_send_event;
+}
diff --git a/sys/d3d11/gstd3d11videosink.h b/sys/d3d11/gstd3d11videosink.h
new file mode 100644 (file)
index 0000000..2fd81fc
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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_VIDEO_SINK_H__
+#define __GST_D3D11_VIDEO_SINK_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideosink.h>
+#include <gst/video/videooverlay.h>
+#include <gst/video/navigation.h>
+
+#include "gstd3d11_fwd.h"
+#include "gstd3d11window.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_D3D11_VIDEO_SINK                     (gst_d3d11_video_sink_get_type())
+#define GST_D3D11_VIDEO_SINK(obj)                     (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_VIDEO_SINK,GstD3D11VideoSink))
+#define GST_D3D11_VIDEO_SINK_CLASS(klass)             (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D11_VIDEO_SINK,GstD3D11VideoSinkClass))
+#define GST_D3D11_VIDEO_SINK_GET_CLASS(obj)           (GST_D3D11_VIDEO_SINK_CLASS(G_OBJECT_GET_CLASS(obj)))
+#define GST_IS_D3D11_VIDEO_SINK(obj)                  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_VIDEO_SINK))
+#define GST_IS_D3D11_VIDEO_SINK_CLASS(klass)          (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_VIDEO_SINK))
+
+typedef struct _GstD3D11VideoSink GstD3D11VideoSink;
+typedef struct _GstD3D11VideoSinkClass GstD3D11VideoSinkClass;
+
+struct _GstD3D11VideoSink
+{
+  GstVideoSink sink;
+  GstD3D11Device *device;
+  GstD3D11Window *window;
+
+  GstVideoInfo info;
+  DXGI_FORMAT dxgi_format;
+
+  guintptr window_id;
+
+  /* properties */
+  gint adapter;
+  gboolean force_aspect_ratio;
+  gboolean enable_navigation_events;
+
+  /* saved render rectangle until we have a window */
+  GstVideoRectangle render_rect;
+  gboolean pending_render_rect;
+
+  ID3D11Texture2D *fallback_staging;
+};
+
+struct _GstD3D11VideoSinkClass
+{
+  GstVideoSinkClass parent_class;
+};
+
+GType    gst_d3d11_video_sink_get_type (void);
+
+G_END_DECLS
+
+
+#endif /* __GST_D3D11_VIDEO_SINK_H__ */
diff --git a/sys/d3d11/gstd3d11window.c b/sys/d3d11/gstd3d11window.c
new file mode 100644 (file)
index 0000000..e34e47c
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * 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 "gstd3d11window.h"
+#include "gstd3d11device.h"
+
+#include <windows.h>
+
+enum
+{
+  PROP_0,
+  PROP_D3D11_DEVICE,
+  PROP_FORCE_ASPECT_RATIO,
+  PROP_ENABLE_NAVIGATION_EVENTS,
+};
+
+#define DEFAULT_ENABLE_NAVIGATION_EVENTS  TRUE
+#define DEFAULT_FORCE_ASPECT_RATIO        TRUE
+
+enum
+{
+  SIGNAL_KEY_EVENT,
+  SIGNAL_MOUSE_EVENT,
+  SIGNAL_GOT_WINDOW_HANDLE,
+  SIGNAL_LAST
+};
+
+static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
+
+#define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc"
+#define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_object"
+
+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_dispose (GObject * object);
+static void gst_d3d11_window_finalize (GObject * object);
+static gpointer gst_d3d11_window_thread_func (gpointer data);
+static gboolean _create_window (GstD3D11Window * self);
+static void _open_window (GstD3D11Window * self);
+static void _close_window (GstD3D11Window * self);
+static void release_external_win_id (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->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));
+
+  d3d11_window_signals[SIGNAL_KEY_EVENT] =
+      g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
+      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, g_cclosure_marshal_generic,
+      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, g_cclosure_marshal_generic,
+      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->loop = g_main_loop_new (self->main_context, FALSE);
+
+  self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
+  self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
+
+  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->pending_resize = TRUE;
+
+      self->force_aspect_ratio = force_aspect_ratio;
+      break;
+    }
+    case PROP_ENABLE_NAVIGATION_EVENTS:
+      self->enable_navigation_events = g_value_get_boolean (value);
+      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;
+    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->backbuffer) {
+    ID3D11Texture2D_Release (window->backbuffer);
+    window->backbuffer = NULL;
+  }
+
+  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);
+
+  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_device_thread_add (self->device,
+        (GstD3D11DeviceThreadFunc) gst_d3d11_window_release_resources, self);
+  }
+
+  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 = _create_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);
+
+  if (self->created)
+    _open_window (self);
+
+  g_main_loop_run (self->loop);
+
+  if (self->created)
+    _close_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
+_open_window (GstD3D11Window * self)
+{
+  self->msg_io_channel = g_io_channel_win32_new_messages (0);
+  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);
+}
+
+static void
+_close_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;
+  if (!self->external_win_id)
+    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);
+}
+
+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
+_create_window (GstD3D11Window * self)
+{
+  WNDCLASSEX wc;
+  ATOM atom = 0;
+  HINSTANCE hinstance = GetModuleHandle (NULL);
+
+  GST_LOG_OBJECT (self, "Attempting to create a win32 window");
+
+  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) {
+      GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (NULL),
+          ("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);
+
+  if (!self->internal_win_id) {
+    GST_ELEMENT_ERROR (self, RESOURCE, FAILED, (NULL),
+        ("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_ELEMENT_ERROR (self, RESOURCE, FAILED, (NULL),
+        ("Failed to create device_handle"));
+    return FALSE;
+  }
+
+  GST_LOG_OBJECT (self,
+      "Created a internal d3d11 window %p", self->internal_win_id);
+
+  return TRUE;
+}
+
+static void
+gst_d3d11_window_on_resize (GstD3D11Device * device, GstD3D11Window * window)
+{
+  HRESULT hr;
+  ID3D11Device *d3d11_dev;
+  ID3D11DeviceContext *d3d11_context;
+  guint width, height;
+
+  if (!window->swap_chain)
+    return;
+
+  d3d11_dev = gst_d3d11_device_get_device (device);
+  d3d11_context = gst_d3d11_device_get_device_context (device);
+
+  if (window->backbuffer) {
+    ID3D11Texture2D_Release (window->backbuffer);
+    window->backbuffer = NULL;
+  }
+
+  if (window->rtv) {
+    ID3D11RenderTargetView_Release (window->rtv);
+    window->rtv = NULL;
+  }
+
+  /* NOTE: there can be various way to resize texture, but
+   * we just copy incoming texture toward resized swap chain buffer in order to
+   * avoid shader coding.
+   * To keep aspect ratio, required vertical or horizontal padding area
+   * will be calculated in here.
+   */
+  width = window->width;
+  height = window->height;
+
+  if (width != window->surface_width || height != window->surface_height) {
+    GstVideoRectangle src_rect, dst_rect;
+    gdouble src_ratio, dst_ratio;
+
+    src_ratio = (gdouble) width / height;
+    dst_ratio = (gdouble) window->surface_width / window->surface_height;
+
+    src_rect.x = 0;
+    src_rect.y = 0;
+    src_rect.w = width;
+    src_rect.h = height;
+
+    dst_rect.x = 0;
+    dst_rect.y = 0;
+
+    if (window->force_aspect_ratio) {
+      if (src_ratio > dst_ratio) {
+        /* padding top and bottom */
+        dst_rect.w = width;
+        dst_rect.h = width / dst_ratio;
+      } else {
+        /* padding left and right */
+        dst_rect.w = height * dst_ratio;
+        dst_rect.h = height;
+      }
+    } else {
+      dst_rect.w = width;
+      dst_rect.h = height;
+    }
+
+    gst_video_sink_center_rect (src_rect, dst_rect, &window->render_rect, TRUE);
+
+    width = dst_rect.w;
+    height = dst_rect.h;
+  }
+
+  hr = IDXGISwapChain_ResizeBuffers (window->swap_chain,
+      0, width, height, DXGI_FORMAT_UNKNOWN, 0);
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
+    return;
+  }
+
+  hr = IDXGISwapChain_GetBuffer (window->swap_chain,
+      0, &IID_ID3D11Texture2D, (void **) &window->backbuffer);
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (window,
+        "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
+    return;
+  }
+
+  hr = ID3D11Device_CreateRenderTargetView (d3d11_dev,
+      (ID3D11Resource *) window->backbuffer, NULL, &window->rtv);
+  if (FAILED (hr)) {
+    GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x",
+        (guint) hr);
+    return;
+  }
+
+  ID3D11DeviceContext_OMSetRenderTargets (d3d11_context, 1, &window->rtv, NULL);
+}
+
+static void
+gst_d3d11_window_on_size (GstD3D11Window * self,
+    HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+  RECT clientRect = { 0, };
+
+  GetClientRect (hWnd, &clientRect);
+
+  self->surface_width = clientRect.right - clientRect.left;
+  self->surface_height = clientRect.bottom - clientRect.top;
+
+  GST_LOG_OBJECT (self, "WM_PAINT, surface %ux%u",
+      self->surface_width, self->surface_height);
+
+  gst_d3d11_device_thread_add (self->device,
+      (GstD3D11DeviceThreadFunc) gst_d3d11_window_on_resize, self);
+}
+
+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_size (self, hWnd, wParam, lParam);
+      break;
+    case WM_CLOSE:
+      if (self->internal_win_id) {
+        ShowWindow (self->internal_win_id, SW_HIDE);
+        _close_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;
+    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);
+  }
+
+  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);
+  LRESULT ret = 0;
+
+  gst_d3d11_window_handle_window_proc (self, hWnd, uMsg, wParam, lParam);
+
+  ret = CallWindowProc (external_window_proc, hWnd, uMsg, wParam, lParam);
+
+  if (uMsg == WM_CLOSE)
+    release_external_win_id (self);
+
+  return ret;
+}
+
+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);
+
+  return window;
+}
+
+#ifdef HAVE_DXGI_1_5_H
+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);
+}
+#endif
+
+gboolean
+gst_d3d11_window_prepare (GstD3D11Window * window, guint width, guint height,
+    DXGI_FORMAT format, GstCaps * caps)
+{
+  DXGI_SWAP_CHAIN_DESC desc = { 0, };
+  gboolean have_cll = FALSE;
+  gboolean have_mastering = FALSE;
+  gboolean hdr_api_available = FALSE;
+
+  g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
+
+  GST_DEBUG_OBJECT (window, "Prepare window with %dx%d format %d",
+      width, height, format);
+
+  g_mutex_lock (&window->lock);
+  if (!window->external_win_id) {
+    window->thread = g_thread_new ("GstD3D11Window",
+        (GThreadFunc) gst_d3d11_window_thread_func, window);
+    while (!g_main_loop_is_running (window->loop))
+      g_cond_wait (&window->cond, &window->lock);
+  }
+  g_mutex_unlock (&window->lock);
+
+  gst_video_info_from_caps (&window->info, caps);
+  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;
+  }
+
+#ifdef HAVE_DXGI_1_5_H
+  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");
+    hdr_api_available = TRUE;
+  }
+#endif
+
+  window->render_rect.x = 0;
+  window->render_rect.y = 0;
+  window->render_rect.w = width;
+  window->render_rect.h = height;
+
+  desc.BufferDesc.Width = window->width = window->surface_width = width;
+  desc.BufferDesc.Height = window->height = window->surface_height = height;
+  /* 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 = DXGI_SWAP_EFFECT_DISCARD;
+#ifdef HAVE_DXGI_1_5_H
+  /* For non-DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 color space support,
+   * DXGI_SWAP_EFFECT_FLIP_DISCARD instead of DXGI_SWAP_EFFECT_DISCARD */
+  if (hdr_api_available)
+    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+#endif
+  desc.OutputWindow =
+      window->external_win_id ? window->
+      external_win_id : window->internal_win_id;
+  desc.Windowed = TRUE;
+  desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+  window->swap_chain =
+      gst_d3d11_device_create_swap_chain (window->device, &desc);
+
+  if (!window->swap_chain) {
+    GST_ERROR_OBJECT (window, "Cannot create swapchain");
+    return FALSE;
+  }
+#ifdef HAVE_DXGI_1_5_H
+  if (hdr_api_available && format == DXGI_FORMAT_R10G10B10A2_UNORM &&
+      have_cll && have_mastering) {
+    UINT can_support = 0;
+    HRESULT hr;
+
+    hr = IDXGISwapChain4_CheckColorSpaceSupport ((IDXGISwapChain4 *)
+        window->swap_chain, DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
+        &can_support);
+
+    if (SUCCEEDED (hr) &&
+        (can_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ==
+        DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) {
+      DXGI_HDR_METADATA_HDR10 metadata = { 0, };
+
+      GST_DEBUG_OBJECT (window,
+          "Swapchain support BT2084 color space, set HDR metadata");
+
+      mastering_display_gst_to_dxgi (&window->mastering_display_info,
+          &window->content_light_level, &metadata);
+
+      hr = IDXGISwapChain4_SetColorSpace1 ((IDXGISwapChain4 *)
+          window->swap_chain, DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
+
+      if (SUCCEEDED (hr)) {
+        hr = IDXGISwapChain4_SetHDRMetaData ((IDXGISwapChain4 *)
+            window->swap_chain, DXGI_HDR_METADATA_TYPE_HDR10,
+            sizeof (DXGI_HDR_METADATA_HDR10), &metadata);
+        if (FAILED (hr)) {
+          GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x",
+              (guint) hr);
+        }
+      } else {
+        GST_WARNING_OBJECT (window, "Couldn't set colorspace, hr 0x%x",
+            (guint) hr);
+      }
+    } else {
+      GST_DEBUG_OBJECT (window,
+          "Swapchain couldn't support BT2084 color space, hr 0x%x", (guint) hr);
+    }
+  }
+#endif
+
+  gst_d3d11_device_thread_add (window->device,
+      (GstD3D11DeviceThreadFunc) gst_d3d11_window_on_resize, window);
+
+  if (!window->rtv) {
+    gst_d3d11_device_thread_add (window->device,
+        (GstD3D11DeviceThreadFunc) gst_d3d11_window_release_resources, window);
+    return FALSE;
+  }
+
+  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));
+
+  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);
+}
+
+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 */
+}
+
+void
+gst_d3d11_window_get_surface_dimensions (GstD3D11Window * window,
+    guint * width, guint * height)
+{
+  g_return_if_fail (GST_IS_D3D11_WINDOW (window));
+
+  if (width)
+    *width = window->surface_width;
+  if (height)
+    *height = window->surface_height;
+}
+
+typedef struct
+{
+  GstD3D11Window *window;
+  ID3D11Resource *resource;
+  GstVideoRectangle *rect;
+
+  GstFlowReturn ret;
+} FramePresentData;
+
+static void
+_present_on_device_thread (GstD3D11Device * device, FramePresentData * data)
+{
+  GstD3D11Window *self = data->window;
+  ID3D11DeviceContext *device_context;
+  HRESULT hr;
+  float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+  D3D11_BOX src_box;
+
+  src_box.left = data->rect->x;
+  src_box.right = data->rect->x + data->rect->w;
+  src_box.top = data->rect->y;
+  src_box.bottom = data->rect->y + data->rect->h;
+  src_box.front = 0;
+  src_box.back = 1;
+
+  device_context = gst_d3d11_device_get_device_context (device);
+
+  if (data->resource) {
+    ID3D11DeviceContext_ClearRenderTargetView (device_context, self->rtv,
+        black);
+    ID3D11DeviceContext_CopySubresourceRegion (device_context,
+        (ID3D11Resource *) self->backbuffer, 0, self->render_rect.x,
+        self->render_rect.y, 0, data->resource, 0, &src_box);
+  }
+
+  hr = IDXGISwapChain_Present (self->swap_chain, 0, DXGI_PRESENT_DO_NOT_WAIT);
+
+  if (FAILED (hr)) {
+    GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
+        (guint) hr);
+  }
+
+  data->ret = GST_FLOW_OK;
+}
+
+GstFlowReturn
+gst_d3d11_window_render (GstD3D11Window * window, ID3D11Texture2D * texture,
+    GstVideoRectangle * rect)
+{
+  FramePresentData data;
+
+  g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
+  g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR);
+
+  if (!window->external_win_id && !window->internal_win_id) {
+    GST_ERROR_OBJECT (window, "Output window was closed");
+    return GST_D3D11_WINDOW_FLOW_CLOSED;
+  }
+
+  GST_OBJECT_LOCK (window);
+  if (rect->w != window->width || rect->h != window->height ||
+      window->pending_resize) {
+    window->width = rect->w;
+    window->height = rect->h;
+
+    gst_d3d11_device_thread_add (window->device,
+        (GstD3D11DeviceThreadFunc) gst_d3d11_window_on_resize, window);
+  }
+  GST_OBJECT_UNLOCK (window);
+
+  data.window = window;
+  data.resource = (ID3D11Resource *) texture;
+  data.rect = rect;
+  data.ret = GST_FLOW_OK;
+
+  gst_d3d11_device_thread_add (window->device,
+      (GstD3D11DeviceThreadFunc) _present_on_device_thread, &data);
+
+  return data.ret;
+}
diff --git a/sys/d3d11/gstd3d11window.h b/sys/d3d11/gstd3d11window.h
new file mode 100644 (file)
index 0000000..3e39e7a
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * GStreamer
+ * 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.
+ */
+
+#ifndef __GST_D3D11_WINDOW_H__
+#define __GST_D3D11_WINDOW_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include "gstd3d11_fwd.h"
+
+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_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))
+
+typedef struct _GstD3D11Window        GstD3D11Window;
+typedef struct _GstD3D11WindowClass   GstD3D11WindowClass;
+
+#define GST_D3D11_WINDOW_FLOW_CLOSED GST_FLOW_CUSTOM_ERROR
+
+struct _GstD3D11Window
+{
+  GstObject parent;
+
+  GstVideoInfo info;
+  GstVideoMasteringDisplayInfo mastering_display_info;
+  GstVideoContentLightLevel content_light_level;
+
+  GstVideoRectangle render_rect;
+
+  GMutex lock;
+  GCond cond;
+
+  GMainContext *main_context;
+  GMainLoop *loop;
+
+  guint width;
+  guint height;
+
+  guint surface_width;
+  guint surface_height;
+
+  gboolean visible;
+
+  GSource *msg_source;
+  GIOChannel *msg_io_channel;
+
+  GThread *thread;
+
+  gboolean created;
+
+  HWND internal_win_id;
+  HWND external_win_id;
+
+  HDC device_handle;
+  IDXGISwapChain *swap_chain;
+  ID3D11Texture2D *backbuffer;
+  ID3D11RenderTargetView *rtv;
+  DXGI_FORMAT format;
+
+  GstD3D11Device *device;
+
+  gboolean pending_resize;
+
+  gboolean force_aspect_ratio;
+  gboolean enable_navigation_events;
+};
+
+struct _GstD3D11WindowClass
+{
+  GstObjectClass object_class;
+};
+
+GType             gst_d3d11_window_get_type     (void);
+
+GstD3D11Window *  gst_d3d11_window_new (GstD3D11Device * device);
+
+void              gst_d3d11_window_show (GstD3D11Window * window);
+
+void              gst_d3d11_window_set_window_handle (GstD3D11Window * window,
+                                                      guintptr id);
+
+void              gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
+                                                         gint x, gint y,
+                                                         gint width, gint height);
+
+void gst_d3d11_window_get_surface_dimensions (GstD3D11Window * window,
+                                         guint * width,
+                                         guint * height);
+
+gboolean gst_d3d11_window_prepare (GstD3D11Window * window,
+                                   guint width,
+                                   guint height,
+                                   DXGI_FORMAT format,
+                                   GstCaps * caps);
+
+GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window,
+                                       ID3D11Texture2D * texture,
+                                       GstVideoRectangle * src_rect);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_WINDOW_H__ */
diff --git a/sys/d3d11/meson.build b/sys/d3d11/meson.build
new file mode 100644 (file)
index 0000000..bd080d1
--- /dev/null
@@ -0,0 +1,51 @@
+d3d11_sources = [
+  'gstd3d11bufferpool.c',
+  'gstd3d11device.c',
+  'gstd3d11memory.c',
+  'gstd3d11utils.c',
+  'gstd3d11videosink.c',
+  'gstd3d11window.c',
+  'plugin.c',
+]
+
+have_d3d11 = false
+extra_c_args = []
+extra_dep = []
+
+d3d11_option = get_option('d3d11')
+if host_system != 'windows' or d3d11_option.disabled()
+  subdir_done()
+endif
+
+d3d11_lib = cc.find_library('d3d11', required : d3d11_option)
+dxgi_lib = cc.find_library('dxgi', required : d3d11_option)
+
+have_d3d11 = d3d11_lib.found() and dxgi_lib.found() and cc.has_header('d3d11.h') and cc.has_header('dxgi.h')
+if not have_d3d11
+  if d3d11_option.enabled()
+    error('The d3d11 plugin was enabled explicitly, but required dependencies were not found.')
+  endif
+  subdir_done()
+endif
+
+# required for HDR meatadata
+if cc.has_header('dxgi1_5.h')
+  extra_c_args += ['-DHAVE_DXGI_1_5_H']
+endif
+
+# for enabling debug layer
+if cc.has_header('d3d11sdklayers.h')
+  extra_c_args += ['-DHAVE_D3D11SDKLAYER_H']
+  extra_dep += [gmodule_dep]
+endif
+
+gstd3d11 = library('gstd3d11',
+  d3d11_sources,
+  c_args : gst_plugins_bad_args + extra_c_args,
+  include_directories : [configinc],
+  dependencies : [gstbase_dep, gstvideo_dep, gstallocators_dep, d3d11_lib, dxgi_lib] + extra_dep,
+  install : true,
+  install_dir : plugins_install_dir,
+)
+pkgconfig.generate(gstd3d11, install_dir : plugins_pkgconfig_install_dir)
+plugins += [gstd3d11]
diff --git a/sys/d3d11/plugin.c b/sys/d3d11/plugin.c
new file mode 100644 (file)
index 0000000..11d95a6
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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 <gst/gst.h>
+#include "gstd3d11videosink.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin,
+      "d3d11videosink", GST_RANK_SECONDARY - 1, GST_TYPE_D3D11_VIDEO_SINK);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    d3d11,
+    "Direct3D11 plugin",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
index a42e872..0b69ad0 100644 (file)
@@ -1,6 +1,7 @@
 subdir('androidmedia')
 subdir('applemedia')
 subdir('bluez')
+subdir('d3d11')
 subdir('d3dvideosink')
 subdir('decklink')
 subdir('directsound')