d3d11videosink: Add new Direct3D11 video render plugin

Direct3D11 was shipped as part of Windows7 and it's obviously
primary graphics API on Windows.

This plugin includes HDR10 rendering if following requirements are satisfied
* IDXGISwapChain4::SetHDRMetaData is available (decleared in dxgi1_5.h)
* Display can support DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 color space
* Upstream provides 10 bitdepth format with smpte-st 2084 static metadata
This commit is contained in:
Seungha Yang 2019-01-30 20:07:29 +09:00 committed by Sebastian Dröge
parent 329b2d3a6a
commit 5c3879ace6
17 changed files with 4639 additions and 0 deletions

View file

@ -87,6 +87,7 @@ option('colormanagement', type : 'feature', value : 'auto', description : 'Color
option('curl', type : 'feature', value : 'auto', description : 'cURL network source and sink plugin')
option('curl-ssh2', type : 'feature', value : 'auto', description : 'cURL network source and sink plugin libssh2 support')
option('d3dvideosink', type : 'feature', value : 'auto', description : 'Direct3D video sink plugin')
option('d3d11', type : 'feature', value : 'auto', description : 'Direct3D11 plugin')
option('dash', type : 'feature', value : 'auto', description : 'DASH demuxer plugin')
option('dc1394', type : 'feature', value : 'auto', description : 'libdc1394 IIDC camera source plugin')
option('decklink', type : 'feature', value : 'auto', description : 'DeckLink audio/video source/sink plugin')

59
sys/d3d11/gstd3d11_fwd.h Normal file
View file

@ -0,0 +1,59 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_FWD_H__
#define __GST_D3D11_FWD_H__
#include <gst/gst.h>
/* define COBJMACROS to use d3d11 C APIs */
#ifndef COBJMACROS
#define COBJMACROS
#endif
#ifndef INITGUID
#include <initguid.h>
#endif
#include <d3d11.h>
#ifdef HAVE_DXGI_1_5_H
#include <dxgi1_5.h>
#else
#include <dxgi.h>
#endif
G_BEGIN_DECLS
typedef struct _GstD3D11Device GstD3D11Device;
typedef struct _GstD3D11DeviceClass GstD3D11DeviceClass;
typedef struct _GstD3D11DevicePrivate GstD3D11DevicePrivate;
typedef struct _GstD3D11AllocationParams GstD3D11AllocationParams;
typedef struct _GstD3D11Memory GstD3D11Memory;
typedef struct _GstD3D11Allocator GstD3D11Allocator;
typedef struct _GstD3D11AllocatorClass GstD3D11AllocatorClass;
typedef struct _GstD3D11AllocatorPrivate GstD3D11AllocatorPrivate;
typedef struct _GstD3D11BufferPool GstD3D11BufferPool;
typedef struct _GstD3D11BufferPoolClass GstD3D11BufferPoolClass;
typedef struct _GstD3D11BufferPoolPrivate GstD3D11BufferPoolPrivate;
G_END_DECLS
#endif /* __GST_D3D11_FWD_H__ */

View file

@ -0,0 +1,308 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11bufferpool.h"
#include "gstd3d11memory.h"
#include "gstd3d11device.h"
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_buffer_pool_debug);
#define GST_CAT_DEFAULT gst_d3d11_buffer_pool_debug
struct _GstD3D11BufferPoolPrivate
{
GstD3D11Device *device;
GstD3D11Allocator *allocator;
GstCaps *caps;
gboolean add_videometa;
GstD3D11AllocationParams *d3d11_params;
};
#define gst_d3d11_buffer_pool_parent_class parent_class
G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11BufferPool,
gst_d3d11_buffer_pool, GST_TYPE_BUFFER_POOL);
static void gst_d3d11_buffer_pool_dispose (GObject * object);
static const gchar **gst_d3d11_buffer_pool_get_options (GstBufferPool * pool);
static gboolean gst_d3d11_buffer_pool_set_config (GstBufferPool * pool,
GstStructure * config);
static GstFlowReturn gst_d3d11_buffer_pool_alloc (GstBufferPool * pool,
GstBuffer ** buffer, GstBufferPoolAcquireParams * params);
static void
gst_d3d11_buffer_pool_class_init (GstD3D11BufferPoolClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstBufferPoolClass *bufferpool_class = GST_BUFFER_POOL_CLASS (klass);
gobject_class->dispose = gst_d3d11_buffer_pool_dispose;
bufferpool_class->get_options = gst_d3d11_buffer_pool_get_options;
bufferpool_class->set_config = gst_d3d11_buffer_pool_set_config;
bufferpool_class->alloc_buffer = gst_d3d11_buffer_pool_alloc;
GST_DEBUG_CATEGORY_INIT (gst_d3d11_buffer_pool_debug, "d3d11bufferpool", 0,
"d3d11bufferpool object");
}
static void
gst_d3d11_buffer_pool_init (GstD3D11BufferPool * self)
{
self->priv = gst_d3d11_buffer_pool_get_instance_private (self);
}
static void
gst_d3d11_buffer_pool_dispose (GObject * object)
{
GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (object);
GstD3D11BufferPoolPrivate *priv = self->priv;
if (priv->d3d11_params)
gst_d3d11_allocation_params_free (priv->d3d11_params);
priv->d3d11_params = NULL;
gst_clear_object (&priv->device);
gst_clear_object (&priv->allocator);
gst_clear_caps (&priv->caps);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static const gchar **
gst_d3d11_buffer_pool_get_options (GstBufferPool * pool)
{
static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT,
NULL
};
return options;
}
static gboolean
gst_d3d11_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
{
GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
GstD3D11BufferPoolPrivate *priv = self->priv;
GstVideoInfo info;
GstCaps *caps = NULL;
guint min_buffers, max_buffers;
guint max_align, n;
GstAllocator *allocator = NULL;
GstAllocationParams alloc_params;
gboolean ret = TRUE;
D3D11_TEXTURE2D_DESC *desc;
if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min_buffers,
&max_buffers))
goto wrong_config;
if (caps == NULL)
goto no_caps;
/* now parse the caps from the config */
if (!gst_video_info_from_caps (&info, caps))
goto wrong_caps;
GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
caps);
if (!gst_buffer_pool_config_get_allocator (config, &allocator, &alloc_params))
goto wrong_config;
gst_caps_replace (&priv->caps, caps);
if (priv->allocator)
gst_object_unref (priv->allocator);
if (allocator) {
if (!GST_IS_D3D11_ALLOCATOR (allocator)) {
gst_object_unref (allocator);
goto wrong_allocator;
} else {
priv->allocator = gst_object_ref (allocator);
}
} else {
priv->allocator = gst_d3d11_allocator_new (priv->device);
g_assert (priv->allocator);
}
priv->add_videometa = gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_META);
if (priv->d3d11_params)
gst_d3d11_allocation_params_free (priv->d3d11_params);
priv->d3d11_params =
gst_buffer_pool_config_get_d3d11_allocation_params (config);
if (!priv->d3d11_params)
priv->d3d11_params = gst_d3d11_allocation_params_new (&alloc_params,
&info, NULL);
desc = &priv->d3d11_params->desc;
GST_LOG_OBJECT (self, "Direct3D11 Allocation params");
GST_LOG_OBJECT (self, "\t%dx%d, DXGI format %d",
desc->Width, desc->Height, desc->Format);
GST_LOG_OBJECT (self, "\tMipLevel %d, ArraySize %d",
desc->MipLevels, desc->ArraySize);
GST_LOG_OBJECT (self, "\tSampleDesc.Count %d, SampleDesc.Quality %d",
desc->SampleDesc.Count, desc->SampleDesc.Quality);
GST_LOG_OBJECT (self, "\tUsage %d", desc->Usage);
GST_LOG_OBJECT (self, "\tBindFlags 0x%x", desc->BindFlags);
GST_LOG_OBJECT (self, "\tCPUAccessFlags 0x%x", desc->CPUAccessFlags);
GST_LOG_OBJECT (self, "\tMiscFlags 0x%x", desc->MiscFlags);
max_align = alloc_params.align;
if (gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
priv->add_videometa = TRUE;
gst_buffer_pool_config_get_video_alignment (config,
&priv->d3d11_params->align);
for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
max_align |= priv->d3d11_params->align.stride_align[n];
for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
priv->d3d11_params->align.stride_align[n] = max_align;
gst_video_info_align (&priv->d3d11_params->info,
&priv->d3d11_params->align);
gst_buffer_pool_config_set_video_alignment (config,
&priv->d3d11_params->align);
}
if (alloc_params.align < max_align) {
GST_WARNING_OBJECT (pool, "allocation params alignment %u is smaller "
"than the max specified video stride alignment %u, fixing",
(guint) alloc_params.align, max_align);
alloc_params.align = priv->d3d11_params->parent.align = max_align;
gst_buffer_pool_config_set_allocator (config, allocator, &alloc_params);
gst_allocation_params_copy (&alloc_params);
}
gst_buffer_pool_config_set_params (config,
caps, GST_VIDEO_INFO_SIZE (&priv->d3d11_params->info), min_buffers,
max_buffers);
return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config) && ret;
/* ERRORS */
wrong_config:
{
GST_WARNING_OBJECT (pool, "invalid config");
return FALSE;
}
no_caps:
{
GST_WARNING_OBJECT (pool, "no caps in config");
return FALSE;
}
wrong_caps:
{
GST_WARNING_OBJECT (pool,
"failed getting geometry from caps %" GST_PTR_FORMAT, caps);
return FALSE;
}
wrong_allocator:
{
GST_WARNING_OBJECT (pool, "Incorrect allocator type for this pool");
return FALSE;
}
}
static GstFlowReturn
gst_d3d11_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params)
{
GstD3D11BufferPool *self = GST_D3D11_BUFFER_POOL (pool);
GstD3D11BufferPoolPrivate *priv = self->priv;
GstMemory *mem;
GstBuffer *buf;
GstVideoInfo *info = &priv->d3d11_params->info;
mem = gst_d3d11_allocator_alloc (priv->allocator, priv->d3d11_params);
if (!mem) {
GST_ERROR_OBJECT (self, "cannot create texture memory");
return GST_FLOW_ERROR;
}
buf = gst_buffer_new ();
gst_buffer_append_memory (buf, mem);
if (priv->add_videometa) {
GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
GST_DEBUG_OBJECT (self, "adding GstVideoMeta");
gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
dmem->offset, dmem->stride);
}
*buffer = buf;
return GST_FLOW_OK;
}
GstD3D11BufferPool *
gst_d3d11_buffer_pool_new (GstD3D11Device * device)
{
GstD3D11BufferPool *pool;
GstD3D11Allocator *alloc;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
pool = g_object_new (GST_TYPE_D3D11_BUFFER_POOL, NULL);
alloc = gst_d3d11_allocator_new (device);
pool->priv->device = gst_object_ref (device);
pool->priv->allocator = alloc;
return pool;
}
GstD3D11AllocationParams *
gst_buffer_pool_config_get_d3d11_allocation_params (GstStructure * config)
{
GstD3D11AllocationParams *ret;
if (!gst_structure_get (config, "d3d11-allocation-params",
GST_TYPE_D3D11_ALLOCATION_PARAMS, &ret, NULL))
ret = NULL;
return ret;
}
void
gst_buffer_pool_config_set_d3d11_allocation_params (GstStructure * config,
GstD3D11AllocationParams * params)
{
g_return_if_fail (config != NULL);
g_return_if_fail (params != NULL);
gst_structure_set (config, "d3d11-allocation-params",
GST_TYPE_D3D11_ALLOCATION_PARAMS, params, NULL);
}

View file

