d3d11: Add support for Universal Windows Platform

Initial UWP support via new window (CoreWindow and SwapChainPanel) implementation.
This commit is contained in:
Seungha Yang 2019-12-30 18:58:59 +09:00
parent 43a8eb9e92
commit e4daa2ef43
16 changed files with 3184 additions and 1718 deletions

View file

@ -21,7 +21,7 @@
#define __GST_D3D11_FWD_H__
#include <gst/gst.h>
#include "d3d11config.h"
#include "gstd3d11config.h"
#ifndef INITGUID
#include <initguid.h>

View file

@ -21,8 +21,6 @@
#include "config.h"
#endif
#include "d3d11config.h"
#include "gstd3d11device.h"
#include "gstd3d11utils.h"
#include "gmodule.h"
@ -200,20 +198,39 @@ static gboolean
gst_d3d11_device_enable_dxgi_debug (void)
{
static volatile gsize _init = 0;
gboolean ret = FALSE;
/* If all below libraries are unavailable, d3d11 device would fail with
* D3D11_CREATE_DEVICE_DEBUG flag */
if (g_once_init_enter (&_init)) {
#if (!GST_D3D11_WINAPI_ONLY_APP)
dxgi_debug_module = g_module_open ("dxgidebug.dll", G_MODULE_BIND_LAZY);
if (dxgi_debug_module)
g_module_symbol (dxgi_debug_module,
"DXGIGetDebugInterface", (gpointer *) & GstDXGIGetDebugInterface);
ret = ! !GstDXGIGetDebugInterface;
#elif (DXGI_HEADER_VERSION >= 3)
ret = TRUE;
#endif
g_once_init_leave (&_init, 1);
}
return ! !GstDXGIGetDebugInterface;
return ret;
}
static HRESULT
gst_d3d11_device_dxgi_get_device_interface (REFIID riid, void **debug)
{
#if (!GST_D3D11_WINAPI_ONLY_APP)
if (GstDXGIGetDebugInterface) {
return GstDXGIGetDebugInterface (riid, debug);
}
#elif (DXGI_HEADER_VERSION >= 3)
return DXGIGetDebugInterface1 (0, riid, debug);
#endif
return E_NOINTERFACE;
}
static inline GstDebugLevel
@ -374,14 +391,15 @@ gst_d3d11_device_constructed (GObject * object)
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
"dxgi debug library was loaded");
hr = GstDXGIGetDebugInterface (&IID_IDXGIDebug, (void **) &debug);
hr = gst_d3d11_device_dxgi_get_device_interface (&IID_IDXGIDebug,
(void **) &debug);
if (SUCCEEDED (hr)) {
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
"IDXGIDebug interface available");
priv->dxgi_debug = debug;
hr = GstDXGIGetDebugInterface (&IID_IDXGIInfoQueue,
hr = gst_d3d11_device_dxgi_get_device_interface (&IID_IDXGIInfoQueue,
(void **) &info_queue);
if (SUCCEEDED (hr)) {
GST_CAT_INFO_OBJECT (gst_d3d11_debug_layer_debug, self,
@ -762,7 +780,7 @@ gst_d3d11_device_create_swap_chain (GstD3D11Device * device,
gst_d3d11_device_unlock (device);
if (!gst_d3d11_result (hr, device)) {
GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
(guint) hr);
swap_chain = NULL;
}
@ -771,6 +789,7 @@ gst_d3d11_device_create_swap_chain (GstD3D11Device * device,
}
#if (DXGI_HEADER_VERSION >= 2)
#if (!GST_D3D11_WINAPI_ONLY_APP)
/**
* gst_d3d11_device_create_swap_chain_for_hwnd:
* @device: a #GstD3D11Device
@ -807,14 +826,96 @@ gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Device * device,
gst_d3d11_device_unlock (device);
if (!gst_d3d11_result (hr, device)) {
GST_ERROR_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
(guint) hr);
swap_chain = NULL;
}
return swap_chain;
}
#endif
#endif /* GST_D3D11_WINAPI_ONLY_APP */
#if GST_D3D11_WINAPI_ONLY_APP
/**
* gst_d3d11_device_create_swap_chain_for_core_window:
* @device: a #GstD3D11Device
* @core_window: CoreWindow handle
* @desc: a DXGI_SWAP_CHAIN_DESC1 structure for swapchain
* @output: (nullable): a IDXGIOutput interface for the output to restrict content to
*
* Create a IDXGISwapChain1 object. Caller must release returned swap chain object
* via IDXGISwapChain1_Release()
*
* Returns: (transfer full) (nullable): a new IDXGISwapChain1 or %NULL
* when failed to create swap chain with given @desc
*/
IDXGISwapChain1 *
gst_d3d11_device_create_swap_chain_for_core_window (GstD3D11Device * device,
guintptr core_window, const DXGI_SWAP_CHAIN_DESC1 * desc,
IDXGIOutput * output)
{
GstD3D11DevicePrivate *priv;
IDXGISwapChain1 *swap_chain = NULL;
HRESULT hr;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
priv = device->priv;
gst_d3d11_device_lock (device);
hr = IDXGIFactory2_CreateSwapChainForCoreWindow ((IDXGIFactory2 *)
priv->factory, (IUnknown *) priv->device, (IUnknown *) core_window, desc,
output, &swap_chain);
gst_d3d11_device_unlock (device);
if (!gst_d3d11_result (hr, device)) {
GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
(guint) hr);
swap_chain = NULL;
}
return swap_chain;
}
/**
* gst_d3d11_device_create_swap_chain_for_composition:
* @device: a #GstD3D11Device
* @desc: a DXGI_SWAP_CHAIN_DESC1 structure for swapchain
* @output: (nullable): a IDXGIOutput interface for the output to restrict content to
*
* Create a IDXGISwapChain1 object. Caller must release returned swap chain object
* via IDXGISwapChain1_Release()
*
* Returns: (transfer full) (nullable): a new IDXGISwapChain1 or %NULL
* when failed to create swap chain with given @desc
*/
IDXGISwapChain1 *
gst_d3d11_device_create_swap_chain_for_composition (GstD3D11Device * device,
const DXGI_SWAP_CHAIN_DESC1 * desc, IDXGIOutput * output)
{
GstD3D11DevicePrivate *priv;
IDXGISwapChain1 *swap_chain = NULL;
HRESULT hr;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
priv = device->priv;
gst_d3d11_device_lock (device);
hr = IDXGIFactory2_CreateSwapChainForComposition ((IDXGIFactory2 *)
priv->factory, (IUnknown *) priv->device, desc, output, &swap_chain);
gst_d3d11_device_unlock (device);
if (!gst_d3d11_result (hr, device)) {
GST_WARNING_OBJECT (device, "Cannot create SwapChain Object: 0x%x",
(guint) hr);
swap_chain = NULL;
}
return swap_chain;
}
#endif /* GST_D3D11_WINAPI_ONLY_APP */
#endif /* (DXGI_HEADER_VERSION >= 2) */
/**
* gst_d3d11_device_release_swap_chain:

View file

@ -91,6 +91,7 @@ IDXGISwapChain * gst_d3d11_device_create_swap_chain (GstD3D11Device * devi
const DXGI_SWAP_CHAIN_DESC * desc);
#if (DXGI_HEADER_VERSION >= 2)
#if (!GST_D3D11_WINAPI_ONLY_APP)
IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Device * device,
HWND hwnd,
const DXGI_SWAP_CHAIN_DESC1 * desc,
@ -98,6 +99,19 @@ IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_hwnd (GstD3D11Devic
IDXGIOutput * output);
#endif
#if GST_D3D11_WINAPI_ONLY_APP
IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_core_window (GstD3D11Device * device,
guintptr core_window,
const DXGI_SWAP_CHAIN_DESC1 * desc,
IDXGIOutput * output);
IDXGISwapChain1 * gst_d3d11_device_create_swap_chain_for_composition (GstD3D11Device * device,
const DXGI_SWAP_CHAIN_DESC1 * desc,
IDXGIOutput * output);
#endif /* GST_D3D11_WINAPI_ONLY_APP */
#endif /* (DXGI_HEADER_VERSION >= 2) */
void gst_d3d11_device_release_swap_chain (GstD3D11Device * device,
IDXGISwapChain * swap_chain);

View file

@ -21,8 +21,6 @@
#include "config.h"
#endif
#include "d3d11config.h"
#include "gstd3d11utils.h"
#include "gstd3d11device.h"
@ -331,8 +329,12 @@ gst_d3d11_is_windows_8_or_greater (void)
static gboolean ret = FALSE;
if (g_once_init_enter (&version_once)) {
#if (!GST_D3D11_WINAPI_ONLY_APP)
if (IsWindows8OrGreater ())
ret = TRUE;
#else
ret = TRUE;
#endif
g_once_init_leave (&version_once, 1);
}

View file

@ -28,6 +28,13 @@
#include "gstd3d11bufferpool.h"
#include "gstd3d11format.h"
#if GST_D3D11_WINAPI_ONLY_APP
#include "gstd3d11window_corewindow.h"
#include "gstd3d11window_swapchainpanel.h"
#else
#include "gstd3d11window_win32.h"
#endif
enum
{
PROP_0,
@ -82,6 +89,7 @@ static gboolean gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink,
static gboolean gst_d3d11_video_sink_query (GstBaseSink * sink,
GstQuery * query);
static gboolean gst_d3d11_video_sink_unlock (GstBaseSink * sink);
static gboolean gst_d3d11_video_sink_unlock_stop (GstBaseSink * sink);
static GstFlowReturn
gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf);
@ -159,6 +167,8 @@ gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_propose_allocation);
basesink_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_query);
basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_unlock);
basesink_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_unlock_stop);
videosink_class->show_frame =
GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_show_frame);
@ -362,15 +372,6 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
goto no_display_size;
if (!self->window_id)
gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (self));
if (self->window_id) {
GST_DEBUG_OBJECT (self, "Set external window %" G_GUINTPTR_FORMAT,
(guintptr) self->window_id);
gst_d3d11_window_set_window_handle (self->window, self->window_id);
}
GST_OBJECT_LOCK (self);
if (!self->pending_render_rect) {
self->render_rect.x = 0;
@ -383,12 +384,6 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
self->render_rect.x, self->render_rect.y, self->render_rect.w,
self->render_rect.h);
self->pending_render_rect = FALSE;
g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio,
"fullscreen-toggle-mode", self->fullscreen_toggle_mode,
"fullscreen", self->fullscreen, NULL);
GST_OBJECT_UNLOCK (self);
if (!gst_d3d11_window_prepare (self->window, GST_VIDEO_SINK_WIDTH (self),
@ -484,21 +479,12 @@ gst_d3d11_video_mouse_key_event (GstD3D11Window * window, const gchar * event,
}
}
static void
gst_d3d11_video_sink_got_window_handle (GstD3D11Window * window,
gpointer window_handle, GstD3D11VideoSink * self)
{
GST_LOG_OBJECT (self,
"got window handle %" G_GUINTPTR_FORMAT, (guintptr) window_handle);
gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (self),
(guintptr) window_handle);
}
static gboolean
gst_d3d11_video_sink_start (GstBaseSink * sink)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
gboolean is_hardware = TRUE;
GstD3D11WindowNativeType window_type = GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
GST_DEBUG_OBJECT (self, "Start");
@ -508,12 +494,67 @@ gst_d3d11_video_sink_start (GstBaseSink * sink)
return FALSE;
}
self->window = gst_d3d11_window_new (self->device);
if (!self->window_id)
gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (self));
if (self->window_id) {
window_type =
gst_d3d11_window_get_native_type_from_handle (self->window_id);
if (window_type != GST_D3D11_WINDOW_NATIVE_TYPE_NONE) {
gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (self),
self->window_id);
}
}
GST_DEBUG_OBJECT (self, "Create window (type: %s)",
gst_d3d11_window_get_native_type_to_string (window_type));
#if GST_D3D11_WINAPI_ONLY_APP
if (window_type != GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW &&
window_type != GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL) {
GST_ERROR_OBJECT (sink, "Overlay handle must be set before READY state");
return FALSE;
}
#endif
switch (window_type) {
#if (!GST_D3D11_WINAPI_ONLY_APP)
case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
self->window = gst_d3d11_window_win32_new (self->device, self->window_id);
break;
#else
case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
self->window = gst_d3d11_window_core_window_new (self->device,
self->window_id);
break;
case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
self->window = gst_d3d11_window_swap_chain_panel_new (self->device,
self->window_id);
break;
#endif
default:
break;
}
if (!self->window) {
GST_ERROR_OBJECT (sink, "Cannot create d3d11window");
return FALSE;
}
GST_OBJECT_LOCK (self);
g_object_set (self->window,
"force-aspect-ratio", self->force_aspect_ratio,
"fullscreen-toggle-mode", self->fullscreen_toggle_mode,
"fullscreen", self->fullscreen,
"enable-navigation-events", self->enable_navigation_events, NULL);
GST_OBJECT_UNLOCK (self);
g_signal_connect (self->window, "key-event",
G_CALLBACK (gst_d3d11_video_sink_key_event), self);
g_signal_connect (self->window, "mouse-event",
G_CALLBACK (gst_d3d11_video_mouse_key_event), self);
g_object_get (self->device, "hardware", &is_hardware, NULL);
if (!is_hardware) {
@ -523,16 +564,6 @@ gst_d3d11_video_sink_start (GstBaseSink * sink)
self->can_convert = TRUE;
}
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;
}
@ -688,7 +719,18 @@ gst_d3d11_video_sink_unlock (GstBaseSink * sink)
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
if (self->window)
gst_d3d11_window_flush (self->window);
gst_d3d11_window_unlock (self->window);
return TRUE;
}
static gboolean
gst_d3d11_video_sink_unlock_stop (GstBaseSink * sink)
{
GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
if (self->window)
gst_d3d11_window_unlock_stop (self->window);
return TRUE;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,940 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11window.h"
#include "gstd3d11device.h"
#include "gstd3d11memory.h"
#include "gstd3d11utils.h"
#if GST_D3D11_WINAPI_ONLY_APP
/* workaround for GetCurrentTime collision */
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
#include <windows.ui.xaml.h>
#include <windows.applicationmodel.core.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
using namespace Microsoft::WRL;
#endif
extern "C" {
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
#define GST_CAT_DEFAULT gst_d3d11_window_debug
}
enum
{
PROP_0,
PROP_D3D11_DEVICE,
PROP_FORCE_ASPECT_RATIO,
PROP_ENABLE_NAVIGATION_EVENTS,
PROP_FULLSCREEN_TOGGLE_MODE,
PROP_FULLSCREEN,
PROP_WINDOW_HANDLE,
};
#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
#define DEFAULT_FULLSCREEN FALSE
enum
{
SIGNAL_KEY_EVENT,
SIGNAL_MOUSE_EVENT,
SIGNAL_LAST
};
static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
GType
gst_d3d11_window_fullscreen_toggle_mode_type (void)
{
static volatile gsize mode_type = 0;
if (g_once_init_enter (&mode_type)) {
static const GFlagsValue mode_types[] = {
{GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE,
"GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"},
{GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER,
"GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"},
{GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY,
"GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"},
{0, NULL, NULL},
};
GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
mode_types);
g_once_init_leave (&mode_type, tmp);
}
return (GType) mode_type;
}
#define gst_d3d11_window_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_d3d11_window_dispose (GObject * object);
static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
GstBuffer * buffer);
static void
gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_d3d11_window_set_property;
gobject_class->get_property = gst_d3d11_window_get_property;
gobject_class->dispose = gst_d3d11_window_dispose;
g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
g_param_spec_object ("d3d11device", "D3D11 Device",
"GstD3D11Device object for creating swapchain",
GST_TYPE_D3D11_DEVICE,
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio",
"Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
g_param_spec_boolean ("enable-navigation-events",
"Enable navigation events",
"When enabled, signals for navigation events are emitted",
DEFAULT_ENABLE_NAVIGATION_EVENTS,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE,
g_param_spec_flags ("fullscreen-toggle-mode",
"Full screen toggle mode",
"Full screen toggle mode used to trigger fullscreen mode change",
GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
g_param_spec_boolean ("fullscreen",
"fullscreen",
"Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
DEFAULT_FULLSCREEN,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE,
g_param_spec_pointer ("window-handle",
"Window Handle", "External Window Handle",
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS)));
d3d11_window_signals[SIGNAL_KEY_EVENT] =
g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
d3d11_window_signals[SIGNAL_MOUSE_EVENT] =
g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
}
static void
gst_d3d11_window_init (GstD3D11Window * self)
{
self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
self->fullscreen = DEFAULT_FULLSCREEN;
self->aspect_ratio_n = 1;
self->aspect_ratio_d = 1;
}
static void
gst_d3d11_window_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstD3D11Window *self = GST_D3D11_WINDOW (object);
GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
switch (prop_id) {
case PROP_D3D11_DEVICE:
self->device = (GstD3D11Device *) g_value_dup_object (value);
break;
case PROP_FORCE_ASPECT_RATIO:
{
self->force_aspect_ratio = g_value_get_boolean (value);
if (self->swap_chain)
klass->update_swap_chain (self);
break;
}
case PROP_ENABLE_NAVIGATION_EVENTS:
self->enable_navigation_events = g_value_get_boolean (value);
break;
case PROP_FULLSCREEN_TOGGLE_MODE:
self->fullscreen_toggle_mode =
(GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
break;
case PROP_FULLSCREEN:
{
self->requested_fullscreen = g_value_get_boolean (value);
if (self->swap_chain)
klass->change_fullscreen_mode (self);
break;
}
case PROP_WINDOW_HANDLE:
self->external_handle = (guintptr) g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_window_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstD3D11Window *self = GST_D3D11_WINDOW (object);
switch (prop_id) {
case PROP_ENABLE_NAVIGATION_EVENTS:
g_value_set_boolean (value, self->enable_navigation_events);
break;
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, self->force_aspect_ratio);
break;
case PROP_FULLSCREEN_TOGGLE_MODE:
g_value_set_flags (value, self->fullscreen_toggle_mode);
break;
case PROP_FULLSCREEN:
g_value_set_boolean (value, self->fullscreen);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_d3d11_window_release_resources (GstD3D11Device * device,
GstD3D11Window * window)
{
if (window->rtv) {
window->rtv->Release ();
window->rtv = NULL;
}
if (window->swap_chain) {
window->swap_chain->Release ();
window->swap_chain = NULL;
}
}
static void
gst_d3d11_window_dispose (GObject * object)
{
GstD3D11Window *self = GST_D3D11_WINDOW (object);
if (self->device) {
gst_d3d11_window_release_resources (self->device, self);
}
if (self->converter) {
gst_d3d11_color_converter_free (self->converter);
self->converter = NULL;
}
if (self->compositor) {
gst_d3d11_overlay_compositor_free (self->compositor);
self->compositor = NULL;
}
gst_clear_buffer (&self->cached_buffer);
gst_clear_object (&self->device);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
void
gst_d3d11_window_on_resize (GstD3D11Window * window, guint width, guint height)
{
HRESULT hr;
ID3D11Device *device_handle;
D3D11_TEXTURE2D_DESC desc;
DXGI_SWAP_CHAIN_DESC swap_desc;
ID3D11Texture2D *backbuffer = NULL;
GstVideoRectangle src_rect, dst_rect, rst_rect;
IDXGISwapChain *swap_chain;
gst_d3d11_device_lock (window->device);
if (!window->swap_chain)
goto done;
device_handle = gst_d3d11_device_get_device_handle (window->device);
swap_chain = window->swap_chain;
if (window->rtv) {
window->rtv->Release ();
window->rtv = NULL;
}
/* Set zero width and height here. dxgi will decide client area by itself */
swap_chain->GetDesc (&swap_desc);
hr = swap_chain->ResizeBuffers (0, width, height, DXGI_FORMAT_UNKNOWN,
swap_desc.Flags);
if (!gst_d3d11_result (hr, window->device)) {
GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
goto done;
}
hr = swap_chain->GetBuffer (0, IID_ID3D11Texture2D, (void **) &backbuffer);
if (!gst_d3d11_result (hr, window->device)) {
GST_ERROR_OBJECT (window,
"Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
goto done;
}
backbuffer->GetDesc (&desc);
window->surface_width = desc.Width;
window->surface_height = desc.Height;
width = window->width;
height = window->height;
{
src_rect.x = 0;
src_rect.y = 0;
src_rect.w = width * window->aspect_ratio_n;
src_rect.h = height * window->aspect_ratio_d;
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.w = window->surface_width;
dst_rect.h = window->surface_height;
if (window->force_aspect_ratio) {
src_rect.w = width * window->aspect_ratio_n;
src_rect.h = height * window->aspect_ratio_d;
gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
} else {
rst_rect = dst_rect;
}
}
window->render_rect.left = rst_rect.x;
window->render_rect.top = rst_rect.y;
window->render_rect.right = rst_rect.x + rst_rect.w;
window->render_rect.bottom = rst_rect.y + rst_rect.h;
GST_LOG_OBJECT (window,
"New client area %dx%d, render rect x: %d, y: %d, %dx%d",
desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h);
hr = device_handle->CreateRenderTargetView ((ID3D11Resource *) backbuffer,
NULL, &window->rtv);
if (!gst_d3d11_result (hr, window->device)) {
GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x",
(guint) hr);
goto done;
}
window->first_present = TRUE;
/* redraw the last scene if cached buffer exits */
gst_d3d111_window_present (window, NULL);
done:
if (backbuffer)
backbuffer->Release ();
gst_d3d11_device_unlock (window->device);
}
void
gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
const gchar * key)
{
g_return_if_fail (GST_IS_D3D11_WINDOW (window));
if (!window->enable_navigation_events)
return;
g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
}
void
gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
gint button, gdouble x, gdouble y)
{
g_return_if_fail (GST_IS_D3D11_WINDOW (window));
if (!window->enable_navigation_events)
return;
g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
event, button, x, y);
}
#if (DXGI_HEADER_VERSION >= 5)
static inline UINT16
fraction_to_uint (guint num, guint den, guint scale)
{
gdouble val;
gst_util_fraction_to_double (num, den, &val);
return (UINT16) val *scale;
}
static void
mastering_display_gst_to_dxgi (GstVideoMasteringDisplayInfo * m,
GstVideoContentLightLevel * c, DXGI_HDR_METADATA_HDR10 * meta)
{
meta->RedPrimary[0] = fraction_to_uint (m->Rx_n, m->Rx_d, 50000);
meta->RedPrimary[1] = fraction_to_uint (m->Ry_n, m->Ry_d, 50000);
meta->GreenPrimary[0] = fraction_to_uint (m->Gx_n, m->Gx_d, 50000);
meta->GreenPrimary[1] = fraction_to_uint (m->Gy_n, m->Gy_d, 50000);
meta->BluePrimary[0] = fraction_to_uint (m->Bx_n, m->Bx_d, 50000);
meta->BluePrimary[1] = fraction_to_uint (m->By_n, m->By_d, 50000);
meta->WhitePoint[0] = fraction_to_uint (m->Wx_n, m->Wx_d, 50000);
meta->WhitePoint[1] = fraction_to_uint (m->Wy_n, m->Wy_d, 50000);
meta->MaxMasteringLuminance =
fraction_to_uint (m->max_luma_n, m->max_luma_d, 1);
meta->MinMasteringLuminance =
fraction_to_uint (m->min_luma_n, m->min_luma_d, 1);
meta->MaxContentLightLevel = fraction_to_uint (c->maxCLL_n, c->maxCLL_d, 1);
meta->MaxFrameAverageLightLevel =
fraction_to_uint (c->maxFALL_n, c->maxFALL_d, 1);
}
/* missing in mingw header... */
typedef enum
{
GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0,
GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3,
GST_DXGI_COLOR_SPACE_RESERVED = 4,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11,
GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16,
GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18,
GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20,
GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23,
GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24,
GST_DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF
} GST_DXGI_COLOR_SPACE_TYPE;
typedef struct
{
GST_DXGI_COLOR_SPACE_TYPE type;
GstVideoColorRange range;
GstVideoTransferFunction transfer;
GstVideoColorPrimaries primaries;
} DxgiColorSpaceMap;
/* https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type */
static const DxgiColorSpaceMap colorspace_map[] = {
/* RGB, bt709 */
{GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255,
GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709},
{GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, GST_VIDEO_COLOR_RANGE_0_255,
GST_VIDEO_TRANSFER_GAMMA10, GST_VIDEO_COLOR_PRIMARIES_BT709},
{GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235,
GST_VIDEO_TRANSFER_BT709, GST_VIDEO_COLOR_PRIMARIES_BT709},
/* RGB, bt2020 */
{GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255,
GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020},
{GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235,
GST_VIDEO_TRANSFER_BT2020_10, GST_VIDEO_COLOR_PRIMARIES_BT2020},
/* RGB, bt2084 */
{GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, GST_VIDEO_COLOR_RANGE_0_255,
GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020},
{GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020,
GST_VIDEO_COLOR_RANGE_16_235,
GST_VIDEO_TRANSFER_SMPTE2084, GST_VIDEO_COLOR_PRIMARIES_BT2020},
/* RGB, SRGB */
{GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709, GST_VIDEO_COLOR_RANGE_16_235,
GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT709},
{GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020, GST_VIDEO_COLOR_RANGE_16_235,
GST_VIDEO_TRANSFER_SRGB, GST_VIDEO_COLOR_PRIMARIES_BT2020},
};
static gboolean
gst_d3d11_window_color_space_from_video_info (GstD3D11Window * self,
GstVideoInfo * info, IDXGISwapChain4 * swapchain,
GST_DXGI_COLOR_SPACE_TYPE * dxgi_colorspace)
{
guint i;
gint best_idx = -1;
gint best_score = 0;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (dxgi_colorspace != NULL, FALSE);
/* We render only RGB for now */
if (!GST_VIDEO_FORMAT_INFO_IS_RGB (info->finfo))
return FALSE;
/* find the best matching colorspace */
for (i = 0; i < G_N_ELEMENTS (colorspace_map); i++) {
GstVideoColorimetry *cinfo = &info->colorimetry;
UINT can_support = 0;
HRESULT hr;
gint score = 0;
GstVideoTransferFunction transfer = cinfo->transfer;
DXGI_COLOR_SPACE_TYPE type = (DXGI_COLOR_SPACE_TYPE) colorspace_map[i].type;
if (transfer == GST_VIDEO_TRANSFER_BT2020_12)
transfer = GST_VIDEO_TRANSFER_BT2020_10;
hr = swapchain->CheckColorSpaceSupport (type, &can_support);
if (SUCCEEDED (hr) &&
(can_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ==
DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) {
if (cinfo->range == colorspace_map[i].range)
score++;
if (transfer == colorspace_map[i].transfer)
score++;
if (cinfo->primaries == colorspace_map[i].primaries)
score++;
GST_DEBUG_OBJECT (self,
"colorspace %d supported, score %d", type, score);
if (score > best_score) {
best_score = score;
best_idx = i;
}
} else {
GST_DEBUG_OBJECT (self,
"colorspace %d not supported", type);
}
}
if (best_idx < 0)
return FALSE;
*dxgi_colorspace = colorspace_map[best_idx].type;
return TRUE;
}
#endif
gboolean
gst_d3d11_window_prepare (GstD3D11Window * window, guint width, guint height,
guint aspect_ratio_n, guint aspect_ratio_d, GstCaps * caps, GError ** error)
{
GstD3D11WindowClass *klass;
GstCaps *render_caps;
guint swapchain_flags = 0;
#if (DXGI_HEADER_VERSION >= 5)
gboolean have_cll = FALSE;
gboolean have_mastering = FALSE;
gboolean swapchain4_available = FALSE;
#endif
g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
g_return_val_if_fail (aspect_ratio_n > 0, FALSE);
g_return_val_if_fail (aspect_ratio_d > 0, FALSE);
GST_DEBUG_OBJECT (window, "Prepare window with %dx%d caps %" GST_PTR_FORMAT,
width, height, caps);
render_caps = gst_d3d11_device_get_supported_caps (window->device,
(D3D11_FORMAT_SUPPORT) (D3D11_FORMAT_SUPPORT_TEXTURE2D |
D3D11_FORMAT_SUPPORT_DISPLAY));
GST_DEBUG_OBJECT (window, "rendering caps %" GST_PTR_FORMAT, render_caps);
render_caps = gst_d3d11_caps_fixate_format (caps, render_caps);
if (!render_caps || gst_caps_is_empty (render_caps)) {
GST_ERROR_OBJECT (window, "Couldn't define render caps");
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
"Couldn't define render caps");
gst_clear_caps (&render_caps);
return FALSE;
}
render_caps = gst_caps_fixate (render_caps);
gst_video_info_from_caps (&window->render_info, render_caps);
gst_clear_caps (&render_caps);
window->render_format =
gst_d3d11_format_from_gst (GST_VIDEO_INFO_FORMAT (&window->render_info));
if (!window->render_format ||
window->render_format->dxgi_format == DXGI_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (window, "Unknown dxgi render format");
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
"Unknown dxgi render format");
return FALSE;
}
gst_video_info_from_caps (&window->info, caps);
if (window->converter)
gst_d3d11_color_converter_free (window->converter);
window->converter = NULL;
if (window->compositor)
gst_d3d11_overlay_compositor_free (window->compositor);
window->compositor = NULL;
/* preserve upstream colorimetry */
window->render_info.width = width;
window->render_info.height = height;
window->render_info.colorimetry.primaries =
window->info.colorimetry.primaries;
window->render_info.colorimetry.transfer = window->info.colorimetry.transfer;
window->converter =
gst_d3d11_color_converter_new (window->device, &window->info,
&window->render_info);
if (!window->converter) {
GST_ERROR_OBJECT (window, "Cannot create converter");
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
"Cannot create converter");
return FALSE;
}
window->compositor =
gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
if (!window->compositor) {
GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
"Cannot create overlay compositor");
return FALSE;
}
window->allow_tearing = FALSE;
#if (DXGI_HEADER_VERSION >= 5)
if (!gst_video_content_light_level_from_caps (&window->content_light_level,
caps)) {
gst_video_content_light_level_init (&window->content_light_level);
} else {
have_cll = TRUE;
}
if (!gst_video_mastering_display_info_from_caps
(&window->mastering_display_info, caps)) {
gst_video_mastering_display_info_init (&window->mastering_display_info);
} else {
have_mastering = TRUE;
}
if (gst_d3d11_device_get_chosen_dxgi_factory_version (window->device) >=
GST_D3D11_DXGI_FACTORY_5) {
GST_DEBUG_OBJECT (window, "DXGI 1.5 interface is available");
swapchain4_available = TRUE;
g_object_get (window->device,
"allow-tearing", &window->allow_tearing, NULL);
if (window->allow_tearing) {
GST_DEBUG_OBJECT (window, "device support tearning");
swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
}
}
#endif
if (window->swap_chain) {
gst_d3d11_device_lock (window->device);
gst_d3d11_window_release_resources (window->device, window);
gst_d3d11_device_unlock (window->device);
}
window->aspect_ratio_n = aspect_ratio_n;
window->aspect_ratio_d = aspect_ratio_d;
window->render_rect.left = 0;
window->render_rect.top = 0;
window->render_rect.right = width;
window->render_rect.bottom = height;
window->width = width;
window->height = height;
klass = GST_D3D11_WINDOW_GET_CLASS (window);
if (!klass->create_swap_chain (window, window->render_format->dxgi_format,
width, height, swapchain_flags, &window->swap_chain)) {
GST_ERROR_OBJECT (window, "Cannot create swapchain");
g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
"Cannot create swapchain");
return FALSE;
}
#if (DXGI_HEADER_VERSION >= 5)
if (swapchain4_available) {
HRESULT hr;
GST_DXGI_COLOR_SPACE_TYPE ctype;
IDXGISwapChain4* swap_chain4 = (IDXGISwapChain4 *) window->swap_chain;
if (gst_d3d11_window_color_space_from_video_info (window,
&window->render_info, swap_chain4, &ctype)) {
hr = swap_chain4->SetColorSpace1 ((DXGI_COLOR_SPACE_TYPE) ctype);
if (!gst_d3d11_result (hr, window->device)) {
GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x",
ctype, (guint) hr);
} else {
GST_DEBUG_OBJECT (window, "Set colorspace %d", ctype);
}
if (have_cll && have_mastering) {
DXGI_HDR_METADATA_HDR10 metadata = { 0, };
GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain");
mastering_display_gst_to_dxgi (&window->mastering_display_info,
&window->content_light_level, &metadata);
hr = swap_chain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10,
sizeof (DXGI_HDR_METADATA_HDR10), &metadata);
if (!gst_d3d11_result (hr, window->device)) {
GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x",
(guint) hr);
}
}
} else {
GST_DEBUG_OBJECT (window,
"Could not get color space from %" GST_PTR_FORMAT, caps);
}
}
#endif
if (window->requested_fullscreen != window->fullscreen) {
klass->change_fullscreen_mode (window);
}
GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
return TRUE;
}
void
gst_d3d11_window_show (GstD3D11Window * window)
{
GstD3D11WindowClass *klass;
g_return_if_fail (GST_IS_D3D11_WINDOW (window));
klass = GST_D3D11_WINDOW_GET_CLASS (window);
if (klass->show)
klass->show (window);
}
void
gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, gint x, gint y,
gint width, gint height)
{
g_return_if_fail (GST_IS_D3D11_WINDOW (window));
/* TODO: resize window and view */
}
static GstFlowReturn
gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer)
{
GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
GstFlowReturn ret = GST_FLOW_OK;
guint present_flags = 0;
if (buffer) {
gst_buffer_replace (&self->cached_buffer, buffer);
}
if (self->cached_buffer) {
ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES];
guint i, j, k;
for (i = 0, j = 0; i < gst_buffer_n_memory (self->cached_buffer); i++) {
GstD3D11Memory *mem =
(GstD3D11Memory *) gst_buffer_peek_memory (self->cached_buffer, i);
for (k = 0; k < mem->num_shader_resource_views; k++) {
srv[j] = mem->shader_resource_view[k];
j++;
}
}
if (self->first_present) {
gst_d3d11_color_converter_update_rect (self->converter,
&self->render_rect);
gst_d3d11_overlay_compositor_update_rect (self->compositor,
&self->render_rect);
}
gst_d3d11_color_converter_convert_unlocked (self->converter,
srv, &self->rtv);
gst_d3d11_overlay_compositor_upload (self->compositor, self->cached_buffer);
gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &self->rtv);
#if (DXGI_HEADER_VERSION >= 5)
if (self->allow_tearing) {
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
}
#endif
ret = klass->present (self, present_flags);
self->first_present = FALSE;
}
return ret;
}
GstFlowReturn
gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
GstVideoRectangle * rect)
{
GstMemory *mem;
GstFlowReturn ret;
g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR);
mem = gst_buffer_peek_memory (buffer, 0);
if (!gst_is_d3d11_memory (mem)) {
GST_ERROR_OBJECT (window, "Invalid buffer");
return GST_FLOW_ERROR;
}
gst_d3d11_device_lock (window->device);
ret = gst_d3d111_window_present (window, buffer);
gst_d3d11_device_unlock (window->device);
return ret;
}
gboolean
gst_d3d11_window_unlock (GstD3D11Window * window)
{
GstD3D11WindowClass *klass;
gboolean ret = TRUE;
g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
klass = GST_D3D11_WINDOW_GET_CLASS (window);
if (klass->unlock)
ret = klass->unlock (window);
return ret;
}
gboolean
gst_d3d11_window_unlock_stop (GstD3D11Window * window)
{
GstD3D11WindowClass *klass;
gboolean ret = TRUE;
g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
klass = GST_D3D11_WINDOW_GET_CLASS (window);
if (klass->unlock_stop)
ret = klass->unlock_stop (window);
gst_d3d11_device_lock (window->device);
gst_clear_buffer (&window->cached_buffer);
gst_d3d11_device_unlock (window->device);
return ret;
}
GstD3D11WindowNativeType
gst_d3d11_window_get_native_type_from_handle (guintptr handle)
{
if (!handle)
return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
#if (!GST_D3D11_WINAPI_ONLY_APP)
if (IsWindow ((HWND) handle))
return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
#else
{
ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
if (SUCCEEDED (window.As (&core_window)))
return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
if (SUCCEEDED (window.As (&panel)))
return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
}
#endif
return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
}
const gchar *
gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
{
switch (type) {
case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
return "none";
case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
return "hwnd";
case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
return "core-window";
case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
return "swap-chain-panel";
default:
break;
}
return "none";
}

