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')
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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, ¶ms->desc, NULL);
+ if (!texture) {
+ GST_ERROR_OBJECT (allocator, "Couldn't create texture");
+ goto error;
+ }
+
+ staging = _create_staging_texture (device, ¶ms->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 = ¶ms->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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+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]
--- /dev/null
+/* 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)
subdir('androidmedia')
subdir('applemedia')
subdir('bluez')
+subdir('d3d11')
subdir('d3dvideosink')
subdir('decklink')
subdir('directsound')