@ -0,0 +1,67 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_BUFFER_POOL_H__
#define __GST_D3D11_BUFFER_POOL_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_BUFFER_POOL (gst_d3d11_buffer_pool_get_type())
#define GST_D3D11_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPool))
#define GST_D3D11_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPoolClass))
#define GST_IS_D3D11_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_BUFFER_POOL))
#define GST_IS_D3D11_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_BUFFER_POOL))
#define GST_D3D11_BUFFER_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_BUFFER_POOL, GstD3D11BufferPoolClass))
struct _GstD3D11BufferPool
{
GstBufferPool parent;
/*< private >*/
GstD3D11BufferPoolPrivate *priv;
gpointer _gst_reserved[GST_PADDING];
};
struct _GstD3D11BufferPoolClass
{
GstBufferPoolClass bufferpool_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_d3d11_buffer_pool_get_type (void);
GstD3D11BufferPool * gst_d3d11_buffer_pool_new (GstD3D11Device *device);
GstD3D11AllocationParams * gst_buffer_pool_config_get_d3d11_allocation_params (GstStructure * config);
void gst_buffer_pool_config_set_d3d11_allocation_params (GstStructure * config,
GstD3D11AllocationParams * params);
G_END_DECLS
#endif /* __GST_D3D11_BUFFER_POOL_H__ */

898
sys/d3d11/gstd3d11device.c Normal file
View file

@ -0,0 +1,898 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11device.h"
#include "gmodule.h"
#ifdef HAVE_D3D11SDKLAYER_H
#include <d3d11sdklayers.h>
#endif
GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_device_debug);
#define GST_CAT_DEFAULT gst_d3d11_device_debug
#ifdef HAVE_D3D11SDKLAYER_H
static GModule *sdk_layer = NULL;
#endif
enum
{
PROP_0,
PROP_ADAPTER
};
#define DEFAULT_ADAPTER -1
struct _GstD3D11DevicePrivate
{
gint adapter;
ID3D11Device *device;
ID3D11DeviceContext *device_context;
IDXGIFactory1 *factory;
GstD3D11DXGIFactoryVersion factory_ver;
ID3D11VideoDevice *video_device;
ID3D11VideoContext *video_context;
GMutex lock;
GCond cond;
GThread *thread;
GThread *active_thread;
GMainLoop *loop;
GMainContext *main_context;
#ifdef HAVE_D3D11SDKLAYER_H
ID3D11Debug *debug;
ID3D11InfoQueue *info_queue;
#endif
};
#define gst_d3d11_device_parent_class parent_class
G_DEFINE_TYPE_WITH_PRIVATE (GstD3D11Device, gst_d3d11_device, GST_TYPE_OBJECT);
static void gst_d3d11_device_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_d3d11_device_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_d3d11_device_constructed (GObject * object);
static void gst_d3d11_device_dispose (GObject * object);
static void gst_d3d11_device_finalize (GObject * object);
static gpointer gst_d3d11_device_thread_func (gpointer data);
#ifdef HAVE_D3D11SDKLAYER_H
static gboolean
gst_d3d11_device_enable_debug_layer (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
sdk_layer = g_module_open ("d3d11sdklayers.dll", G_MODULE_BIND_LAZY);
if (!sdk_layer)
sdk_layer = g_module_open ("d3d11_1sdklayers.dll", G_MODULE_BIND_LAZY);
g_once_init_leave (&_init, 1);
}
return ! !sdk_layer;
}
static gboolean
gst_d3d11_device_get_message (GstD3D11Device * self)
{
GstD3D11DevicePrivate *priv = self->priv;
D3D11_MESSAGE *msg;
SIZE_T msg_len = 0;
HRESULT hr;
UINT64 num_msg, i;
num_msg = ID3D11InfoQueue_GetNumStoredMessages (priv->info_queue);
for (i = 0; i < num_msg; i++) {
hr = ID3D11InfoQueue_GetMessage (priv->info_queue, i, NULL, &msg_len);
if (FAILED (hr) || msg_len == 0) {
return G_SOURCE_CONTINUE;
}
msg = (D3D11_MESSAGE *) g_malloc0 (msg_len);
hr = ID3D11InfoQueue_GetMessage (priv->info_queue, i, msg, &msg_len);
GST_TRACE_OBJECT (self, "D3D11 Message - %s", msg->pDescription);
g_free (msg);
}
ID3D11InfoQueue_ClearStoredMessages (priv->info_queue);
return G_SOURCE_CONTINUE;
}
#endif
static void
gst_d3d11_device_class_init (GstD3D11DeviceClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_d3d11_device_set_property;
gobject_class->get_property = gst_d3d11_device_get_property;
gobject_class->constructed = gst_d3d11_device_constructed;
gobject_class->dispose = gst_d3d11_device_dispose;
gobject_class->finalize = gst_d3d11_device_finalize;
g_object_class_install_property (gobject_class, PROP_ADAPTER,
g_param_spec_int ("adapter", "Adapter",
"Adapter index for creating device (-1 for default)",
-1, G_MAXINT32, DEFAULT_ADAPTER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug,
"d3d11device", 0, "d3d11 device");
GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
}
static void
gst_d3d11_device_init (GstD3D11Device * self)
{
GstD3D11DevicePrivate *priv;
priv = gst_d3d11_device_get_instance_private (self);
priv->adapter = DEFAULT_ADAPTER;
g_mutex_init (&priv->lock);
g_cond_init (&priv->cond);
priv->main_context = g_main_context_new ();
priv->loop = g_main_loop_new (priv->main_context, FALSE);
self->priv = priv;
}
static void
_relase_adapter (IDXGIAdapter1 * adapter)
{
IDXGIAdapter1_Release (adapter);
}
static void
gst_d3d11_device_constructed (GObject * object)
{
GstD3D11Device *self = GST_D3D11_DEVICE (object);
GstD3D11DevicePrivate *priv = self->priv;
IDXGIAdapter1 *adapter = NULL;
GList *adapter_list = NULL;
GList *hw_adapter_list = NULL;
IDXGIFactory1 *factory = NULL;
HRESULT hr;
guint i;
guint num_adapter = 0;
UINT d3d11_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
static const D3D_DRIVER_TYPE driver_types[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_UNKNOWN
};
static const D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
D3D_FEATURE_LEVEL selected_level;
#ifdef HAVE_DXGI_1_5_H
hr = CreateDXGIFactory1 (&IID_IDXGIFactory5, (void **) &factory);
if (FAILED (hr)) {
GST_INFO_OBJECT (self, "IDXGIFactory5 was unavailable");
factory = NULL;
}
priv->factory_ver = GST_D3D11_DXGI_FACTORY_5;
#endif
if (!factory) {
priv->factory_ver = GST_D3D11_DXGI_FACTORY_1;
hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &factory);
}
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "cannot create dxgi factory, hr: 0x%x", (guint) hr);
goto error;
}
while (IDXGIFactory1_EnumAdapters1 (factory, num_adapter,
&adapter) != DXGI_ERROR_NOT_FOUND) {
DXGI_ADAPTER_DESC1 desc;
hr = IDXGIAdapter1_GetDesc1 (adapter, &desc);
if (SUCCEEDED (hr)) {
gchar *vender = NULL;
vender = g_utf16_to_utf8 (desc.Description, -1, NULL, NULL, NULL);
GST_DEBUG_OBJECT (self,
"adapter index %d: D3D11 device vendor-id: 0x%04x, device-id: 0x%04x, "
"Flags: 0x%x, %s",
num_adapter, desc.VendorId, desc.DeviceId, desc.Flags, vender);
g_free (vender);
/* DXGI_ADAPTER_FLAG_SOFTWARE is missing in dxgi.h of mingw */
if ((desc.Flags & 0x2) != 0x2) {
hw_adapter_list = g_list_append (hw_adapter_list, adapter);
IDXGIAdapter1_AddRef (adapter);
}
}
adapter_list = g_list_append (adapter_list, adapter);
num_adapter++;
if (priv->adapter >= 0 && priv->adapter < num_adapter)
break;
}
adapter = NULL;
if (priv->adapter >= 0) {
if (priv->adapter >= num_adapter) {
GST_WARNING_OBJECT (self,
"Requested index %d is out of scope for adapter", priv->adapter);
} else {
adapter = (IDXGIAdapter1 *) g_list_nth_data (adapter_list, priv->adapter);
}
} else if (hw_adapter_list) {
adapter = (IDXGIAdapter1 *) g_list_nth_data (hw_adapter_list, 0);
} else if (adapter_list) {
adapter = (IDXGIAdapter1 *) g_list_nth_data (adapter_list, 0);
}
if (adapter)
IDXGIAdapter1_AddRef (adapter);
#ifdef HAVE_D3D11SDKLAYER_H
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_TRACE) {
/* DirectX SDK should be installed on system for this */
if (gst_d3d11_device_enable_debug_layer ()) {
GST_INFO_OBJECT (self, "sdk layer library was loaded");
d3d11_flags |= D3D11_CREATE_DEVICE_DEBUG;
}
}
#endif
if (adapter) {
hr = D3D11CreateDevice ((IDXGIAdapter *) adapter, D3D_DRIVER_TYPE_UNKNOWN,
NULL, d3d11_flags, feature_levels, G_N_ELEMENTS (feature_levels),
D3D11_SDK_VERSION, &priv->device, &selected_level,
&priv->device_context);
if (FAILED (hr)) {
/* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
hr = D3D11CreateDevice ((IDXGIAdapter *) adapter, D3D_DRIVER_TYPE_UNKNOWN,
NULL, d3d11_flags, &feature_levels[1],
G_N_ELEMENTS (feature_levels) - 1, D3D11_SDK_VERSION, &priv->device,
&selected_level, &priv->device_context);
}
if (SUCCEEDED (hr)) {
GST_DEBUG_OBJECT (self, "Selected feature level 0x%x", selected_level);
}
} else {
for (i = 0; i < G_N_ELEMENTS (driver_types); i++) {
hr = D3D11CreateDevice (NULL, driver_types[i], NULL,
d3d11_flags,
feature_levels, G_N_ELEMENTS (feature_levels),
D3D11_SDK_VERSION, &priv->device, &selected_level,
&priv->device_context);
if (FAILED (hr)) {
/* Retry if the system could not recognize D3D_FEATURE_LEVEL_11_1 */
hr = D3D11CreateDevice (NULL, driver_types[i], NULL,
d3d11_flags,
&feature_levels[1], G_N_ELEMENTS (feature_levels) - 1,
D3D11_SDK_VERSION, &priv->device, &selected_level,
&priv->device_context);
}
if (SUCCEEDED (hr)) {
GST_DEBUG_OBJECT (self, "Selected driver type 0x%x, feature level 0x%x",
driver_types[i], selected_level);
break;
}
}
}
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "cannot create d3d11 device, hr: 0x%x", (guint) hr);
goto error;
}
priv->factory = factory;
if (adapter)
IDXGIAdapter1_Release (adapter);
if (adapter_list)
g_list_free_full (adapter_list, (GDestroyNotify) _relase_adapter);
if (hw_adapter_list)
g_list_free_full (hw_adapter_list, (GDestroyNotify) _relase_adapter);
#ifdef HAVE_D3D11SDKLAYER_H
if ((d3d11_flags & D3D11_CREATE_DEVICE_DEBUG) == D3D11_CREATE_DEVICE_DEBUG) {
ID3D11Debug *debug;
ID3D11InfoQueue *info_queue;
hr = ID3D11Device_QueryInterface (priv->device,
&IID_ID3D11Debug, (void **) &debug);
if (SUCCEEDED (hr)) {
GST_DEBUG_OBJECT (self, "D3D11Debug interface available");
ID3D11Debug_ReportLiveDeviceObjects (debug, D3D11_RLDO_DETAIL);
priv->debug = debug;
}
hr = ID3D11Device_QueryInterface (priv->device,
&IID_ID3D11InfoQueue, (void **) &info_queue);
if (SUCCEEDED (hr)) {
GSource *source;
GST_DEBUG_OBJECT (self, "D3D11InfoQueue interface available");
priv->info_queue = info_queue;
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) gst_d3d11_device_get_message,
self, NULL);
g_source_attach (source, priv->main_context);
g_source_unref (source);
}
}
#endif
g_mutex_lock (&priv->lock);
priv->thread = g_thread_new ("GstD3D11Device",
(GThreadFunc) gst_d3d11_device_thread_func, self);
while (!g_main_loop_is_running (priv->loop))
g_cond_wait (&priv->cond, &priv->lock);
g_mutex_unlock (&priv->lock);
G_OBJECT_CLASS (parent_class)->constructed (object);
return;
error:
if (factory)
IDXGIFactory1_Release (factory);
if (adapter)
IDXGIAdapter1_Release (adapter);
if (adapter_list)
g_list_free_full (adapter_list, (GDestroyNotify) _relase_adapter);
if (hw_adapter_list)
g_list_free_full (hw_adapter_list, (GDestroyNotify) _relase_adapter);
G_OBJECT_CLASS (parent_class)->constructed (object);
return;
}
static void
gst_d3d11_device_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstD3D11Device *self = GST_D3D11_DEVICE (object);
GstD3D11DevicePrivate *priv = self->priv;
switch (prop_id) {
case PROP_ADAPTER:
priv->adapter = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_device_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstD3D11Device *self = GST_D3D11_DEVICE (object);
GstD3D11DevicePrivate *priv = self->priv;
switch (prop_id) {
case PROP_ADAPTER:
g_value_set_int (value, priv->adapter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_device_dispose (GObject * object)
{
GstD3D11Device *self = GST_D3D11_DEVICE (object);
GstD3D11DevicePrivate *priv = self->priv;
GST_LOG_OBJECT (self, "dispose");
if (priv->loop) {
g_main_loop_quit (priv->loop);
}
if (priv->thread) {
g_thread_join (priv->thread);
priv->thread = NULL;
}
if (priv->loop) {
g_main_loop_unref (priv->loop);
priv->loop = NULL;
}
if (priv->main_context) {
g_main_context_unref (priv->main_context);
priv->main_context = NULL;
}
#ifdef HAVE_D3D11SDKLAYER_H
if (priv->debug) {
ID3D11Debug_Release (priv->debug);
priv->debug = NULL;
}
if (priv->info_queue) {
ID3D11InfoQueue_ClearStoredMessages (priv->info_queue);
ID3D11InfoQueue_Release (priv->info_queue);
priv->info_queue = NULL;
}
#endif
if (priv->device) {
ID3D11Device_Release (priv->device);
priv->device = NULL;
}
if (priv->device_context) {
ID3D11DeviceContext_Release (priv->device_context);
priv->device_context = NULL;
}
if (priv->video_device) {
ID3D11VideoDevice_Release (priv->video_device);
priv->video_device = NULL;
}
if (priv->video_context) {
ID3D11VideoContext_Release (priv->video_context);
priv->video_context = NULL;
}
if (priv->factory) {
IDXGIFactory1_Release (priv->factory);
priv->factory = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_d3d11_device_finalize (GObject * object)
{
GstD3D11Device *self = GST_D3D11_DEVICE (object);
GstD3D11DevicePrivate *priv = self->priv;
GST_LOG_OBJECT (self, "finalize");
g_mutex_clear (&priv->lock);
g_cond_clear (&priv->cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
running_cb (gpointer user_data)
{
GstD3D11Device *self = GST_D3D11_DEVICE (user_data);
GstD3D11DevicePrivate *priv = self->priv;
GST_TRACE_OBJECT (self, "Main loop running now");
g_mutex_lock (&priv->lock);
g_cond_signal (&priv->cond);
g_mutex_unlock (&priv->lock);
return G_SOURCE_REMOVE;
}
static gpointer
gst_d3d11_device_thread_func (gpointer data)
{
GstD3D11Device *self = GST_D3D11_DEVICE (data);
GstD3D11DevicePrivate *priv = self->priv;
GSource *source;
GST_DEBUG_OBJECT (self, "Enter loop");
g_main_context_push_thread_default (priv->main_context);
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL);
g_source_attach (source, priv->main_context);
g_source_unref (source);
priv->active_thread = g_thread_self ();
g_main_loop_run (priv->loop);
g_main_context_pop_thread_default (priv->main_context);
GST_DEBUG_OBJECT (self, "Exit loop");
return NULL;
}
/**
* gst_d3d11_device_new:
* @adapter: the index of adapter for creating d3d11 device (-1 for default)
*
* Returns: (transfer full) (nullable): a new #GstD3D11Device for @adapter or %NULL
* when failed to create D3D11 device with given adapter index.
*/
GstD3D11Device *
gst_d3d11_device_new (gint adapter)
{
GstD3D11Device *device = NULL;
GstD3D11DevicePrivate *priv;
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (gst_d3d11_device_debug, "d3d11device", 0,
"d3d11 device");
g_once_init_leave (&_init, 1);
}
device = g_object_new (GST_TYPE_D3D11_DEVICE, "adapter", adapter, NULL);
priv = device->priv;
if (!priv->device || !priv->device_context) {
GST_ERROR ("Cannot create d3d11 device");
g_object_unref (device);
device = NULL;
}
return device;
}
/**
* gst_d3d11_device_get_device:
* @device: a #GstD3D11Device
*
* Used for various D3D11 APIs directly.
* Caller must not destroy returned device object.
*
* Returns: (transfer none): the ID3D11Device
*/
ID3D11Device *
gst_d3d11_device_get_device (GstD3D11Device * device)
{
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
return device->priv->device;
}
/**
* gst_d3d11_device_get_device_context:
* @device: a #GstD3D11Device
*
* Used for various D3D11 APIs directly.
* Caller must not destroy returned device object.
*
* Returns: (transfer none): the ID3D11DeviceContext
*/
ID3D11DeviceContext *
gst_d3d11_device_get_device_context (GstD3D11Device * device)
{
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
return device->priv->device_context;
}
GstD3D11DXGIFactoryVersion
gst_d3d11_device_get_chosen_dxgi_factory_version (GstD3D11Device * device)
{
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device),
GST_D3D11_DXGI_FACTORY_UNKNOWN);
return device->priv->factory_ver;
}
typedef struct
{
GstD3D11Device *device;
GstD3D11DeviceThreadFunc func;
gpointer data;
gboolean fired;
} MessageData;
static gboolean
gst_d3d11_device_message_callback (MessageData * msg)
{
GstD3D11Device *self = msg->device;
GstD3D11DevicePrivate *priv = self->priv;
msg->func (self, msg->data);
g_mutex_lock (&priv->lock);
msg->fired = TRUE;
g_cond_signal (&priv->cond);
g_mutex_unlock (&priv->lock);
return G_SOURCE_REMOVE;
}
/**
* gst_d3d11_device_thread_add:
* @device: a #GstD3D11Device
* @func: (scope call): a #GstD3D11DeviceThreadFunc
* @data: (closure): user data to call @func with
*
* Execute @func in the D3DDevice thread of @device with @data
*
* MT-safe
*/
void
gst_d3d11_device_thread_add (GstD3D11Device * device,
GstD3D11DeviceThreadFunc func, gpointer data)
{
GstD3D11DevicePrivate *priv;
MessageData msg = { 0, };
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
g_return_if_fail (func != NULL);
priv = device->priv;
if (priv->active_thread == g_thread_self ()) {
func (device, data);
return;
}
msg.device = gst_object_ref (device);
msg.func = func;
msg.data = data;
msg.fired = FALSE;
g_main_context_invoke (priv->main_context,
(GSourceFunc) gst_d3d11_device_message_callback, &msg);
g_mutex_lock (&priv->lock);
while (!msg.fired)
g_cond_wait (&priv->cond, &priv->lock);
g_mutex_unlock (&priv->lock);
gst_object_unref (device);
}
typedef struct
{
IDXGISwapChain *swap_chain;
const DXGI_SWAP_CHAIN_DESC *desc;
} CreateSwapChainData;
static void
gst_d3d11_device_create_swap_chain_internal (GstD3D11Device * device,
CreateSwapChainData * data)
{
GstD3D11DevicePrivate *priv = device->priv;
HRESULT hr;
hr = IDXGIFactory1_CreateSwapChain (priv->factory, (IUnknown *) priv->device,
(DXGI_SWAP_CHAIN_DESC *) data->desc, &data->swap_chain);
if (FAILED (hr)) {
GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
(guint) hr);
data->swap_chain = NULL;
}
}
/**
* gst_d3d11_device_create_swap_chain:
* @device: a #GstD3D11Device
* @desc: a DXGI_SWAP_CHAIN_DESC structure for swapchain
*
* Creat a IDXGISwapChain object. Caller must release returned swap chain object
* via IDXGISwapChain_Release()
*
* Returns: (transfer full) (nullable): a new IDXGISwapChain or %NULL
* when failed to create swap chain with given @desc
*/
IDXGISwapChain *
gst_d3d11_device_create_swap_chain (GstD3D11Device * device,
const DXGI_SWAP_CHAIN_DESC * desc)
{
CreateSwapChainData data = { 0, };
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
data.swap_chain = NULL;
data.desc = desc;
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
gst_d3d11_device_create_swap_chain_internal, &data);
return data.swap_chain;
}
static void
gst_d3d11_device_release_swap_chain_internal (GstD3D11Device * device,
IDXGISwapChain * swap_chain)
{
IDXGISwapChain_Release (swap_chain);
}
/**
* gst_d3d11_device_release_swap_chain:
* @device: a #GstD3D11Device
* @swap_chain: a IDXGISwapChain
*
* Release a @swap_chain from device thread
*
*/
void
gst_d3d11_device_release_swap_chain (GstD3D11Device * device,
IDXGISwapChain * swap_chain)
{
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
gst_d3d11_device_thread_add (device,
(GstD3D11DeviceThreadFunc) gst_d3d11_device_release_swap_chain_internal,
swap_chain);
}
typedef struct
{
ID3D11Texture2D *texture;
const D3D11_TEXTURE2D_DESC *desc;
const D3D11_SUBRESOURCE_DATA *inital_data;
} CreateTextureData;
static void
gst_d3d11_device_create_texture_internal (GstD3D11Device * device,
CreateTextureData * data)
{
GstD3D11DevicePrivate *priv = device->priv;
HRESULT hr;
hr = ID3D11Device_CreateTexture2D (priv->device, data->desc,
data->inital_data, &data->texture);
if (FAILED (hr)) {
GST_ERROR ("Failed to create staging texture (0x%x)", (guint) hr);
data->texture = NULL;
}
}
ID3D11Texture2D *
gst_d3d11_device_create_texture (GstD3D11Device * device,
const D3D11_TEXTURE2D_DESC * desc,
const D3D11_SUBRESOURCE_DATA * inital_data)
{
CreateTextureData data;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
g_return_val_if_fail (desc != NULL, NULL);
data.texture = NULL;
data.desc = desc;
data.inital_data = inital_data;
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
gst_d3d11_device_create_texture_internal, &data);
return data.texture;
}
static void
gst_d3d11_device_release_texture_internal (GstD3D11Device * device,
ID3D11Texture2D * texture)
{
ID3D11Texture2D_Release (texture);
}
void
gst_d3d11_device_release_texture (GstD3D11Device * device,
ID3D11Texture2D * texture)
{
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
g_return_if_fail (texture != NULL);
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
gst_d3d11_device_release_texture_internal, texture);
}
/**
* gst_context_set_d3d11_device:
* @context: a #GstContext
* @device: (transfer none): resulting #GstD3D11Device
*
* Sets @device on @context
*/
void
gst_context_set_d3d11_device (GstContext * context, GstD3D11Device * device)
{
GstStructure *s;
const gchar *context_type;
g_return_if_fail (GST_IS_CONTEXT (context));
g_return_if_fail (GST_IS_D3D11_DEVICE (device));
context_type = gst_context_get_context_type (context);
if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
return;
GST_CAT_LOG (GST_CAT_CONTEXT,
"setting GstD3DDevice(%" GST_PTR_FORMAT ") on context(%" GST_PTR_FORMAT
")", device, context);
s = gst_context_writable_structure (context);
gst_structure_set (s, "device", GST_TYPE_D3D11_DEVICE, device, NULL);
}
/**
* gst_context_get_d3d11_device:
* @context: a #GstContext
* @device: (out) (transfer full): resulting #GstD3D11Device
*
* Returns: Whether @device was in @context
*/
gboolean
gst_context_get_d3d11_device (GstContext * context, GstD3D11Device ** device)
{
const GstStructure *s;
const gchar *context_type;
gboolean ret;
g_return_val_if_fail (GST_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (device != NULL, FALSE);
context_type = gst_context_get_context_type (context);
if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
return FALSE;
s = gst_context_get_structure (context);
ret = gst_structure_get (s, "device", GST_TYPE_D3D11_DEVICE, device, NULL);
GST_CAT_LOG (GST_CAT_CONTEXT, "got GstD3DDevice(%p) from context(%p)",
*device, context);
return ret;
}

113
sys/d3d11/gstd3d11device.h Normal file
View file

@ -0,0 +1,113 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_DEVICE_H__
#define __GST_D3D11_DEVICE_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_DEVICE (gst_d3d11_device_get_type())
#define GST_D3D11_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_DEVICE,GstD3D11Device))
#define GST_D3D11_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D11_DEVICE,GstD3D11DeviceClass))
#define GST_D3D11_DEVICE_GET_CLASS(obj) (GST_D3D11_DEVICE_CLASS(G_OBJECT_GET_CLASS(obj)))
#define GST_IS_D3D11_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_DEVICE))
#define GST_IS_D3D11_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_DEVICE))
#define GST_D3D11_DEVICE_CAST(obj) ((GstD3D11Device*)(obj))
#define GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE "gst.d3d11.device.handle"
/**
* GstD3D11DeviceThreadFunc:
* @device: a #GstD3D11Device
* @data: user data
*
* Represents a function to run in the D3D11 device thread with @device and @data
*/
typedef void (*GstD3D11DeviceThreadFunc) (GstD3D11Device * device, gpointer data);
typedef enum
{
GST_D3D11_DXGI_FACTORY_UNKNOWN = 0,
GST_D3D11_DXGI_FACTORY_1,
GST_D3D11_DXGI_FACTORY_2,
GST_D3D11_DXGI_FACTORY_3,
GST_D3D11_DXGI_FACTORY_4,
GST_D3D11_DXGI_FACTORY_5,
} GstD3D11DXGIFactoryVersion;
struct _GstD3D11Device
{
GstObject parent;
GstD3D11DevicePrivate *priv;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstD3D11DeviceClass
{
GstObjectClass parent_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_d3d11_device_get_type (void);
GstD3D11Device * gst_d3d11_device_new (gint adapter);
ID3D11Device * gst_d3d11_device_get_device (GstD3D11Device * device);
ID3D11DeviceContext * gst_d3d11_device_get_device_context (GstD3D11Device * device);
GstD3D11DXGIFactoryVersion gst_d3d11_device_get_chosen_dxgi_factory_version (GstD3D11Device * device);
IDXGISwapChain * gst_d3d11_device_create_swap_chain (GstD3D11Device * device,
const DXGI_SWAP_CHAIN_DESC * desc);
void gst_d3d11_device_release_swap_chain (GstD3D11Device * device,
IDXGISwapChain * swap_chain);
void gst_d3d11_device_thread_add (GstD3D11Device * device,
GstD3D11DeviceThreadFunc func,
gpointer data);
ID3D11Texture2D * gst_d3d11_device_create_texture (GstD3D11Device * device,
const D3D11_TEXTURE2D_DESC * desc,
const D3D11_SUBRESOURCE_DATA *inital_data);
void gst_d3d11_device_release_texture (GstD3D11Device * device,
ID3D11Texture2D * texture);
void gst_context_set_d3d11_device (GstContext * context,
GstD3D11Device * device);
gboolean gst_context_get_d3d11_device (GstContext * context,
GstD3D11Device ** device);
G_END_DECLS
#endif /* __GST_D3D11_DEVICE_H__ */

405
sys/d3d11/gstd3d11memory.c Normal file
View file

@ -0,0 +1,405 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstd3d11memory.h"
#include "gstd3d11device.h"
#include "gstd3d11utils.h"
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_allocator_debug);
#define GST_CAT_DEFAULT gst_d3d11_allocator_debug
GstD3D11AllocationParams *
gst_d3d11_allocation_params_new (GstAllocationParams * alloc_params,
GstVideoInfo * info, GstVideoAlignment * align)
{
GstD3D11AllocationParams *ret;
g_return_val_if_fail (alloc_params != NULL, NULL);
g_return_val_if_fail (info != NULL, NULL);
ret = g_new0 (GstD3D11AllocationParams, 1);
memcpy (&ret->info, info, sizeof (GstVideoInfo));
if (align) {
ret->align = *align;
} else {
gst_video_alignment_reset (&ret->align);
}
ret->desc.Width = GST_VIDEO_INFO_WIDTH (info);
ret->desc.Height = GST_VIDEO_INFO_HEIGHT (info);
ret->desc.MipLevels = 1;
ret->desc.ArraySize = 1;
ret->desc.Format =
gst_d3d11_dxgi_format_from_gst (GST_VIDEO_INFO_FORMAT (info));
ret->desc.SampleDesc.Count = 1;
ret->desc.SampleDesc.Quality = 0;
ret->desc.Usage = D3D11_USAGE_DEFAULT;
/* User must set proper BindFlags and MiscFlags manually */
return ret;
}
GstD3D11AllocationParams *
gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src)
{
GstD3D11AllocationParams *dst;
g_return_val_if_fail (src != NULL, NULL);
dst = g_new0 (GstD3D11AllocationParams, 1);
memcpy (dst, src, sizeof (GstD3D11AllocationParams));
return dst;
}
void
gst_d3d11_allocation_params_free (GstD3D11AllocationParams * params)
{
g_free (params);
}
G_DEFINE_BOXED_TYPE (GstD3D11AllocationParams, gst_d3d11_allocation_params,
(GBoxedCopyFunc) gst_d3d11_allocation_params_copy,
(GBoxedFreeFunc) gst_d3d11_allocation_params_free);
#define gst_d3d11_allocator_parent_class parent_class
G_DEFINE_TYPE (GstD3D11Allocator, gst_d3d11_allocator, GST_TYPE_ALLOCATOR);
static inline D3D11_MAP
_gst_map_flags_to_d3d11 (GstMapFlags flags)
{
if ((flags & GST_MAP_READWRITE) == GST_MAP_READWRITE)
return D3D11_MAP_READ_WRITE;
else if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
return D3D11_MAP_WRITE;
else if ((flags & GST_MAP_READ) == GST_MAP_READ)
return D3D11_MAP_READ;
else
g_assert_not_reached ();
return D3D11_MAP_READ;
}
static ID3D11Texture2D *
_create_staging_texture (GstD3D11Device * device,
const D3D11_TEXTURE2D_DESC * ref)
{
D3D11_TEXTURE2D_DESC desc = { 0, };
desc.Width = ref->Width;
desc.Height = ref->Height;
desc.MipLevels = 1;
desc.Format = ref->Format;
desc.SampleDesc.Count = 1;
desc.ArraySize = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
return gst_d3d11_device_create_texture (device, &desc, NULL);
}
typedef struct
{
GstD3D11Memory *mem;
D3D11_MAP map_flag;
gboolean ret;
} D3D11MapData;
static void
_map_cpu_access_data (GstD3D11Device * device, gpointer data)
{
D3D11MapData *map_data = (D3D11MapData *) data;
GstD3D11Memory *dmem = map_data->mem;
HRESULT hr;
ID3D11Resource *texture = (ID3D11Resource *) dmem->texture;
ID3D11Resource *staging = (ID3D11Resource *) dmem->staging;
ID3D11DeviceContext *device_context =
gst_d3d11_device_get_device_context (device);
ID3D11DeviceContext_CopySubresourceRegion (device_context,
staging, 0, 0, 0, 0, texture, 0, NULL);
hr = ID3D11DeviceContext_Map (device_context,
staging, 0, map_data->map_flag, 0, &dmem->map);
if (FAILED (hr)) {
GST_ERROR ("Failed to map staging texture (0x%x)", (guint) hr);
map_data->ret = FALSE;
}
map_data->ret = TRUE;
}
static gpointer
gst_d3d11_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
{
GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
if ((flags & GST_MAP_D3D11) == GST_MAP_D3D11)
return dmem->texture;
if (dmem->cpu_map_count == 0) {
D3D11MapData map_data;
GstD3D11Device *device = GST_D3D11_ALLOCATOR (mem->allocator)->device;
map_data.mem = dmem;
map_data.map_flag = _gst_map_flags_to_d3d11 (flags);
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
_map_cpu_access_data, &map_data);
if (!map_data.ret)
return NULL;
}
if ((flags & GST_MAP_WRITE) == GST_MAP_WRITE)
dmem->need_upload = TRUE;
dmem->cpu_map_count++;
return dmem->map.pData;
}
static void
_unmap_cpu_access_data (GstD3D11Device * device, gpointer data)
{
GstD3D11Memory *dmem = (GstD3D11Memory *) data;
ID3D11Resource *texture = (ID3D11Resource *) dmem->texture;
ID3D11Resource *staging = (ID3D11Resource *) dmem->staging;
ID3D11DeviceContext *device_context =
gst_d3d11_device_get_device_context (device);
ID3D11DeviceContext_Unmap (device_context, staging, 0);
if (dmem->need_upload) {
ID3D11DeviceContext_CopySubresourceRegion (device_context, texture,
0, 0, 0, 0, staging, 0, NULL);
}
dmem->need_upload = FALSE;
}
static void
gst_d3d11_memory_unmap_full (GstMemory * mem, GstMapInfo * info)
{
GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
GstD3D11Device *device = GST_D3D11_ALLOCATOR (mem->allocator)->device;
if ((info->flags & GST_MAP_D3D11) == GST_MAP_D3D11)
return;
dmem->cpu_map_count--;
if (dmem->cpu_map_count > 0)
return;
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
_unmap_cpu_access_data, dmem);
}
static GstMemory *
gst_d3d11_memory_share (GstMemory * mem, gssize offset, gssize size)
{
/* TODO: impl. */
return NULL;
}
static GstMemory *
gst_d3d11_allocator_dummy_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
g_return_val_if_reached (NULL);
}
static void
gst_d3d11_allocator_free (GstAllocator * allocator, GstMemory * mem)
{
GstD3D11Memory *dmem = (GstD3D11Memory *) mem;
GstD3D11Device *device = GST_D3D11_ALLOCATOR (allocator)->device;
if (dmem->texture)
gst_d3d11_device_release_texture (device, dmem->texture);
if (dmem->staging)
gst_d3d11_device_release_texture (device, dmem->staging);
g_slice_free (GstD3D11Memory, dmem);
}
static void
gst_d3d11_allocator_dispose (GObject * object)
{
GstD3D11Allocator *alloc = GST_D3D11_ALLOCATOR (object);
gst_clear_object (&alloc->device);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_d3d11_allocator_class_init (GstD3D11AllocatorClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
gobject_class->dispose = gst_d3d11_allocator_dispose;
allocator_class->alloc = gst_d3d11_allocator_dummy_alloc;
allocator_class->free = gst_d3d11_allocator_free;
GST_DEBUG_CATEGORY_INIT (gst_d3d11_allocator_debug, "d3d11allocator", 0,
"d3d11allocator object");
}
static void
gst_d3d11_allocator_init (GstD3D11Allocator * allocator)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
alloc->mem_type = GST_D3D11_MEMORY_NAME;
alloc->mem_map = gst_d3d11_memory_map;
alloc->mem_unmap_full = gst_d3d11_memory_unmap_full;
alloc->mem_share = gst_d3d11_memory_share;
/* fallback copy */
GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
}
GstD3D11Allocator *
gst_d3d11_allocator_new (GstD3D11Device * device)
{
GstD3D11Allocator *allocator;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
allocator = g_object_new (GST_TYPE_D3D11_ALLOCATOR, NULL);
allocator->device = gst_object_ref (device);
return allocator;
}
typedef struct _CalculateSizeData
{
ID3D11Texture2D *staging;
GstVideoInfo *info;
gsize offset[GST_VIDEO_MAX_PLANES];
gint stride[GST_VIDEO_MAX_PLANES];
gsize size;
gboolean ret;
} CalculateSizeData;
static void
_calculate_buffer_size (GstD3D11Device * device, CalculateSizeData * data)
{
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE map;
ID3D11DeviceContext *device_context =
gst_d3d11_device_get_device_context (device);
hr = ID3D11DeviceContext_Map (device_context,
(ID3D11Resource *) data->staging, 0, GST_MAP_READWRITE, 0, &map);
if (FAILED (hr)) {
GST_ERROR ("Failed to map staging texture (0x%x)", (guint) hr);
data->ret = FALSE;
return;
}
ID3D11DeviceContext_Unmap (device_context, (ID3D11Resource *) data->staging,
0);
data->ret = gst_d3d11_calculate_buffer_size (data->info,
map.RowPitch, data->offset, data->stride, &data->size);
}
GstMemory *
gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator,
GstD3D11AllocationParams * params)
{
GstD3D11Memory *mem;
GstD3D11Device *device;
GstAllocationParams *alloc_params;
gsize size, maxsize;
ID3D11Texture2D *texture = NULL;
ID3D11Texture2D *staging = NULL;
CalculateSizeData data;
gint i;
g_return_val_if_fail (GST_IS_D3D11_ALLOCATOR (allocator), NULL);
g_return_val_if_fail (params != NULL, NULL);
device = allocator->device;
texture = gst_d3d11_device_create_texture (device, &params->desc, NULL);
if (!texture) {
GST_ERROR_OBJECT (allocator, "Couldn't create texture");
goto error;
}
staging = _create_staging_texture (device, &params->desc);
if (!staging) {
GST_ERROR_OBJECT (allocator, "Couldn't create staging texture");
goto error;
}
/* try map staging texture to get actual stride and size */
memset (&data, 0, sizeof (CalculateSizeData));
data.staging = staging;
data.info = &params->info;
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
_calculate_buffer_size, &data);
if (!data.ret) {
GST_ERROR_OBJECT (allocator, "Couldn't calculate stride");
goto error;
}
alloc_params = (GstAllocationParams *) params;
maxsize = size = data.size;
maxsize += alloc_params->prefix + alloc_params->padding;
mem = g_slice_new0 (GstD3D11Memory);
gst_memory_init (GST_MEMORY_CAST (mem),
alloc_params->flags, GST_ALLOCATOR_CAST (allocator), NULL, maxsize,
alloc_params->align, 0, size);
mem->desc = params->desc;
mem->texture = texture;
mem->staging = staging;
for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
mem->offset[i] = data.offset[i];
mem->stride[i] = data.stride[i];
}
return GST_MEMORY_CAST (mem);
error:
if (texture)
gst_d3d11_device_release_texture (device, texture);
if (staging)
gst_d3d11_device_release_texture (device, staging);
return NULL;
}