View file

@ -31,9 +31,9 @@
G_BEGIN_DECLS
#define GST_TYPE_D3D11_WINDOW (gst_d3d11_window_get_type())
#define GST_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_D3D11_WINDOW, GstD3D11Window))
#define GST_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS((klass), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass))
#define GST_IS_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_D3D11_WINDOW))
#define GST_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3D11_WINDOW, GstD3D11Window))
#define GST_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass))
#define GST_IS_D3D11_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3D11_WINDOW))
#define GST_IS_D3D11_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_D3D11_WINDOW))
#define GST_D3D11_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_D3D11_WINDOW, GstD3D11WindowClass))
#define GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE (gst_d3d11_window_fullscreen_toggle_mode_type())
@ -43,13 +43,6 @@ typedef struct _GstD3D11WindowClass GstD3D11WindowClass;
#define GST_D3D11_WINDOW_FLOW_CLOSED GST_FLOW_CUSTOM_ERROR
typedef enum
{
GST_D3D11_WINDOW_OVERLAY_STATE_NONE = 0,
GST_D3D11_WINDOW_OVERLAY_STATE_OPENED,
GST_D3D11_WINDOW_OVERLAY_STATE_CLOSED,
} GstD3D11WindowOverlayState;
typedef enum
{
GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE = 0,
@ -59,10 +52,30 @@ typedef enum
GType gst_d3d11_window_fullscreen_toggle_mode_type (void);
typedef enum
{
GST_D3D11_WINDOW_NATIVE_TYPE_NONE = 0,
GST_D3D11_WINDOW_NATIVE_TYPE_HWND,
GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW,
GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL,
} GstD3D11WindowNativeType;
struct _GstD3D11Window
{
GstObject parent;
/*< protected >*/
gboolean initialized;
GstD3D11Device *device;
guintptr external_handle;
/* properties */
gboolean force_aspect_ratio;
gboolean enable_navigation_events;
GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
gboolean requested_fullscreen;
gboolean fullscreen;
GstVideoInfo info;
GstVideoInfo render_info;
const GstD3D11Format *render_format;
@ -78,12 +91,6 @@ struct _GstD3D11Window
/* requested rect via gst_d3d11_window_render */
GstVideoRectangle rect;
GMutex lock;
GCond cond;
GMainContext *main_context;
GMainLoop *loop;
guint width;
guint height;
@ -93,77 +100,83 @@ struct _GstD3D11Window
guint aspect_ratio_n;
guint aspect_ratio_d;
gboolean visible;
GSource *msg_source;
GIOChannel *msg_io_channel;
GThread *thread;
gboolean created;
HWND internal_win_id;
HWND external_win_id;
GstD3D11WindowOverlayState overlay_state;
HDC device_handle;
IDXGISwapChain *swap_chain;
ID3D11RenderTargetView *rtv;
DXGI_FORMAT format;
gboolean first_present;
gboolean have_swapchain1;
GstD3D11Device *device;
/* properties */
gboolean force_aspect_ratio;
gboolean enable_navigation_events;
GstD3D11WindowFullscreenToggleMode fullscreen_toggle_mode;
gboolean requested_fullscreen;
gboolean fullscreen;
/* atomic */
volatile gint pending_fullscreen_count;
GstBuffer *cached_buffer;
gboolean first_present;
gboolean allow_tearing;
/* fullscreen related */
RECT restore_rect;
LONG restore_style;
};
struct _GstD3D11WindowClass
{
GstObjectClass object_class;
void (*show) (GstD3D11Window * window);
void (*update_swap_chain) (GstD3D11Window * window);
void (*change_fullscreen_mode) (GstD3D11Window * window);
gboolean (*create_swap_chain) (GstD3D11Window * window,
DXGI_FORMAT format,
guint width,
guint height,
guint swapchain_flags,
IDXGISwapChain ** swap_chain);
GstFlowReturn (*present) (GstD3D11Window * window,
guint present_flags);
gboolean (*unlock) (GstD3D11Window * window);
gboolean (*unlock_stop) (GstD3D11Window * window);
};
GType gst_d3d11_window_get_type (void);
GType gst_d3d11_window_get_type (void);
GstD3D11Window * gst_d3d11_window_new (GstD3D11Device * device);
void gst_d3d11_window_show (GstD3D11Window * window);
void gst_d3d11_window_show (GstD3D11Window * window);
void gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
gint x, gint y,
gint width, gint height);
void gst_d3d11_window_set_window_handle (GstD3D11Window * window,
guintptr id);
gboolean gst_d3d11_window_prepare (GstD3D11Window * window,
guint width,
guint height,
guint aspect_ratio_n,
guint aspect_ratio_d,
GstCaps * caps,
GError ** error);
void gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
gint x, gint y,
gint width, gint height);
GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window,
GstBuffer * buffer,
GstVideoRectangle * src_rect);
gboolean gst_d3d11_window_prepare (GstD3D11Window * window,
guint width,
guint height,
guint aspect_ratio_n,
guint aspect_ratio_d,
GstCaps * caps,
GError ** error);
gboolean gst_d3d11_window_unlock (GstD3D11Window * window);
GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window,
GstBuffer * buffer,
GstVideoRectangle * src_rect);
gboolean gst_d3d11_window_unlock_stop (GstD3D11Window * window);
gboolean gst_d3d11_window_flush (GstD3D11Window * window);
void gst_d3d11_window_on_resize (GstD3D11Window * window,
guint width,
guint height);
void gst_d3d11_window_on_key_event (GstD3D11Window * window,
const gchar * event,
const gchar * key);
void gst_d3d11_window_on_mouse_event (GstD3D11Window * window,
const gchar * event,
gint button,
gdouble x,
gdouble y);
/* utils */
GstD3D11WindowNativeType gst_d3d11_window_get_native_type_from_handle (guintptr handle);
const gchar * gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstD3D11Window, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,364 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11window.h"
#include "gstd3d11device.h"
#include "gstd3d11memory.h"
#include "gstd3d11utils.h"
#include "gstd3d11window_corewindow.h"
/* workaround for GetCurrentTime collision */
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.media.dxinterop.h>
#include <windows.applicationmodel.core.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#include <windows.graphics.display.h>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::UI;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Graphics;
typedef ABI::Windows::Foundation::
__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CWindowSizeChangedEventArgs_t
IWindowSizeChangedEventHandler;
extern "C" {
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
#define GST_CAT_DEFAULT gst_d3d11_window_debug
}
typedef struct _CoreWindowWinRTStorage
{
ComPtr<Core::ICoreWindow> core_window;
EventRegistrationToken event_token;
} CoreWindowWinRTStorage;
struct _GstD3D11WindowCoreWindow
{
GstD3D11Window parent;
CoreWindowWinRTStorage *storage;
};
#define gst_d3d11_window_core_window_parent_class parent_class
G_DEFINE_TYPE (GstD3D11WindowCoreWindow, gst_d3d11_window_core_window,
GST_TYPE_D3D11_WINDOW);
static void gst_d3d11_window_core_window_constructed (GObject * object);
static void gst_d3d11_window_core_window_dispose (GObject * object);
static void gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window * window);
static void
gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window * window);
static gboolean
gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
IDXGISwapChain ** swap_chain);
static GstFlowReturn
gst_d3d11_window_core_window_present (GstD3D11Window * window,
guint present_flags);
static void
gst_d3d11_window_core_window_on_resize (GstD3D11WindowCoreWindow * self,
guint width, guint height);
static float
get_logical_dpi (void)
{
ComPtr<Display::IDisplayPropertiesStatics> properties;
HRESULT hr;
HStringReference str_ref =
HStringReference (RuntimeClass_Windows_Graphics_Display_DisplayProperties);
hr = GetActivationFactory (str_ref.Get(), properties.GetAddressOf());
if (gst_d3d11_result (hr, NULL)) {
float dpi = 96.0f;
hr = properties->get_LogicalDpi (&dpi);
if (gst_d3d11_result (hr, NULL))
return dpi;
}
return 96.0f;
}
static inline float dip_to_pixel (float dip)
{
/* https://docs.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels */
return dip * get_logical_dpi() / 96.0f;
}
class CoreResizeHandler
: public RuntimeClass<RuntimeClassFlags<ClassicCom>,
IWindowSizeChangedEventHandler>
{
public:
CoreResizeHandler () {}
HRESULT RuntimeClassInitialize (GstD3D11WindowCoreWindow * listener)
{
if (!listener)
return E_INVALIDARG;
window = listener;
return S_OK;
}
IFACEMETHOD(Invoke)
(Core::ICoreWindow * sender, Core::IWindowSizeChangedEventArgs * args)
{
if (window) {
Size new_size;
HRESULT hr = args->get_Size(&new_size);
if (gst_d3d11_result (hr, NULL)) {
guint width, height;
width = (guint) dip_to_pixel (new_size.Width);
height = (guint) dip_to_pixel (new_size.Height);
gst_d3d11_window_core_window_on_resize (window, width, height);
}
}
return S_OK;
}
private:
GstD3D11WindowCoreWindow * window;
};
static void
gst_d3d11_window_core_window_class_init (GstD3D11WindowCoreWindowClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
gobject_class->constructed = gst_d3d11_window_core_window_constructed;
gobject_class->dispose = gst_d3d11_window_core_window_dispose;
window_class->update_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_update_swap_chain);
window_class->change_fullscreen_mode =
GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_change_fullscreen_mode);
window_class->create_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_create_swap_chain);
window_class->present =
GST_DEBUG_FUNCPTR (gst_d3d11_window_core_window_present);
}
static void
gst_d3d11_window_core_window_init (GstD3D11WindowCoreWindow * self)
{
self->storage = new CoreWindowWinRTStorage;
}
static void
gst_d3d11_window_core_window_constructed (GObject * object)
{
GstD3D11Window *window = GST_D3D11_WINDOW (object);
GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (object);
CoreWindowWinRTStorage *storage = self->storage;
HRESULT hr;
ComPtr<IInspectable> inspectable;
ComPtr<IWindowSizeChangedEventHandler> resize_handler;
Rect bounds;
if (!window->external_handle) {
GST_ERROR_OBJECT (self, "No external window handle");
return;
}
inspectable = reinterpret_cast<IInspectable*> (window->external_handle);
hr = inspectable.As (&storage->core_window);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = storage->core_window->get_Bounds (&bounds);
if (!gst_d3d11_result (hr, NULL))
goto error;
window->surface_width = dip_to_pixel (bounds.Width);
window->surface_height = dip_to_pixel (bounds.Height);
GST_DEBUG_OBJECT (self,
"client size %dx%d", window->surface_width, window->surface_height);
hr = MakeAndInitialize<CoreResizeHandler>(&resize_handler, self);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = storage->core_window->add_SizeChanged (resize_handler.Get(),
&storage->event_token);
if (!gst_d3d11_result (hr, NULL))
goto error;
window->initialized = TRUE;
return;
error:
GST_ERROR_OBJECT (self, "Invalid window handle");
return;
}
static void
gst_d3d11_window_core_window_dispose (GObject * object)
{
GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (object);
CoreWindowWinRTStorage *storage = self->storage;
if (storage) {
if (storage->core_window)
storage->core_window->remove_SizeChanged (storage->event_token);
delete storage;
}
self->storage = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
gst_d3d11_window_core_window_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
IDXGISwapChain ** swap_chain)
{
GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
ComPtr<IDXGISwapChain1> new_swapchain;
GstD3D11Device *device = window->device;
DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
desc1.Width = width;
desc1.Height = height;
desc1.Format = format;
desc1.Stereo = FALSE;
desc1.SampleDesc.Count = 1;
desc1.SampleDesc.Quality = 0;
desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc1.BufferCount = 2;
desc1.Scaling = DXGI_SCALING_NONE;
desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
desc1.Flags = swapchain_flags;
new_swapchain =
gst_d3d11_device_create_swap_chain_for_core_window (device,
window->external_handle, &desc1, NULL);
if (!new_swapchain) {
GST_ERROR_OBJECT (self, "Cannot create swapchain");
return FALSE;
}
new_swapchain.CopyTo (swap_chain);
gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self),
window->surface_width, window->surface_height);
return TRUE;
}
static GstFlowReturn
gst_d3d11_window_core_window_present (GstD3D11Window * window,
guint present_flags)
{
GstD3D11WindowCoreWindow *self = GST_D3D11_WINDOW_CORE_WINDOW (window);
HRESULT hr;
DXGI_PRESENT_PARAMETERS present_params = { 0, };
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */
if (!window->first_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}
hr = swap_chain->Present1 (0, present_flags, &present_params);
if (!gst_d3d11_result (hr, window->device)) {
GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
(guint) hr);
}
return GST_FLOW_OK;
}
static void
gst_d3d11_window_core_window_update_swap_chain (GstD3D11Window * window)
{
gst_d3d11_window_on_resize (window,
window->surface_width, window->surface_height);
return;
}
static void
gst_d3d11_window_core_window_change_fullscreen_mode (GstD3D11Window * window)
{
GST_FIXME_OBJECT (window, "Implement fullscreen mode change");
return;
}
static void
gst_d3d11_window_core_window_on_resize (GstD3D11WindowCoreWindow * self,
guint width, guint height)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
window->surface_width = width;
window->surface_height = height;
GST_LOG_OBJECT (self, "New size %dx%d", width, height);
gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self), width, height);
}
GstD3D11Window *
gst_d3d11_window_core_window_new (GstD3D11Device * device, guintptr handle)
{
GstD3D11Window *window;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
g_return_val_if_fail (handle, NULL);
window = (GstD3D11Window *)
g_object_new (GST_TYPE_D3D11_WINDOW_CORE_WINDOW,
"d3d11device", device, "window-handle", handle, NULL);
if (!window->initialized) {
gst_object_unref (window);
return NULL;
}
g_object_ref_sink (window);
return window;
}