123
sys/d3d11/gstd3d11memory.h Normal file
View file

@ -0,0 +1,123 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_MEMORY_H__
#define __GST_D3D11_MEMORY_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_ALLOCATION_PARAMS (gst_d3d11_allocation_params_get_type())
#define GST_TYPE_D3D11_ALLOCATOR (gst_d3d11_allocator_get_type())
#define GST_D3D11_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11Allocator))
#define GST_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass))
#define GST_IS_D3D11_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_ALLOCATOR))
#define GST_IS_D3D11_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_ALLOCATOR))
#define GST_D3D11_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_ALLOCATOR, GstD3D11AllocatorClass))
#define GST_D3D11_MEMORY_NAME "D3D11Memory"
/**
* GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY:
*
* Name of the caps feature for indicating the use of #GstD3D11Memory
*/
#define GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY "memory:D3D11Memory"
/**
* GST_MAP_D3D11:
*
* Flag indicating that we should map the D3D11 resource instead of to system memory.
*/
#define GST_MAP_D3D11 (GST_MAP_FLAG_LAST << 1)
struct _GstD3D11Memory
{
GstMemory mem;
GstMapFlags map_flags;
gint cpu_map_count;
ID3D11Texture2D *texture;
ID3D11Texture2D *staging;
D3D11_TEXTURE2D_DESC desc;
D3D11_MAPPED_SUBRESOURCE map;
gboolean need_upload;
gsize offset[GST_VIDEO_MAX_PLANES];
gint stride[GST_VIDEO_MAX_PLANES];
};
struct _GstD3D11AllocationParams
{
GstAllocationParams parent;
D3D11_TEXTURE2D_DESC desc;
GstVideoInfo info;
GstVideoAlignment align;
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE];
};
struct _GstD3D11Allocator
{
GstAllocator parent;
/*< private >*/
GstD3D11Device *device;
gpointer _gst_reserved[GST_PADDING];
};
struct _GstD3D11AllocatorClass
{
GstAllocatorClass allocator_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_d3d11_allocation_params_get_type (void);
GstD3D11AllocationParams * gst_d3d11_allocation_params_new (GstAllocationParams * alloc_params,
GstVideoInfo * info,
GstVideoAlignment * align);
GstD3D11AllocationParams * gst_d3d11_allocation_params_copy (GstD3D11AllocationParams * src);
void gst_d3d11_allocation_params_free (GstD3D11AllocationParams * parms);
GType gst_d3d11_allocator_get_type (void);
GstD3D11Allocator * gst_d3d11_allocator_new (GstD3D11Device *device);
GstMemory * gst_d3d11_allocator_alloc (GstD3D11Allocator * allocator,
GstD3D11AllocationParams * params);
G_END_DECLS
#endif /* __GST_D3D11_MEMORY_H__ */

429
sys/d3d11/gstd3d11utils.c Normal file
View file

@ -0,0 +1,429 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11utils.h"
#include "gstd3d11device.h"
GST_DEBUG_CATEGORY_STATIC (gst_d3d11_utils_debug);
GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
static GstDebugCategory *
_init_d3d11_utils_debug (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (gst_d3d11_utils_debug, "d3d11utils", 0,
"Direct3D11 Utilities");
g_once_init_leave (&_init, 1);
}
return gst_d3d11_utils_debug;
}
static void
_init_context_debug (void)
{
static volatile gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
g_once_init_leave (&_init, 1);
}
}
#define GST_CAT_DEFAULT _init_d3d11_utils_debug()
static const struct
{
GstVideoFormat gst_format;
DXGI_FORMAT dxgi_format;
} gst_dxgi_format_map[] = {
/* TODO: add more formats */
{
GST_VIDEO_FORMAT_BGRA, DXGI_FORMAT_B8G8R8A8_UNORM}, {
GST_VIDEO_FORMAT_RGBA, DXGI_FORMAT_R8G8B8A8_UNORM}, {
GST_VIDEO_FORMAT_RGB10A2_LE, DXGI_FORMAT_R10G10B10A2_UNORM}
};
GstVideoFormat
gst_d3d11_dxgi_format_to_gst (DXGI_FORMAT format)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
if (gst_dxgi_format_map[i].dxgi_format == format)
return gst_dxgi_format_map[i].gst_format;
}
return GST_VIDEO_FORMAT_UNKNOWN;
}
DXGI_FORMAT
gst_d3d11_dxgi_format_from_gst (GstVideoFormat format)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
if (gst_dxgi_format_map[i].gst_format == format)
return gst_dxgi_format_map[i].dxgi_format;
}
return DXGI_FORMAT_UNKNOWN;
}
typedef struct
{
GstCaps *caps;
D3D11_FORMAT_SUPPORT flags;
} SupportCapsData;
static void
gst_d3d11_device_get_supported_caps_internal (GstD3D11Device * device,
SupportCapsData * data)
{
ID3D11Device *d3d11_device;
HRESULT hr;
gint i;
GValue v_list = G_VALUE_INIT;
GstCaps *supported_caps;
d3d11_device = gst_d3d11_device_get_device (device);
g_value_init (&v_list, GST_TYPE_LIST);
for (i = 0; i < G_N_ELEMENTS (gst_dxgi_format_map); i++) {
UINT format_support = 0;
GstVideoFormat format = gst_dxgi_format_map[i].gst_format;
hr = ID3D11Device_CheckFormatSupport (d3d11_device,
gst_dxgi_format_map[i].dxgi_format, &format_support);
if (SUCCEEDED (hr) && ((format_support & data->flags) == data->flags)) {
GValue v_str = G_VALUE_INIT;
g_value_init (&v_str, G_TYPE_STRING);
GST_LOG_OBJECT (device, "d3d11 device can support %s with flags 0x%x",
gst_video_format_to_string (format), data->flags);
g_value_set_string (&v_str, gst_video_format_to_string (format));
gst_value_list_append_and_take_value (&v_list, &v_str);
}
}
supported_caps = gst_caps_new_simple ("video/x-raw",
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
gst_caps_set_value (supported_caps, "format", &v_list);
g_value_unset (&v_list);
data->caps = supported_caps;
}
/**
* gst_d3d11_device_get_supported_caps:
* @device: a #GstD3DDevice
* @flags: D3D11_FORMAT_SUPPORT flags
*
* Check supported format with given flags
*
* Returns: a #GstCaps representing supported format
*/
GstCaps *
gst_d3d11_device_get_supported_caps (GstD3D11Device * device,
D3D11_FORMAT_SUPPORT flags)
{
SupportCapsData data;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
data.caps = NULL;
data.flags = flags;
gst_d3d11_device_thread_add (device, (GstD3D11DeviceThreadFunc)
gst_d3d11_device_get_supported_caps_internal, &data);
return data.caps;
}
gboolean
gst_d3d11_calculate_buffer_size (GstVideoInfo * info, guint pitch,
gsize offset[GST_VIDEO_MAX_PLANES], gint stride[GST_VIDEO_MAX_PLANES],
gsize * size)
{
g_return_val_if_fail (info != NULL, FALSE);
switch (GST_VIDEO_INFO_FORMAT (info)) {
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_RGB10A2_LE:
offset[0] = 0;
stride[0] = pitch;
*size = pitch * GST_VIDEO_INFO_HEIGHT (info);
break;
case GST_VIDEO_FORMAT_NV12:
offset[0] = 0;
stride[0] = pitch;
offset[1] = offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
stride[1] = pitch;
*size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
break;
default:
return FALSE;
}
GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
" (%s %dx%d, Pitch %d)", *size,
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)),
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), pitch);
return TRUE;
}
/**
* gst_d3d11_handle_set_context:
* @element: a #GstElement
* @context: a #GstContext
* @device: (inout) (transfer full): location of a #GstD3DDevice
*
* Helper function for implementing #GstElementClass.set_context() in
* D3D11 capable elements.
*
* Retrieve's the #GstD3D11Device in @context and places the result in @device.
*
* Returns: whether the @device could be set successfully
*/
gboolean
gst_d3d11_handle_set_context (GstElement * element, GstContext * context,
GstD3D11Device ** device)
{
GstD3D11Device *device_replacement = NULL;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (device != NULL, FALSE);
if (!context)
return FALSE;
if (!gst_context_get_d3d11_device (context, &device_replacement))
return FALSE;
if (*device)
gst_object_unref (*device);
*device = device_replacement;
return TRUE;
}
/**
* gst_d3d11_handle_context_query:
* @element: a #GstElement
* @query: a #GstQuery of type %GST_QUERY_CONTEXT
* @device: (transfer none) (nullable): a #GstD3D11Device
*
* Returns: Whether the @query was successfully responded to from the passed
* @device.
*/
gboolean
gst_d3d11_handle_context_query (GstElement * element, GstQuery * query,
GstD3D11Device * device)
{
const gchar *context_type;
GstContext *context, *old_context;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
GST_LOG_OBJECT (element, "handle context query %" GST_PTR_FORMAT, query);
if (!device)
return FALSE;
gst_query_parse_context_type (query, &context_type);
if (g_strcmp0 (context_type, GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE) != 0)
return FALSE;
gst_query_parse_context (query, &old_context);
if (old_context)
context = gst_context_copy (old_context);
else
context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
gst_context_set_d3d11_device (context, device);
gst_query_set_context (query, context);
gst_context_unref (context);
GST_DEBUG_OBJECT (element, "successfully set %" GST_PTR_FORMAT
" on %" GST_PTR_FORMAT, device, query);
return TRUE;
}
static gboolean
pad_query (const GValue * item, GValue * value, gpointer user_data)
{
GstPad *pad = g_value_get_object (item);
GstQuery *query = user_data;
gboolean res;
res = gst_pad_peer_query (pad, query);
if (res) {
g_value_set_boolean (value, TRUE);
return FALSE;
}
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
return TRUE;
}
static gboolean
run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
{
GstIterator *it;
GstIteratorFoldFunction func = pad_query;
GValue res = { 0 };
g_value_init (&res, G_TYPE_BOOLEAN);
g_value_set_boolean (&res, FALSE);
/* Ask neighbor */
if (direction == GST_PAD_SRC)
it = gst_element_iterate_src_pads (element);
else
it = gst_element_iterate_sink_pads (element);
while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
gst_iterator_resync (it);
gst_iterator_free (it);
return g_value_get_boolean (&res);
}
static void
run_d3d11_context_query (GstElement * element)
{
GstQuery *query;
GstContext *ctxt;
/* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
* check if downstream already has a context of the specific type
* 2b) Query upstream as above.
*/
query = gst_query_new_context (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
if (run_query (element, query, GST_PAD_SRC)) {
gst_query_parse_context (query, &ctxt);
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
"found context (%" GST_PTR_FORMAT ") in downstream query", ctxt);
gst_element_set_context (element, ctxt);
} else if (run_query (element, query, GST_PAD_SINK)) {
gst_query_parse_context (query, &ctxt);
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
"found context (%" GST_PTR_FORMAT ") in upstream query", ctxt);
gst_element_set_context (element, ctxt);
} else {
/* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
* the required context type and afterwards check if a
* usable context was set now as in 1). The message could
* be handled by the parent bins of the element and the
* application.
*/
GstMessage *msg;
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
"posting need context message");
msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE);
gst_element_post_message (element, msg);
}
/*
* Whomever responds to the need-context message performs a
* GstElement::set_context() with the required context in which the element
* is required to update the display_ptr or call gst_gl_handle_set_context().
*/
gst_query_unref (query);
}
/**
* gst_d3d11_ensure_element_data:
* @element: the #GstElement running the query
* @device: (inout): the resulting #GstD3D11Device
* @preferred_adapter: the index of preferred adapter
*
* Perform the steps necessary for retrieving a #GstD3D11Device
* from the surrounding elements or from the application using the #GstContext mechanism.
*
* If the contents of @device is not %NULL, then no #GstContext query is
* necessary for #GstD3D11Device retrieval is performed.
*
* Returns: whether a #GstD3D11Device exists in @device
*/
gboolean
gst_d3d11_ensure_element_data (GstElement * element, GstD3D11Device ** device,
gint preferred_adapter)
{
GstD3D11Device *new_device;
GstContext *context;
g_return_val_if_fail (element != NULL, FALSE);
g_return_val_if_fail (device != NULL, FALSE);
_init_context_debug ();
if (*device) {
GST_LOG_OBJECT (element, "already have a device %" GST_PTR_FORMAT, *device);
return TRUE;
}
run_d3d11_context_query (element);
/* Neighbour found and it updated the devicey */
if (*device) {
return TRUE;
}
new_device = gst_d3d11_device_new (preferred_adapter);
if (!new_device) {
GST_ERROR_OBJECT (element,
"Couldn't create new device with adapter index %d", preferred_adapter);
return FALSE;
}
*device = new_device;
context = gst_context_new (GST_D3D11_DEVICE_HANDLE_CONTEXT_TYPE, TRUE);
gst_context_set_d3d11_device (context, new_device);
gst_element_set_context (element, context);
GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
"posting have context (%" GST_PTR_FORMAT
") message with device (%" GST_PTR_FORMAT ")", context, device);
gst_element_post_message (GST_ELEMENT_CAST (element),
gst_message_new_have_context (GST_OBJECT_CAST (element), context));
return TRUE;
}

57
sys/d3d11/gstd3d11utils.h Normal file
View file

@ -0,0 +1,57 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_UTILS_H__
#define __GST_D3D11_UTILS_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
G_BEGIN_DECLS
GstVideoFormat gst_d3d11_dxgi_format_to_gst (DXGI_FORMAT format);
DXGI_FORMAT gst_d3d11_dxgi_format_from_gst (GstVideoFormat format);
GstCaps * gst_d3d11_device_get_supported_caps (GstD3D11Device * device,
D3D11_FORMAT_SUPPORT flags);
gboolean gst_d3d11_calculate_buffer_size (GstVideoInfo * info,
guint pitch,
gsize offset[GST_VIDEO_MAX_PLANES],
gint stride[GST_VIDEO_MAX_PLANES],
gsize *size);
gboolean gst_d3d11_handle_set_context (GstElement * element,
GstContext * context,
GstD3D11Device ** device);
gboolean gst_d3d11_handle_context_query (GstElement * element,
GstQuery * query,
GstD3D11Device * device);
gboolean gst_d3d11_ensure_element_data (GstElement * element,
GstD3D11Device ** device,
gint preferred_adapter);
G_END_DECLS
#endif /* __GST_D3D11_UTILS_H__ */

View file

@ -0,0 +1,815 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11videosink.h"
#include "gstd3d11memory.h"
#include "gstd3d11utils.h"
#include "gstd3d11device.h"
#include "gstd3d11bufferpool.h"
enum
{
PROP_0,
PROP_ADAPTER,
PROP_FORCE_ASPECT_RATIO,
PROP_ENABLE_NAVIGATION_EVENTS
};
#define DEFAULT_ADAPTER -1
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw, "
"format = (string) { BGRA, RGBA, RGB10A2_LE }, "
"framerate = (fraction) [ 0, MAX ], "
"width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
);
GST_DEBUG_CATEGORY (d3d11_video_sink_debug);
#define GST_CAT_DEFAULT d3d11_video_sink_debug
static void gst_d3d11_videosink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_d3d11_videosink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_d3d11_video_sink_video_overlay_init (GstVideoOverlayInterface * iface);
static void
gst_d3d11_video_sink_navigation_init (GstNavigationInterface * iface);
static void gst_d3d11_video_sink_set_context (GstElement * element,
GstContext * context);
static GstCaps *gst_d3d11_video_sink_get_caps (GstBaseSink * sink,
GstCaps * filter);
static gboolean gst_d3d11_video_sink_set_caps (GstBaseSink * sink,
GstCaps * caps);
static gboolean gst_d3d11_video_sink_start (GstBaseSink * sink);
static gboolean gst_d3d11_video_sink_stop (GstBaseSink * sink);
static gboolean gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink,
GstQuery * query);
static GstFlowReturn
gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf);
#define gst_d3d11_video_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstD3D11VideoSink, gst_d3d11_video_sink,
GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
gst_d3d11_video_sink_video_overlay_init);
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
gst_d3d11_video_sink_navigation_init);
GST_DEBUG_CATEGORY_INIT (d3d11_video_sink_debug,
"d3d11videosink", 0, "Direct3D11 Video Sink"));
static void
gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
GstVideoSinkClass *videosink_class = GST_VIDEO_SINK_CLASS (klass);
gobject_class->set_property = gst_d3d11_videosink_set_property;
gobject_class->get_property = gst_d3d11_videosink_get_property;
g_object_class_install_property (gobject_class, PROP_ADAPTER,
g_param_spec_int ("adapter", "Adapter",
"Adapter index for creating device (-1 for default)",
-1, G_MAXINT32, DEFAULT_ADAPTER,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio",
"Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
g_param_spec_boolean ("enable-navigation-events",
"Enable navigation events",
"When enabled, navigation events are sent upstream",
DEFAULT_ENABLE_NAVIGATION_EVENTS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
element_class->set_context =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_context);
gst_element_class_set_static_metadata (element_class,
"Direct3D11 video sink", "Sink/Video",
"A Direct3D11 based videosink",
"Seungha Yang <seungha.yang@navercorp.com>");
gst_element_class_add_static_pad_template (element_class, &sink_template);
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_get_caps);
basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_set_caps);
basesink_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_start);
basesink_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_stop);
basesink_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_propose_allocation);
videosink_class->show_frame =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_show_frame);
}
static void
gst_d3d11_video_sink_init (GstD3D11VideoSink * self)
{
self->adapter = DEFAULT_ADAPTER;
self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
}
static void
gst_d3d11_videosink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (object);
GST_OBJECT_LOCK (self);
switch (prop_id) {
case PROP_ADAPTER:
self->adapter = g_value_get_int (value);
break;
case PROP_FORCE_ASPECT_RATIO:
self->force_aspect_ratio = g_value_get_boolean (value);
if (self->window)
g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio, NULL);
break;
case PROP_ENABLE_NAVIGATION_EVENTS:
self->enable_navigation_events = g_value_get_boolean (value);
if (self->window) {
g_object_set (self->window,
"enable-navigation-events", self->enable_navigation_events, NULL);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (self);
}
static void
gst_d3d11_videosink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (object);
switch (prop_id) {
case PROP_ADAPTER:
g_value_set_int (value, self->adapter);
break;
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, self->force_aspect_ratio);
break;
case PROP_ENABLE_NAVIGATION_EVENTS:
g_value_set_boolean (value, self->enable_navigation_events);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_video_sink_set_context (GstElement * element, GstContext * context)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (element);
gst_d3d11_handle_set_context (element, context, &self->device);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
static GstCaps *
gst_d3d11_video_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GstCaps *caps = NULL;
if (self->device)
caps = gst_d3d11_device_get_supported_caps (self->device,
D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY |
D3D11_FORMAT_SUPPORT_RENDER_TARGET);
if (!caps)
caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
if (caps && filter) {
GstCaps *isect;
isect = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = isect;
}
return caps;
}
static gboolean
gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GstCaps *sink_caps = NULL;
gint video_width, video_height;
gint video_par_n, video_par_d; /* video's PAR */
gint display_par_n = 1, display_par_d = 1; /* display's PAR */
guint num, den;
D3D11_TEXTURE2D_DESC desc = { 0, };
ID3D11Texture2D *staging;
GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
sink_caps = gst_d3d11_device_get_supported_caps (self->device,
D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY |
D3D11_FORMAT_SUPPORT_RENDER_TARGET);
GST_DEBUG_OBJECT (self, "supported caps %" GST_PTR_FORMAT, sink_caps);
if (!gst_caps_can_intersect (sink_caps, caps))
goto incompatible_caps;
gst_clear_caps (&sink_caps);
if (!gst_video_info_from_caps (&self->info, caps))
goto invalid_format;
video_width = GST_VIDEO_INFO_WIDTH (&self->info);
video_height = GST_VIDEO_INFO_HEIGHT (&self->info);
video_par_n = GST_VIDEO_INFO_PAR_N (&self->info);
video_par_d = GST_VIDEO_INFO_PAR_D (&self->info);
/* get aspect ratio from caps if it's present, and
* convert video width and height to a display width and height
* using wd / hd = wv / hv * PARv / PARd */
/* TODO: Get display PAR */
if (!gst_video_calculate_display_ratio (&num, &den, video_width,
video_height, video_par_n, video_par_d, display_par_n, display_par_d))
goto no_disp_ratio;
GST_DEBUG_OBJECT (sink,
"video width/height: %dx%d, calculated display ratio: %d/%d format: %s",
video_width, video_height, num, den,
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&self->info)));
/* now find a width x height that respects this display ratio.
* prefer those that have one of w/h the same as the incoming video
* using wd / hd = num / den
*/
/* start with same height, because of interlaced video
* check hd / den is an integer scale factor, and scale wd with the PAR
*/
if (video_height % den == 0) {
GST_DEBUG_OBJECT (self, "keeping video height");
GST_VIDEO_SINK_WIDTH (self) = (guint)
gst_util_uint64_scale_int (video_height, num, den);
GST_VIDEO_SINK_HEIGHT (self) = video_height;
} else if (video_width % num == 0) {
GST_DEBUG_OBJECT (self, "keeping video width");
GST_VIDEO_SINK_WIDTH (self) = video_width;
GST_VIDEO_SINK_HEIGHT (self) = (guint)
gst_util_uint64_scale_int (video_width, den, num);
} else {
GST_DEBUG_OBJECT (self, "approximating while keeping video height");
GST_VIDEO_SINK_WIDTH (self) = (guint)
gst_util_uint64_scale_int (video_height, num, den);
GST_VIDEO_SINK_HEIGHT (self) = video_height;
}
GST_DEBUG_OBJECT (self, "scaling to %dx%d",
GST_VIDEO_SINK_WIDTH (self), GST_VIDEO_SINK_HEIGHT (self));
if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
goto no_display_size;
self->dxgi_format =
gst_d3d11_dxgi_format_from_gst (GST_VIDEO_INFO_FORMAT (&self->info));
if (!self->window_id)
gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (self));
if (self->window_id) {
GST_DEBUG_OBJECT (self, "Set external window %" G_GUINTPTR_FORMAT,
(guintptr) self->window_id);
gst_d3d11_window_set_window_handle (self->window, self->window_id);
}
GST_OBJECT_LOCK (self);
if (!self->pending_render_rect) {
self->render_rect.x = 0;
self->render_rect.y = 0;
self->render_rect.w = GST_VIDEO_SINK_WIDTH (self);
self->render_rect.h = GST_VIDEO_SINK_HEIGHT (self);
}
gst_d3d11_window_set_render_rectangle (self->window,
self->render_rect.x, self->render_rect.y, self->render_rect.w,
self->render_rect.h);
self->pending_render_rect = FALSE;
if (!self->force_aspect_ratio) {
g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio, NULL);
}
GST_OBJECT_UNLOCK (self);
if (!gst_d3d11_window_prepare (self->window, GST_VIDEO_SINK_WIDTH (self),
GST_VIDEO_SINK_HEIGHT (self), self->dxgi_format, caps)) {
GST_ERROR_OBJECT (self, "cannot create swapchain");
return FALSE;
}
if (self->fallback_staging) {
gst_d3d11_device_release_texture (self->device, self->fallback_staging);
self->fallback_staging = NULL;
}
desc.Width = GST_VIDEO_SINK_WIDTH (self);
desc.Height = GST_VIDEO_SINK_HEIGHT (self);
desc.MipLevels = 1;
desc.Format = self->dxgi_format;
desc.SampleDesc.Count = 1;
desc.ArraySize = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
staging = gst_d3d11_device_create_texture (self->device, &desc, NULL);
if (!staging) {
GST_ERROR_OBJECT (self, "cannot create fallback staging texture");
return FALSE;
}
self->fallback_staging = staging;
return TRUE;
/* ERRORS */
incompatible_caps:
{
GST_ERROR_OBJECT (sink, "caps incompatible");
gst_clear_caps (&sink_caps);
return FALSE;
}
invalid_format:
{
GST_DEBUG_OBJECT (sink,
"Could not locate image format from caps %" GST_PTR_FORMAT, caps);
return FALSE;
}
no_disp_ratio:
{
GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
("Error calculating the output display ratio of the video."));
return FALSE;
}
no_display_size:
{
GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
("Error calculating the output display ratio of the video."));
return FALSE;
}
}
static void
gst_d3d11_video_sink_key_event (GstD3D11Window * window, const gchar * event,
const gchar * key, GstD3D11VideoSink * self)
{
if (self->enable_navigation_events) {
GST_LOG_OBJECT (self, "send key event %s, key %s", event, key);
gst_navigation_send_key_event (GST_NAVIGATION (self), event, key);
}
}
static void
gst_d3d11_video_mouse_key_event (GstD3D11Window * window, const gchar * event,
gint button, gdouble x, gdouble y, GstD3D11VideoSink * self)
{
if (self->enable_navigation_events) {
GST_LOG_OBJECT (self,
"send mouse event %s, button %d (%.1f, %.1f)", event, button, x, y);
gst_navigation_send_mouse_event (GST_NAVIGATION (self), event, button, x,
y);
}
}
static void
gst_d3d11_video_sink_got_window_handle (GstD3D11Window * window,
gpointer window_handle, GstD3D11VideoSink * self)
{
GST_LOG_OBJECT (self,
"got window handle %" G_GUINTPTR_FORMAT, (guintptr) window_handle);
gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (self),
(guintptr) window_handle);
}
static gboolean
gst_d3d11_video_sink_start (GstBaseSink * sink)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GST_DEBUG_OBJECT (self, "Start");
if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), &self->device,
self->adapter) || !self->device) {
GST_ERROR_OBJECT (sink, "Cannot create d3d11device");
return FALSE;
}
self->window = gst_d3d11_window_new (self->device);
if (!self->window) {
GST_ERROR_OBJECT (sink, "Cannot create d3d11window");
return FALSE;
}
g_object_set (self->window,
"enable-navigation-events", self->enable_navigation_events, NULL);
g_signal_connect (self->window, "key-event",
G_CALLBACK (gst_d3d11_video_sink_key_event), self);
g_signal_connect (self->window, "mouse-event",
G_CALLBACK (gst_d3d11_video_mouse_key_event), self);
g_signal_connect (self->window, "got-window-handle",
G_CALLBACK (gst_d3d11_video_sink_got_window_handle), self);
return TRUE;
}
static gboolean
gst_d3d11_video_sink_stop (GstBaseSink * sink)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GST_DEBUG_OBJECT (self, "Stop");
if (self->fallback_staging) {
ID3D11Texture2D_Release (self->fallback_staging);
self->fallback_staging = NULL;
}
gst_clear_object (&self->device);
gst_clear_object (&self->window);
return TRUE;
}
static gboolean
gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GstStructure *config;
GstCaps *caps;
GstBufferPool *pool = NULL;
GstVideoInfo info;
guint size;
gboolean need_pool;
if (!self->device || !self->window)
return FALSE;
gst_query_parse_allocation (query, &caps, &need_pool);
if (caps == NULL)
goto no_caps;
if (!gst_video_info_from_caps (&info, caps))
goto invalid_caps;
/* the normal size of a frame */
size = info.size;
if (need_pool) {
GST_DEBUG_OBJECT (self, "create new pool");
pool = (GstBufferPool *) gst_d3d11_buffer_pool_new (self->device);
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, 2,
DXGI_MAX_SWAP_CHAIN_BUFFERS);
if (!gst_buffer_pool_set_config (pool, config)) {
g_object_unref (pool);
goto config_failed;
}
}
gst_query_add_allocation_pool (query, pool, size, 2,
DXGI_MAX_SWAP_CHAIN_BUFFERS);
if (pool)
g_object_unref (pool);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
return TRUE;
/* ERRORS */
no_caps:
{
GST_WARNING_OBJECT (self, "no caps specified");
return FALSE;
}
invalid_caps:
{
GST_WARNING_OBJECT (self, "invalid caps specified");
return FALSE;
}
config_failed:
{
GST_WARNING_OBJECT (self, "failed setting config");
return FALSE;
}
return TRUE;
}
typedef struct
{
GstD3D11VideoSink *sink;
GstVideoFrame *frame;
ID3D11Resource *resource;
GstFlowReturn ret;
} FrameUploadData;
static void
_upload_frame (GstD3D11Device * device, gpointer data)
{
GstD3D11VideoSink *self;
HRESULT hr;
ID3D11DeviceContext *device_context;
FrameUploadData *upload_data = (FrameUploadData *) data;
D3D11_MAPPED_SUBRESOURCE d3d11_map;
guint i;
guint8 *dst;
self = upload_data->sink;
device_context = gst_d3d11_device_get_device_context (device);
hr = ID3D11DeviceContext_Map (device_context,
upload_data->resource, 0, D3D11_MAP_WRITE, 0, &d3d11_map);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "cannot map d3d11 staging texture");
upload_data->ret = GST_FLOW_ERROR;
return;
}
dst = d3d11_map.pData;
for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (upload_data->frame); i++) {
guint w, h;
guint j;
guint8 *src;
gint src_stride;
w = GST_VIDEO_FRAME_COMP_WIDTH (upload_data->frame, i) *
GST_VIDEO_FRAME_COMP_PSTRIDE (upload_data->frame, i);
h = GST_VIDEO_FRAME_COMP_HEIGHT (upload_data->frame, i);
src = GST_VIDEO_FRAME_PLANE_DATA (upload_data->frame, i);
src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (upload_data->frame, i);
for (j = 0; j < h; j++) {
memcpy (dst, src, w);
dst += d3d11_map.RowPitch;
src += src_stride;
}
}
ID3D11DeviceContext_Unmap (device_context, upload_data->resource, 0);
}
static GstFlowReturn
gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
GstVideoFrame frame;
FrameUploadData data;
ID3D11Texture2D *texture;
GstMapInfo map;
GstFlowReturn ret;
gboolean need_unmap = FALSE;
GstMemory *mem;
GstVideoRectangle rect = { 0, };
GstVideoCropMeta *crop;
if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
&& gst_memory_is_type (mem, GST_D3D11_MEMORY_NAME)) {
/* If this buffer has been allocated using our buffer management we simply
put the ximage which is in the PRIVATE pointer */
GST_TRACE_OBJECT (self, "buffer %p from our pool, writing directly", buf);
if (!gst_memory_map (mem, &map, (GST_MAP_READ | GST_MAP_D3D11))) {
GST_ERROR_OBJECT (self, "cannot map d3d11 memory");
return GST_FLOW_ERROR;
}
texture = (ID3D11Texture2D *) map.data;
need_unmap = TRUE;
} else {
if (!gst_video_frame_map (&frame, &self->info, buf, GST_MAP_READ)) {
GST_ERROR_OBJECT (self, "cannot map video frame");
return GST_FLOW_ERROR;
}
GST_TRACE_OBJECT (self,
"buffer %p out of our pool, write to stage buffer", buf);
data.sink = self;
data.frame = &frame;
data.resource = (ID3D11Resource *) self->fallback_staging;
data.ret = GST_FLOW_OK;
gst_d3d11_device_thread_add (self->device, (GstD3D11DeviceThreadFunc)
_upload_frame, &data);
if (data.ret != GST_FLOW_OK)
return data.ret;
gst_video_frame_unmap (&frame);
texture = self->fallback_staging;
}
gst_d3d11_window_show (self->window);
crop = gst_buffer_get_video_crop_meta (buf);
if (crop) {
rect.x = crop->x;
rect.y = crop->y;
rect.w = crop->width;
rect.h = crop->height;
} else {
rect.w = GST_VIDEO_SINK_WIDTH (self);
rect.h = GST_VIDEO_SINK_HEIGHT (self);
}
ret = gst_d3d11_window_render (self->window, texture, &rect);
if (need_unmap)
gst_memory_unmap (mem, &map);
if (ret == GST_D3D11_WINDOW_FLOW_CLOSED) {
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
("Output window was closed"), (NULL));
ret = GST_FLOW_ERROR;
}
return ret;
}
/* VideoOverlay interface */
static void
gst_d3d11_video_sink_set_window_handle (GstVideoOverlay * overlay,
guintptr window_id)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
GST_DEBUG ("set window handle %" G_GUINTPTR_FORMAT, window_id);
self->window_id = window_id;
}
static void
gst_d3d11_video_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
gint y, gint width, gint height)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
GST_DEBUG_OBJECT (self,
"render rect x: %d, y: %d, width: %d, height %d", x, y, width, height);
GST_OBJECT_LOCK (self);
if (self->window) {
gst_d3d11_window_set_render_rectangle (self->window, x, y, width, height);
} else {
self->render_rect.x = x;
self->render_rect.y = y;
self->render_rect.w = width;
self->render_rect.h = height;
self->pending_render_rect = TRUE;
}
GST_OBJECT_UNLOCK (self);
}
static void
gst_d3d11_video_sink_expose (GstVideoOverlay * overlay)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (overlay);
if (self->window && self->window->swap_chain) {
GstVideoRectangle rect = { 0, };
rect.w = GST_VIDEO_SINK_WIDTH (self);
rect.h = GST_VIDEO_SINK_HEIGHT (self);
gst_d3d11_window_render (self->window, NULL, &rect);
}
}
static void
gst_d3d11_video_sink_video_overlay_init (GstVideoOverlayInterface * iface)
{
iface->set_window_handle = gst_d3d11_video_sink_set_window_handle;
iface->set_render_rectangle = gst_d3d11_video_sink_set_render_rectangle;
iface->expose = gst_d3d11_video_sink_expose;
}
/* Navigation interface */
static void
gst_d3d11_video_sink_navigation_send_event (GstNavigation * navigation,
GstStructure * structure)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (navigation);
gboolean handled = FALSE;
GstEvent *event = NULL;
GstVideoRectangle src = { 0, };
GstVideoRectangle dst = { 0, };
GstVideoRectangle result;
gdouble x, y, xscale = 1.0, yscale = 1.0;
if (!self->window) {
gst_structure_free (structure);
return;
}
if (self->force_aspect_ratio) {
/* We get the frame position using the calculated geometry from _setcaps
that respect pixel aspect ratios */
src.w = GST_VIDEO_SINK_WIDTH (self);
src.h = GST_VIDEO_SINK_HEIGHT (self);
dst.w = self->render_rect.w;
dst.h = self->render_rect.h;
gst_video_sink_center_rect (src, dst, &result, TRUE);
result.x += self->render_rect.x;
result.y += self->render_rect.y;
} else {
memcpy (&result, &self->render_rect, sizeof (GstVideoRectangle));
}
xscale = (gdouble) GST_VIDEO_INFO_WIDTH (&self->info) / result.w;
yscale = (gdouble) GST_VIDEO_INFO_HEIGHT (&self->info) / result.h;
/* Converting pointer coordinates to the non scaled geometry */
if (gst_structure_get_double (structure, "pointer_x", &x)) {
x = MIN (x, result.x + result.w);
x = MAX (x - result.x, 0);
gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
(gdouble) x * xscale, NULL);
}
if (gst_structure_get_double (structure, "pointer_y", &y)) {
y = MIN (y, result.y + result.h);
y = MAX (y - result.y, 0);
gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
(gdouble) y * yscale, NULL);
}
event = gst_event_new_navigation (structure);
if (event) {
gst_event_ref (event);
handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (self), event);
if (!handled)
gst_element_post_message (GST_ELEMENT_CAST (self),
gst_navigation_message_new_event (GST_OBJECT_CAST (self), event));
gst_event_unref (event);
}
}
static void
gst_d3d11_video_sink_navigation_init (GstNavigationInterface * iface)
{
iface->send_event = gst_d3d11_video_sink_navigation_send_event;
}