View file

@ -0,0 +1,40 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_WINDOW_CORE_WINDOW_H__
#define __GST_D3D11_WINDOW_CORE_WINDOW_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
#include "gstd3d11window.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_WINDOW_CORE_WINDOW (gst_d3d11_window_core_window_get_type())
G_DECLARE_FINAL_TYPE (GstD3D11WindowCoreWindow, gst_d3d11_window_core_window,
GST, D3D11_WINDOW_CORE_WINDOW, GstD3D11Window);
GstD3D11Window * gst_d3d11_window_core_window_new (GstD3D11Device * device,
guintptr handle);
G_END_DECLS
#endif /* __GST_D3D11_WINDOW_CORE_WINDOW_H__ */

View file

@ -0,0 +1,499 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11window.h"
#include "gstd3d11device.h"
#include "gstd3d11memory.h"
#include "gstd3d11utils.h"
#include "gstd3d11window_swapchainpanel.h"
/* workaround for GetCurrentTime collision */
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.media.dxinterop.h>
#include <windows.applicationmodel.core.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::UI;
using namespace ABI::Windows::Foundation;
extern "C" {
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
#define GST_CAT_DEFAULT gst_d3d11_window_debug
}
/* timeout to wait busy UI thread */
#define DEFAULT_ASYNC_TIMEOUT (5 * 1000)
typedef struct _SwapChainPanelWinRTStorage
{
ComPtr<Xaml::Controls::ISwapChainPanel> panel;
ComPtr<Core::ICoreDispatcher> dispatcher;
ComPtr<IDXGISwapChain1> swapchain;
HANDLE cancellable;
EventRegistrationToken event_token;
} SwapChainPanelWinRTStorage;
struct _GstD3D11WindowSwapChainPanel
{
GstD3D11Window parent;
SwapChainPanelWinRTStorage *storage;
};
#define gst_d3d11_window_swap_chain_panel_parent_class parent_class
G_DEFINE_TYPE (GstD3D11WindowSwapChainPanel,
gst_d3d11_window_swap_chain_panel, GST_TYPE_D3D11_WINDOW);
static void gst_d3d11_window_swap_chain_panel_constructed (GObject * object);
static void gst_d3d11_window_swap_chain_panel_dispose (GObject * object);
static void
gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window);
static void
gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window *
window);
static gboolean
gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
IDXGISwapChain ** swap_chain);
static GstFlowReturn
gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window,
guint present_flags);
static gboolean
gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window);
static gboolean
gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window);
static void
gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11WindowSwapChainPanel *
self, guint width, guint height);
class PanelResizeHandler
: public RuntimeClass<RuntimeClassFlags<ClassicCom>,
Xaml::ISizeChangedEventHandler>
{
public:
PanelResizeHandler () {}
HRESULT RuntimeClassInitialize (GstD3D11WindowSwapChainPanel * listener)
{
if (!listener)
return E_INVALIDARG;
window = listener;
return S_OK;
}
IFACEMETHOD(Invoke)
(IInspectable * sender, Xaml::ISizeChangedEventArgs * args)
{
if (window) {
Size new_size;
HRESULT hr = args->get_NewSize(&new_size);
if (SUCCEEDED(hr)) {
gst_d3d11_window_swap_chain_panel_on_resize (window,
new_size.Width, new_size.Height);
}
}
return S_OK;
}
private:
GstD3D11WindowSwapChainPanel * window;
};
template <typename CB>
static HRESULT
run_async (const ComPtr<Core::ICoreDispatcher> &dispatcher, HANDLE cancellable,
DWORD timeout, CB &&cb)
{
ComPtr<IAsyncAction> async_action;
HRESULT hr;
HRESULT async_hr;
boolean can_now;
DWORD wait_ret;
HANDLE event_handle[2];
hr = dispatcher->get_HasThreadAccess (&can_now);
if (!gst_d3d11_result (hr, NULL))
return hr;
if (can_now)
return cb ();
Event event (CreateEventEx (NULL, NULL, CREATE_EVENT_MANUAL_RESET,
EVENT_ALL_ACCESS));
if (!event.IsValid())
return E_FAIL;
auto handler =
Callback<Implements<RuntimeClassFlags<ClassicCom>,
Core::IDispatchedHandler, FtmBase>>([&async_hr, &cb, &event] {
async_hr = cb ();
SetEvent (event.Get());
return S_OK;
});
hr = dispatcher->RunAsync (Core::CoreDispatcherPriority_Normal, handler.Get(),
async_action.GetAddressOf());
if (!gst_d3d11_result (hr, NULL))
return hr;
event_handle[0] = event.Get();
event_handle[1] = cancellable;
wait_ret = WaitForMultipleObjects (2, event_handle, FALSE, timeout);
if (wait_ret != WAIT_OBJECT_0)
return E_FAIL;
return async_hr;
}
static HRESULT
get_panel_size (const ComPtr<Core::ICoreDispatcher> &dispatcher,
HANDLE cancellable,
const ComPtr<Xaml::Controls::ISwapChainPanel> &panel, Size *size)
{
ComPtr<Xaml::IUIElement> ui;
HRESULT hr = panel.As (&ui);
if (!gst_d3d11_result (hr, NULL))
return hr;
hr = run_async (dispatcher, cancellable, DEFAULT_ASYNC_TIMEOUT,
[ui, size] {
return ui->get_RenderSize (size);
});
return hr;
}
static void
gst_d3d11_window_swap_chain_panel_class_init (GstD3D11WindowSwapChainPanelClass
* klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
gobject_class->constructed = gst_d3d11_window_swap_chain_panel_constructed;
gobject_class->dispose = gst_d3d11_window_swap_chain_panel_dispose;
window_class->update_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_update_swap_chain);
window_class->change_fullscreen_mode =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_change_fullscreen_mode);
window_class->create_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_create_swap_chain);
window_class->present =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_present);
window_class->unlock =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock);
window_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock_stop);
}
static void
gst_d3d11_window_swap_chain_panel_init (GstD3D11WindowSwapChainPanel * self)
{
self->storage = new SwapChainPanelWinRTStorage;
self->storage->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
}
static void
gst_d3d11_window_swap_chain_panel_constructed (GObject * object)
{
GstD3D11Window *window = GST_D3D11_WINDOW (object);
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (object);
SwapChainPanelWinRTStorage *storage = self->storage;
HRESULT hr;
ComPtr<IInspectable> inspectable;
ComPtr<Xaml::IDependencyObject> dependency_obj;
Size size;
ComPtr<Xaml::ISizeChangedEventHandler> resize_handler;
ComPtr<Xaml::IFrameworkElement> framework;
if (!window->external_handle) {
GST_ERROR_OBJECT (self, "No external window handle");
return;
}
inspectable = reinterpret_cast<IInspectable*> (window->external_handle);
hr = inspectable.As (&storage->panel);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = storage->panel.As (&dependency_obj);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = dependency_obj->get_Dispatcher(storage->dispatcher.GetAddressOf());
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = get_panel_size (storage->dispatcher,
storage->cancellable, storage->panel, &size);
if (!gst_d3d11_result (hr, NULL))
goto error;
GST_DEBUG_OBJECT (self, "client size %dx%d", size.Width, size.Height);
window->surface_width = size.Width;
window->surface_height = size.Height;
hr = MakeAndInitialize<PanelResizeHandler>(&resize_handler, self);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = storage->panel.As (&framework);
if (!gst_d3d11_result (hr, NULL))
goto error;
hr = run_async (storage->dispatcher,
storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
[self, framework, resize_handler] {
return framework->add_SizeChanged (resize_handler.Get(),
&self->storage->event_token);
});
if (!gst_d3d11_result (hr, NULL))
goto error;
window->initialized = TRUE;
return;
error:
GST_ERROR_OBJECT (self, "Invalid window handle");
return;
}
static void
gst_d3d11_window_swap_chain_panel_dispose (GObject * object)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (object);
SwapChainPanelWinRTStorage *storage = self->storage;
if (storage) {
if (storage->panel && storage->dispatcher) {
ComPtr<Xaml::IFrameworkElement> framework;
HRESULT hr;
hr = storage->panel.As (&framework);
if (SUCCEEDED (hr)) {
run_async (storage->dispatcher,
storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
[self, framework] {
return framework->remove_SizeChanged (self->storage->event_token);
});
}
CloseHandle (storage->cancellable);
}
delete storage;
}
self->storage = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
IDXGISwapChain ** swap_chain)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
SwapChainPanelWinRTStorage *storage = self->storage;
ComPtr<IDXGISwapChain1> new_swapchain;
ComPtr<ISwapChainPanelNative> panel_native;
GstD3D11Device *device = window->device;
DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
HRESULT hr;
desc1.Width = width;
desc1.Height = height;
desc1.Format = format;
desc1.Stereo = FALSE;
desc1.SampleDesc.Count = 1;
desc1.SampleDesc.Quality = 0;
desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc1.BufferCount = 2;
desc1.Scaling = DXGI_SCALING_STRETCH;
desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc1.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
desc1.Flags = swapchain_flags;
new_swapchain =
gst_d3d11_device_create_swap_chain_for_composition (device, &desc1, NULL);
if (!new_swapchain) {
GST_ERROR_OBJECT (self, "Cannot create swapchain");
return FALSE;
}
hr = storage->panel.As (&panel_native);
if (!gst_d3d11_result (hr, device))
return FALSE;
hr = run_async (storage->dispatcher,
storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
[panel_native, new_swapchain] {
return panel_native->SetSwapChain(new_swapchain.Get());
});
if (!gst_d3d11_result (hr, device))
return FALSE;
new_swapchain.CopyTo (swap_chain);
run_async (storage->dispatcher, storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
[window] {
gst_d3d11_window_on_resize (window,
window->surface_width, window->surface_height);
return S_OK;
});
return TRUE;
}
static GstFlowReturn
gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window,
guint present_flags)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
HRESULT hr;
DXGI_PRESENT_PARAMETERS present_params = { 0, };
IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
/* the first present should not specify dirty-rect */
if (!window->first_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}
hr = swap_chain->Present1 (0, present_flags, &present_params);
if (!gst_d3d11_result (hr, window->device)) {
GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
(guint) hr);
}
return GST_FLOW_OK;
}
static gboolean
gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
SwapChainPanelWinRTStorage *storage = self->storage;
SetEvent (storage->cancellable);
return TRUE;
}
static gboolean
gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
SwapChainPanelWinRTStorage *storage = self->storage;
ResetEvent (storage->cancellable);
return TRUE;
}
static void
gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window)
{
GstD3D11WindowSwapChainPanel *self =
GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
SwapChainPanelWinRTStorage *storage = self->storage;
run_async (storage->dispatcher, storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
[window] {
gst_d3d11_window_on_resize (window,
window->surface_width, window->surface_height);
return S_OK;
});
return;
}
static void
gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window *
window)
{
GST_FIXME_OBJECT (window, "Implement fullscreen mode change");
return;
}
static void
gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11WindowSwapChainPanel *
self, guint width, guint height)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
window->surface_width = width;
window->surface_height = height;
GST_LOG_OBJECT (self, "New size %dx%d", width, height);
gst_d3d11_window_on_resize (GST_D3D11_WINDOW (self), width, height);
}
GstD3D11Window *
gst_d3d11_window_swap_chain_panel_new (GstD3D11Device * device, guintptr handle)
{
GstD3D11Window *window;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
g_return_val_if_fail (handle, NULL);
window = (GstD3D11Window *)
g_object_new (GST_TYPE_D3D11_WINDOW_SWAP_CHAIN_PANEL,
"d3d11device", device, "window-handle", handle, NULL);
if (!window->initialized) {
gst_object_unref (window);
return NULL;
}
g_object_ref_sink (window);
return window;
}