View file

@ -0,0 +1,77 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_VIDEO_SINK_H__
#define __GST_D3D11_VIDEO_SINK_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/videooverlay.h>
#include <gst/video/navigation.h>
#include "gstd3d11_fwd.h"
#include "gstd3d11window.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_VIDEO_SINK (gst_d3d11_video_sink_get_type())
#define GST_D3D11_VIDEO_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_VIDEO_SINK,GstD3D11VideoSink))
#define GST_D3D11_VIDEO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3D11_VIDEO_SINK,GstD3D11VideoSinkClass))
#define GST_D3D11_VIDEO_SINK_GET_CLASS(obj) (GST_D3D11_VIDEO_SINK_CLASS(G_OBJECT_GET_CLASS(obj)))
#define GST_IS_D3D11_VIDEO_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_VIDEO_SINK))
#define GST_IS_D3D11_VIDEO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3D11_VIDEO_SINK))
typedef struct _GstD3D11VideoSink GstD3D11VideoSink;
typedef struct _GstD3D11VideoSinkClass GstD3D11VideoSinkClass;
struct _GstD3D11VideoSink
{
GstVideoSink sink;
GstD3D11Device *device;
GstD3D11Window *window;
GstVideoInfo info;
DXGI_FORMAT dxgi_format;
guintptr window_id;
/* properties */
gint adapter;
gboolean force_aspect_ratio;
gboolean enable_navigation_events;
/* saved render rectangle until we have a window */
GstVideoRectangle render_rect;
gboolean pending_render_rect;
ID3D11Texture2D *fallback_staging;
};
struct _GstD3D11VideoSinkClass
{
GstVideoSinkClass parent_class;
};
GType gst_d3d11_video_sink_get_type (void);
G_END_DECLS
#endif /* __GST_D3D11_VIDEO_SINK_H__ */

1072
sys/d3d11/gstd3d11window.c Normal file

File diff suppressed because it is too large Load diff

125
sys/d3d11/gstd3d11window.h Normal file
View file

@ -0,0 +1,125 @@
/*
* GStreamer
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_WINDOW_H__
#define __GST_D3D11_WINDOW_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_WINDOW (gst_d3d11_window_get_type())
#define GST_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_WINDOW, GstD3D11Window))
#define GST_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass))
#define GST_IS_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_WINDOW))
#define GST_IS_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_WINDOW))
#define GST_D3D11_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass))
typedef struct _GstD3D11Window GstD3D11Window;
typedef struct _GstD3D11WindowClass GstD3D11WindowClass;
#define GST_D3D11_WINDOW_FLOW_CLOSED GST_FLOW_CUSTOM_ERROR
struct _GstD3D11Window
{
GstObject parent;
GstVideoInfo info;
GstVideoMasteringDisplayInfo mastering_display_info;
GstVideoContentLightLevel content_light_level;
GstVideoRectangle render_rect;
GMutex lock;
GCond cond;
GMainContext *main_context;
GMainLoop *loop;
guint width;
guint height;
guint surface_width;
guint surface_height;
gboolean visible;
GSource *msg_source;
GIOChannel *msg_io_channel;
GThread *thread;
gboolean created;
HWND internal_win_id;
HWND external_win_id;
HDC device_handle;
IDXGISwapChain *swap_chain;
ID3D11Texture2D *backbuffer;
ID3D11RenderTargetView *rtv;
DXGI_FORMAT format;
GstD3D11Device *device;
gboolean pending_resize;
gboolean force_aspect_ratio;
gboolean enable_navigation_events;
};
struct _GstD3D11WindowClass
{
GstObjectClass object_class;
};
GType gst_d3d11_window_get_type (void);
GstD3D11Window * gst_d3d11_window_new (GstD3D11Device * device);
void gst_d3d11_window_show (GstD3D11Window * window);
void gst_d3d11_window_set_window_handle (GstD3D11Window * window,
guintptr id);
void gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
gint x, gint y,
gint width, gint height);
void gst_d3d11_window_get_surface_dimensions (GstD3D11Window * window,
guint * width,
guint * height);
gboolean gst_d3d11_window_prepare (GstD3D11Window * window,
guint width,
guint height,
DXGI_FORMAT format,
GstCaps * caps);
GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window,
ID3D11Texture2D * texture,
GstVideoRectangle * src_rect);
G_END_DECLS
#endif /* __GST_D3D11_WINDOW_H__ */