View file

@ -0,0 +1,41 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__
#define __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
#include "gstd3d11window.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_WINDOW_SWAP_CHAIN_PANEL (gst_d3d11_window_swap_chain_panel_get_type())
G_DECLARE_FINAL_TYPE (GstD3D11WindowSwapChainPanel,
gst_d3d11_window_swap_chain_panel,
GST, D3D11_WINDOW_SWAP_CHAIN_PANEL, GstD3D11Window);
GstD3D11Window * gst_d3d11_window_swap_chain_panel_new (GstD3D11Device * device,
guintptr handle);
G_END_DECLS
#endif /* __GST_D3D11_WINDOW_SWAP_CHAIN_PANEL_H__ */

View file

@ -0,0 +1,924 @@
/*
* GStreamer
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
* Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstd3d11device.h"
#include "gstd3d11memory.h"
#include "gstd3d11utils.h"
#include "gstd3d11window_win32.h"
extern "C" {
GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
#define GST_CAT_DEFAULT gst_d3d11_window_debug
}
G_LOCK_DEFINE_STATIC (create_lock);
#define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc"
#define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_win32_object"
#define WM_GST_D3D11_FULLSCREEN (WM_USER + 1)
static LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
static LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
typedef enum
{
GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE = 0,
GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED,
GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED,
} GstD3D11WindowWin32OverlayState;
struct _GstD3D11WindowWin32
{
GstD3D11Window parent;
GMutex lock;
GCond cond;
GMainContext *main_context;
GMainLoop *loop;
gboolean visible;
GSource *msg_source;
GIOChannel *msg_io_channel;
GThread *thread;
HWND internal_hwnd;
HWND external_hwnd;
GstD3D11WindowWin32OverlayState overlay_state;
HDC device_handle;
gboolean first_present;
gboolean have_swapchain1;
/* atomic */
volatile gint pending_fullscreen_count;
/* fullscreen related */
RECT restore_rect;
LONG restore_style;
};
#define gst_d3d11_window_win32_parent_class parent_class
G_DEFINE_TYPE (GstD3D11WindowWin32, gst_d3d11_window_win32,
GST_TYPE_D3D11_WINDOW);
static void gst_d3d11_window_win32_constructed (GObject * object);
static void gst_d3d11_window_win32_dispose (GObject * object);
static void gst_d3d11_window_win32_finalize (GObject * object);
static void gst_d3d11_window_win32_show (GstD3D11Window * window);
static void gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window);
static void
gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window);
static gboolean
gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height,
guint swapchain_flags, IDXGISwapChain ** swap_chain);
static GstFlowReturn gst_d3d11_window_win32_present (GstD3D11Window * window,
guint present_flags);
static gpointer gst_d3d11_window_win32_thread_func (gpointer data);
static gboolean
gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self);
static void gst_d3d11_window_win32_close_internal_window (GstD3D11WindowWin32 *
self);
static void gst_d3d11_window_win32_release_external_handle (GstD3D11WindowWin32
* self);
static void
gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
guintptr handle);
static void
gst_d3d11_window_win32_class_init (GstD3D11WindowWin32Class * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
gobject_class->constructed = gst_d3d11_window_win32_constructed;
gobject_class->dispose = gst_d3d11_window_win32_dispose;
gobject_class->finalize = gst_d3d11_window_win32_finalize;
window_class->show = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_show);
window_class->update_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_update_swap_chain);
window_class->change_fullscreen_mode =
GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_change_fullscreen_mode);
window_class->create_swap_chain =
GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_create_swap_chain);
window_class->present = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_present);
}
static void
gst_d3d11_window_win32_init (GstD3D11WindowWin32 * self)
{
g_mutex_init (&self->lock);
g_cond_init (&self->cond);
self->main_context = g_main_context_new ();
}
static void
gst_d3d11_window_win32_constructed (GObject * object)
{
GstD3D11Window *window = GST_D3D11_WINDOW (object);
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
g_mutex_lock (&self->lock);
self->loop = g_main_loop_new (self->main_context, FALSE);
self->thread = g_thread_new ("GstD3D11WindowWin32Win32",
(GThreadFunc) gst_d3d11_window_win32_thread_func, self);
while (!g_main_loop_is_running (self->loop))
g_cond_wait (&self->cond, &self->lock);
g_mutex_unlock (&self->lock);
if (window->external_handle)
gst_d3d11_window_win32_set_window_handle (self, window->external_handle);
}
static void
gst_d3d11_window_win32_dispose (GObject * object)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
gst_d3d11_window_win32_release_external_handle (self);
if (self->loop) {
g_main_loop_quit (self->loop);
}
if (self->thread) {
g_thread_join (self->thread);
self->thread = NULL;
}
if (self->loop) {
g_main_loop_unref (self->loop);
self->loop = NULL;
}
if (self->main_context) {
g_main_context_unref (self->main_context);
self->main_context = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_d3d11_window_win32_finalize (GObject * object)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
g_mutex_clear (&self->lock);
g_cond_clear (&self->cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
running_cb (gpointer user_data)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (user_data);
GST_TRACE_OBJECT (self, "Main loop running now");
g_mutex_lock (&self->lock);
g_cond_signal (&self->cond);
g_mutex_unlock (&self->lock);
return G_SOURCE_REMOVE;
}
static gpointer
gst_d3d11_window_win32_thread_func (gpointer data)
{
GstD3D11Window *window = GST_D3D11_WINDOW (data);
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (data);
GSource *source;
GST_DEBUG_OBJECT (self, "Enter loop");
g_main_context_push_thread_default (self->main_context);
window->initialized = gst_d3d11_window_win32_create_internal_window (self);
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL);
g_source_attach (source, self->main_context);
g_source_unref (source);
g_main_loop_run (self->loop);
gst_d3d11_window_win32_close_internal_window (self);
g_main_context_pop_thread_default (self->main_context);
GST_DEBUG_OBJECT (self, "Exit loop");
return NULL;
}
static gboolean
msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
{
MSG msg;
if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
return G_SOURCE_CONTINUE;
TranslateMessage (&msg);
DispatchMessage (&msg);
return G_SOURCE_CONTINUE;
}
static void
gst_d3d11_window_win32_close_internal_window (GstD3D11WindowWin32 * self)
{
if (self->internal_hwnd) {
RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
ShowWindow (self->internal_hwnd, SW_HIDE);
SetParent (self->internal_hwnd, NULL);
if (!DestroyWindow (self->internal_hwnd))
GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT
", 0x%x", (guintptr) self->internal_hwnd, (guint) GetLastError ());
self->internal_hwnd = NULL;
}
if (self->msg_source) {
g_source_destroy (self->msg_source);
g_source_unref (self->msg_source);
self->msg_source = NULL;
}
if (self->msg_io_channel) {
g_io_channel_unref (self->msg_io_channel);
self->msg_io_channel = NULL;
}
}
static void
gst_d3d11_window_win32_set_external_handle (GstD3D11WindowWin32 * self)
{
WNDPROC external_window_proc;
RECT rect;
if (!self->external_hwnd) {
/* no parent so the internal window needs borders and system menu */
SetWindowLongPtr (self->internal_hwnd, GWL_STYLE,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW);
SetParent (self->internal_hwnd, NULL);
return;
}
external_window_proc =
(WNDPROC) GetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC);
GST_DEBUG ("set external window %" G_GUINTPTR_FORMAT,
(guintptr) self->external_hwnd);
SetProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME,
(HANDLE) external_window_proc);
SetProp (self->external_hwnd, D3D11_WINDOW_PROP_NAME, self);
SetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC,
(LONG_PTR) sub_class_proc);
SetWindowLongPtr (self->internal_hwnd, GWL_STYLE, WS_CHILD | WS_MAXIMIZE);
SetParent (self->internal_hwnd, self->external_hwnd);
/* take changes into account: SWP_FRAMECHANGED */
GetClientRect (self->external_hwnd, &rect);
SetWindowPos (self->internal_hwnd, HWND_TOP, rect.left, rect.top,
rect.right, rect.bottom,
SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_FRAMECHANGED | SWP_NOACTIVATE);
MoveWindow (self->internal_hwnd, rect.left, rect.top, rect.right,
rect.bottom, FALSE);
}
static void
gst_d3d11_window_win32_release_external_handle (GstD3D11WindowWin32 * self)
{
WNDPROC external_proc;
if (!self->external_hwnd)
return;
external_proc =
(WNDPROC) GetProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME);
if (!external_proc)
return;
GST_DEBUG ("release external window %" G_GUINTPTR_FORMAT,
(guintptr) self->external_hwnd);
SetWindowLongPtr (self->external_hwnd,
GWLP_WNDPROC, (LONG_PTR) external_proc);
RemoveProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME);
RemoveProp (self->external_hwnd, D3D11_WINDOW_PROP_NAME);
self->external_hwnd = NULL;
}
static gboolean
gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self)
{
WNDCLASSEX wc;
ATOM atom = 0;
HINSTANCE hinstance = GetModuleHandle (NULL);
GST_LOG_OBJECT (self, "Attempting to create a win32 window");
G_LOCK (create_lock);
atom = GetClassInfoEx (hinstance, "GSTD3D11", &wc);
if (atom == 0) {
GST_LOG_OBJECT (self, "Register internal window class");
ZeroMemory (&wc, sizeof (WNDCLASSEX));
wc.cbSize = sizeof (WNDCLASSEX);
wc.lpfnWndProc = window_proc;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszClassName = "GSTD3D11";
atom = RegisterClassEx (&wc);
if (atom == 0) {
G_UNLOCK (create_lock);
GST_ERROR_OBJECT (self, "Failed to register window class 0x%x",
(unsigned int) GetLastError ());
return FALSE;
}
} else {
GST_LOG_OBJECT (self, "window class was already registered");
}
self->device_handle = 0;
self->internal_hwnd = 0;
self->visible = FALSE;
self->internal_hwnd = CreateWindowEx (0,
"GSTD3D11",
"Direct3D11 renderer",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, (HWND) NULL, (HMENU) NULL, hinstance, self);
G_UNLOCK (create_lock);
if (!self->internal_hwnd) {
GST_ERROR_OBJECT (self, "Failed to create d3d11 window");
return FALSE;
}
GST_DEBUG_OBJECT (self, "d3d11 window created: %" G_GUINTPTR_FORMAT,
(guintptr) self->internal_hwnd);
/* device_handle is set in the window_proc */
if (!self->device_handle) {
GST_ERROR_OBJECT (self, "device handle is not available");
return FALSE;
}
GST_LOG_OBJECT (self,
"Created a internal d3d11 window %p", self->internal_hwnd);
self->msg_io_channel =
g_io_channel_win32_new_messages ((guintptr) self->internal_hwnd);
self->msg_source = g_io_create_watch (self->msg_io_channel, G_IO_IN);
g_source_set_callback (self->msg_source, (GSourceFunc) msg_cb, self, NULL);
g_source_attach (self->msg_source, self->main_context);
return TRUE;
}
/* always called from window thread */
static void
gst_d3d11_window_win32_change_fullscreen_mode_internal (GstD3D11WindowWin32 *
self)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
HWND hwnd = self->external_hwnd ? self->external_hwnd : self->internal_hwnd;
if (!window->swap_chain)
return;
if (window->requested_fullscreen == window->fullscreen)
return;
GST_DEBUG_OBJECT (self, "Change mode to %s",
window->requested_fullscreen ? "fullscreen" : "windowed");
window->fullscreen = !window->fullscreen;
if (!window->fullscreen) {
/* Restore the window's attributes and size */
SetWindowLong (hwnd, GWL_STYLE, self->restore_style);
SetWindowPos (hwnd, HWND_NOTOPMOST,
self->restore_rect.left,
self->restore_rect.top,
self->restore_rect.right - self->restore_rect.left,
self->restore_rect.bottom - self->restore_rect.top,
SWP_FRAMECHANGED | SWP_NOACTIVATE);
ShowWindow (hwnd, SW_NORMAL);
} else {
IDXGIOutput *output;
DXGI_OUTPUT_DESC output_desc;
IDXGISwapChain *swap_chain = window->swap_chain;
/* show window before change style */
ShowWindow (hwnd, SW_SHOW);
/* Save the old window rect so we can restore it when exiting
* fullscreen mode */
GetWindowRect (hwnd, &self->restore_rect);
self->restore_style = GetWindowLong (hwnd, GWL_STYLE);
/* Make the window borderless so that the client area can fill the screen */
SetWindowLong (hwnd, GWL_STYLE,
self->restore_style &
~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
WS_THICKFRAME));
swap_chain->GetContainingOutput (&output);
output->GetDesc (&output_desc);
output->Release ();
SetWindowPos (hwnd, HWND_TOPMOST,
output_desc.DesktopCoordinates.left,
output_desc.DesktopCoordinates.top,
output_desc.DesktopCoordinates.right,
output_desc.DesktopCoordinates.bottom,
SWP_FRAMECHANGED | SWP_NOACTIVATE);
ShowWindow (hwnd, SW_MAXIMIZE);
}
GST_DEBUG_OBJECT (self, "Fullscreen mode change done");
}
static void
gst_d3d11_window_win32_on_key_event (GstD3D11WindowWin32 * self,
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
gunichar2 wcrep[128];
const gchar *event;
if (!window->enable_navigation_events)
return;
if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
if (utfrep) {
if (uMsg == WM_KEYDOWN)
event = "key-press";
else
event = "key-release";
gst_d3d11_window_on_key_event (window, event, utfrep);
g_free (utfrep);
}
}
}
static void
gst_d3d11_window_win32_on_mouse_event (GstD3D11WindowWin32 * self,
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
gint button;
const gchar *event = NULL;
if (!window->enable_navigation_events)
return;
/* FIXME: convert to render coordinate */
switch (uMsg) {
case WM_MOUSEMOVE:
button = 0;
event = "mouse-move";
break;
case WM_LBUTTONDOWN:
button = 1;
event = "mouse-button-press";
break;
case WM_LBUTTONUP:
button = 1;
event = "mouse-button-release";
break;
case WM_RBUTTONDOWN:
button = 2;
event = "mouse-button-press";
break;
case WM_RBUTTONUP:
button = 2;
event = "mouse-button-release";
break;
case WM_MBUTTONDOWN:
button = 3;
event = "mouse-button-press";
break;
case WM_MBUTTONUP:
button = 3;
event = "mouse-button-release";
break;
default:
break;
}
if (event)
gst_d3d11_window_on_mouse_event (window,
event, button, (gdouble) LOWORD (lParam), (gdouble) HIWORD (lParam));
}
static void
gst_d3d11_window_win32_handle_window_proc (GstD3D11WindowWin32 * self,
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GstD3D11Window *window = GST_D3D11_WINDOW (self);
switch (uMsg) {
case WM_SIZE:
gst_d3d11_window_on_resize (window, 0, 0);
break;
case WM_CLOSE:
if (self->internal_hwnd) {
ShowWindow (self->internal_hwnd, SW_HIDE);
gst_d3d11_window_win32_close_internal_window (self);
}
break;
case WM_KEYDOWN:
case WM_KEYUP:
gst_d3d11_window_win32_on_key_event (self, hWnd, uMsg, wParam, lParam);
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
gst_d3d11_window_win32_on_mouse_event (self, hWnd, uMsg, wParam, lParam);
break;
case WM_SYSKEYDOWN:
if ((window->fullscreen_toggle_mode &
GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER)
== GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) {
WORD state = GetKeyState (VK_RETURN);
BYTE high = HIBYTE (state);
if (high & 0x1) {
window->requested_fullscreen = !window->fullscreen;
gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
}
}
break;
case WM_GST_D3D11_FULLSCREEN:
if (g_atomic_int_get (&self->pending_fullscreen_count)) {
g_atomic_int_dec_and_test (&self->pending_fullscreen_count);
if ((window->fullscreen_toggle_mode &
GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
== GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
}
break;
default:
break;
}
return;
}
static LRESULT CALLBACK
window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
GstD3D11WindowWin32 *self;
if (uMsg == WM_CREATE) {
self = GST_D3D11_WINDOW_WIN32 (((LPCREATESTRUCT) lParam)->lpCreateParams);
GST_LOG_OBJECT (self, "WM_CREATE");
self->device_handle = GetDC (hWnd);
/* Do this, otherwise we hang on exit. We can still use it (due to the
* CS_OWNDC flag in the WindowClass) after we have Released.
*/
ReleaseDC (hWnd, self->device_handle);
SetProp (hWnd, D3D11_WINDOW_PROP_NAME, self);
} else if (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)) {
self = GST_D3D11_WINDOW_WIN32 (GetProp (hWnd, D3D11_WINDOW_PROP_NAME));
g_assert (self->internal_hwnd == hWnd);
gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
lParam);
}
if (uMsg == WM_SIZE)
return 0;
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
static LRESULT FAR PASCAL
sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC external_window_proc =
(WNDPROC) GetProp (hWnd, EXTERNAL_PROC_PROP_NAME);
GstD3D11WindowWin32 *self =
(GstD3D11WindowWin32 *) GetProp (hWnd, D3D11_WINDOW_PROP_NAME);
if (uMsg == WM_SIZE) {
MoveWindow (self->internal_hwnd, 0, 0, LOWORD (lParam), HIWORD (lParam),
FALSE);
} else if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) {
g_mutex_lock (&self->lock);
GST_WARNING_OBJECT (self, "external window is closing");
gst_d3d11_window_win32_release_external_handle (self);
self->external_hwnd = NULL;
self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED;
g_mutex_unlock (&self->lock);
} else {
gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
lParam);
}
return CallWindowProc (external_window_proc, hWnd, uMsg, wParam, lParam);
}
static void
gst_d3d11_window_win32_disable_alt_enter (GstD3D11WindowWin32 * self,
GstD3D11Device * device, IDXGISwapChain * swap_chain, HWND hwnd)
{
IDXGIFactory1 *factory = NULL;
HRESULT hr;
hr = swap_chain->GetParent (IID_IDXGIFactory1, (void **) &factory);
if (!gst_d3d11_result (hr, device) || !factory) {
GST_WARNING_OBJECT (self,
"Cannot get parent dxgi factory for swapchain %p, hr: 0x%x",
swap_chain, (guint) hr);
return;
}
hr = factory->MakeWindowAssociation (hwnd, DXGI_MWA_NO_ALT_ENTER);
if (!gst_d3d11_result (hr, device)) {
GST_WARNING_OBJECT (self,
"MakeWindowAssociation failure, hr: 0x%x", (guint) hr);
}
factory->Release ();
}
static gboolean
gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
DXGI_FORMAT format, guint width, guint height,
guint swapchain_flags, IDXGISwapChain ** swap_chain)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
DXGI_SWAP_CHAIN_DESC desc = { 0, };
DXGI_SWAP_EFFECT swap_effect = DXGI_SWAP_EFFECT_DISCARD;
IDXGISwapChain *new_swapchain = NULL;
GstD3D11Device *device = window->device;
if (gst_d3d11_is_windows_8_or_greater ())
swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
self->have_swapchain1 = FALSE;
#if (DXGI_HEADER_VERSION >= 2)
{
DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
desc1.Width = 0;
desc1.Height = 0;
desc1.Format = format;
/* FIXME: add support stereo */
desc1.Stereo = FALSE;
desc1.SampleDesc.Count = 1;
desc1.SampleDesc.Quality = 0;
desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc1.BufferCount = 2;
desc1.Scaling = DXGI_SCALING_STRETCH;
/* scaling-stretch would break aspect-ratio so we prefer to use scaling-none,
* but Windows7 does not support this method */
if (gst_d3d11_is_windows_8_or_greater ())
desc1.Scaling = DXGI_SCALING_NONE;
desc1.SwapEffect = swap_effect;
desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
desc1.Flags = swapchain_flags;
new_swapchain = (IDXGISwapChain *)
gst_d3d11_device_create_swap_chain_for_hwnd (device,
self->internal_hwnd, &desc1, NULL, NULL);
if (!new_swapchain) {
GST_WARNING_OBJECT (self, "Failed to create swapchain1");
} else {
self->have_swapchain1 = TRUE;
}
}
#endif
if (!new_swapchain) {
/* we will get client area at on_resize */
desc.BufferDesc.Width = 0;
desc.BufferDesc.Height = 0;
/* don't care refresh rate */
desc.BufferDesc.RefreshRate.Numerator = 0;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = format;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;
desc.SwapEffect = swap_effect;
desc.OutputWindow = self->internal_hwnd;
desc.Windowed = TRUE;
desc.Flags = swapchain_flags;
new_swapchain = gst_d3d11_device_create_swap_chain (device, &desc);
}
if (!new_swapchain) {
GST_ERROR_OBJECT (self, "Cannot create swapchain");
return FALSE;
}
/* disable alt+enter here. It should be manually handled */
gst_d3d11_device_lock (device);
gst_d3d11_window_win32_disable_alt_enter (self,
device, new_swapchain, desc.OutputWindow);
gst_d3d11_device_unlock (device);
*swap_chain = new_swapchain;
gst_d3d11_window_on_resize (window, width, height);
return TRUE;
}
static void
gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
guintptr handle)
{
self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE;
if (self->visible) {
ShowWindow (self->internal_hwnd, SW_HIDE);
self->visible = FALSE;
}
gst_d3d11_window_win32_release_external_handle (self);
self->external_hwnd = (HWND) handle;
gst_d3d11_window_win32_set_external_handle (self);
if (self->external_hwnd)
self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED;
}
static void
gst_d3d11_window_win32_show (GstD3D11Window * window)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
gint width, height;
width = window->width;
height = window->height;
if (!self->visible) {
/* if no parent the real size has to be set now because this has not been done
* when at window creation */
if (!self->external_hwnd) {
RECT rect;
GetClientRect (self->internal_hwnd, &rect);
width += 2 * GetSystemMetrics (SM_CXSIZEFRAME);
height +=
2 * GetSystemMetrics (SM_CYSIZEFRAME) +
GetSystemMetrics (SM_CYCAPTION);
MoveWindow (self->internal_hwnd, rect.left, rect.top, width,
height, FALSE);
}
ShowWindow (self->internal_hwnd, SW_SHOW);
self->visible = TRUE;
}
}
static GstFlowReturn
gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
HRESULT hr;
if ((!self->external_hwnd &&
self->overlay_state == GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED)
|| !self->internal_hwnd) {
GST_ERROR_OBJECT (self, "Output window was closed");
return GST_D3D11_WINDOW_FLOW_CLOSED;
}
#if (DXGI_HEADER_VERSION >= 2)
if (self->have_swapchain1) {
IDXGISwapChain1 *swap_chain1 = (IDXGISwapChain1 *) window->swap_chain;
DXGI_PRESENT_PARAMETERS present_params = { 0, };
/* the first present should not specify dirty-rect */
if (!window->first_present) {
present_params.DirtyRectsCount = 1;
present_params.pDirtyRects = &window->render_rect;
}
hr = swap_chain1->Present1 (0, present_flags, &present_params);
} else
#endif
{
hr = window->swap_chain->Present (0, present_flags);
}
if (!gst_d3d11_result (hr, window->device)) {
GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
(guint) hr);
}
return GST_FLOW_OK;
}
static void
gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
if (self->internal_hwnd)
PostMessage (self->internal_hwnd, WM_SIZE, 0, 0);
return;
}
static void
gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window)
{
GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
if (self->internal_hwnd) {
g_atomic_int_add (&self->pending_fullscreen_count, 1);
PostMessage (self->internal_hwnd, WM_GST_D3D11_FULLSCREEN, 0, 0);
}
}
GstD3D11Window *
gst_d3d11_window_win32_new (GstD3D11Device * device, guintptr handle)
{
GstD3D11Window *window;
g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
window = (GstD3D11Window *) g_object_new (GST_TYPE_D3D11_WINDOW_WIN32,
"d3d11device", device, "window-handle", handle, NULL);
if (!window->initialized) {
gst_object_unref (window);
return NULL;
}
g_object_ref_sink (window);
return window;
}