51
sys/d3d11/meson.build Normal file
View file

@ -0,0 +1,51 @@
d3d11_sources = [
'gstd3d11bufferpool.c',
'gstd3d11device.c',
'gstd3d11memory.c',
'gstd3d11utils.c',
'gstd3d11videosink.c',
'gstd3d11window.c',
'plugin.c',
]
have_d3d11 = false
extra_c_args = []
extra_dep = []
d3d11_option = get_option('d3d11')
if host_system != 'windows' or d3d11_option.disabled()
subdir_done()
endif
d3d11_lib = cc.find_library('d3d11', required : d3d11_option)
dxgi_lib = cc.find_library('dxgi', required : d3d11_option)
have_d3d11 = d3d11_lib.found() and dxgi_lib.found() and cc.has_header('d3d11.h') and cc.has_header('dxgi.h')
if not have_d3d11
if d3d11_option.enabled()
error('The d3d11 plugin was enabled explicitly, but required dependencies were not found.')
endif
subdir_done()
endif
# required for HDR meatadata
if cc.has_header('dxgi1_5.h')
extra_c_args += ['-DHAVE_DXGI_1_5_H']
endif
# for enabling debug layer
if cc.has_header('d3d11sdklayers.h')
extra_c_args += ['-DHAVE_D3D11SDKLAYER_H']
extra_dep += [gmodule_dep]
endif
gstd3d11 = library('gstd3d11',
d3d11_sources,
c_args : gst_plugins_bad_args + extra_c_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep, gstallocators_dep, d3d11_lib, dxgi_lib] + extra_dep,
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstd3d11, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstd3d11]

38
sys/d3d11/plugin.c Normal file
View file

@ -0,0 +1,38 @@
/* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include "gstd3d11videosink.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin,
"d3d11videosink", GST_RANK_SECONDARY - 1, GST_TYPE_D3D11_VIDEO_SINK);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
d3d11,
"Direct3D11 plugin",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -1,6 +1,7 @@
subdir('androidmedia')
subdir('applemedia')
subdir('bluez')
subdir('d3d11')
subdir('d3dvideosink')
subdir('decklink')
subdir('directsound')