View file

@ -0,0 +1,40 @@
/*
* GStreamer
* Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_D3D11_WINDOW_WIN32_H__
#define __GST_D3D11_WINDOW_WIN32_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstd3d11_fwd.h"
#include "gstd3d11window.h"
G_BEGIN_DECLS
#define GST_TYPE_D3D11_WINDOW_WIN32 (gst_d3d11_window_win32_get_type())
G_DECLARE_FINAL_TYPE (GstD3D11WindowWin32,
gst_d3d11_window_win32, GST, D3D11_WINDOW_WIN32, GstD3D11Window);
GstD3D11Window * gst_d3d11_window_win32_new (GstD3D11Device * device,
guintptr handle);
G_END_DECLS
#endif /* __GST_D3D11_WINDOW_WIN32_H__ */

View file

@ -4,7 +4,7 @@ d3d11_sources = [
'gstd3d11memory.c',
'gstd3d11utils.c',
'gstd3d11videosink.c',
'gstd3d11window.c',
'gstd3d11window.cpp',
'plugin.c',
'gstd3d11format.c',
'gstd3d11basefilter.c',
@ -44,6 +44,8 @@ extra_c_args = ['-DCOBJMACROS']
have_dxgi_header = false
have_d3d11sdk_h = false
have_dxgidebug_h = false
winapi_desktop = false
winapi_app = false
extra_dep = []
d3d11_conf = configuration_data()
@ -55,6 +57,7 @@ endif
d3d11_lib = cc.find_library('d3d11', required : d3d11_option)
dxgi_lib = cc.find_library('dxgi', required : d3d11_option)
d3dcompiler_lib = cc.find_library('d3dcompiler', required: d3d11_option)
runtimeobject_lib = cc.find_library('runtimeobject', required : false)
foreach dxgi_h: dxgi_headers
if not have_dxgi_header and cc.has_header(dxgi_h[0])
@ -112,14 +115,52 @@ if cc.has_header('dxva.h') and cc.has_header('d3d9.h')
extra_dep += [gstcodecparsers_dep]
endif
winapi_desktop = cxx.compiles('''#include <winapifamily.h>
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#error "not win32"
#endif''',
dependencies: [d3d11_lib, dxgi_lib],
name: 'checking if building for Win32')
if runtimeobject_lib.found()
winapi_app = cxx.compiles('''#include <winapifamily.h>
#include <windows.applicationmodel.core.h>
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#error "not winrt"
#endif''',
dependencies: [d3d11_lib, dxgi_lib, runtimeobject_lib],
name: 'checking if building for WinRT')
endif
if not winapi_desktop and not winapi_app
error('Neither Desktop partition nor App partition')
endif
winapi_app_only = winapi_app and not winapi_desktop
if winapi_app_only
d3d11_sources += ['gstd3d11window_corewindow.cpp',
'gstd3d11window_swapchainpanel.cpp']
extra_dep += [runtimeobject_lib]
else
d3d11_sources += ['gstd3d11window_win32.cpp']
endif
d3d11_conf.set10('GST_D3D11_WINAPI_ONLY_APP', winapi_app_only)
configure_file(
output: 'd3d11config.h',
output: 'gstd3d11config.h',
configuration: d3d11_conf,
)
gstd3d11 = library('gstd3d11',
d3d11_sources,
c_args : gst_plugins_bad_args + extra_c_args,
cpp_args: gst_plugins_bad_args,
include_directories : [configinc],
dependencies : [gstbase_dep, gstvideo_dep, d3d11_lib, dxgi_lib, d3dcompiler_lib] + extra_dep,
install : true,

View file

@ -21,7 +21,7 @@
#include "config.h"
#endif
#include "d3d11config.h"
#include "gstd3d11config.h"
#include <gst/gst.h>
#include "gstd3d11videosink.h"
@ -42,6 +42,7 @@ GST_DEBUG_CATEGORY (gst_d3d11_utils_debug);
GST_DEBUG_CATEGORY (gst_d3d11_format_debug);
GST_DEBUG_CATEGORY (gst_d3d11_device_debug);
GST_DEBUG_CATEGORY (gst_d3d11_overlay_compositor_debug);
GST_DEBUG_CATEGORY (gst_d3d11_window_debug);
#if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
GST_DEBUG_CATEGORY (gst_d3d11_debug_layer_debug);
@ -68,6 +69,8 @@ plugin_init (GstPlugin * plugin)
"d3d11device", 0, "d3d11 device object");
GST_DEBUG_CATEGORY_INIT (gst_d3d11_overlay_compositor_debug,
"d3d11overlaycompositor", 0, "d3d11overlaycompositor");
GST_DEBUG_CATEGORY_INIT (gst_d3d11_window_debug,
"d3d11window", 0, "d3d11window");
#if (HAVE_D3D11SDKLAYERS_H || HAVE_DXGIDEBUG_H)
/* NOTE: enabled only for debug build */