3 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstd3d11window.h"
26 #include "gstd3d11pluginutils.h"
28 #if GST_D3D11_WINAPI_APP
29 /* workaround for GetCurrentTime collision */
33 #include <windows.ui.xaml.h>
34 #include <windows.applicationmodel.core.h>
37 #ifdef HAVE_DIRECT_WRITE
45 using namespace Microsoft::WRL;
49 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
50 #define GST_CAT_DEFAULT gst_d3d11_window_debug
55 struct _GstD3D11WindowPrivate
57 #ifdef HAVE_DIRECT_WRITE
58 IDWriteFactory *dwrite_factory;
59 IDWriteTextFormat *dwrite_format;
61 ID2D1Factory1 *d2d_factory;
62 ID2D1Device *d2d_device;
63 ID2D1DeviceContext *d2d_device_context;
64 ID2D1SolidColorBrush *d2d_brush;
74 PROP_FORCE_ASPECT_RATIO,
75 PROP_ENABLE_NAVIGATION_EVENTS,
76 PROP_FULLSCREEN_TOGGLE_MODE,
82 #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
83 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
84 #define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
85 #define DEFAULT_FULLSCREEN FALSE
86 #define DEFAULT_RENDER_STATS FALSE
95 static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
98 gst_d3d11_window_fullscreen_toggle_mode_type (void)
100 static gsize mode_type = 0;
102 if (g_once_init_enter (&mode_type)) {
103 static const GFlagsValue mode_types[] = {
104 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE,
105 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"},
106 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER,
107 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"},
108 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY,
109 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"},
112 GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
114 g_once_init_leave (&mode_type, tmp);
117 return (GType) mode_type;
120 #define gst_d3d11_window_parent_class parent_class
121 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstD3D11Window, gst_d3d11_window,
124 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
125 const GValue * value, GParamSpec * pspec);
126 static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
127 GValue * value, GParamSpec * pspec);
128 static void gst_d3d11_window_dispose (GObject * object);
129 static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
130 GstBuffer * buffer, GstStructure * stats,
131 ID3D11VideoProcessorOutputView * pov, ID3D11RenderTargetView * rtv);
132 static void gst_d3d11_window_on_resize_default (GstD3D11Window * window,
133 guint width, guint height);
134 static gboolean gst_d3d11_window_prepare_default (GstD3D11Window * window,
135 guint display_width, guint display_height, GstCaps * caps,
136 gboolean * video_processor_available, GError ** error);
139 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
141 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
143 gobject_class->set_property = gst_d3d11_window_set_property;
144 gobject_class->get_property = gst_d3d11_window_get_property;
145 gobject_class->dispose = gst_d3d11_window_dispose;
147 klass->on_resize = GST_DEBUG_FUNCPTR (gst_d3d11_window_on_resize_default);
148 klass->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_prepare_default);
150 g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
151 g_param_spec_object ("d3d11device", "D3D11 Device",
152 "GstD3D11Device object for creating swapchain",
153 GST_TYPE_D3D11_DEVICE,
154 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
155 G_PARAM_STATIC_STRINGS)));
157 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
158 g_param_spec_boolean ("force-aspect-ratio",
159 "Force aspect ratio",
160 "When enabled, scaling will respect original aspect ratio",
161 DEFAULT_FORCE_ASPECT_RATIO,
162 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
164 g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
165 g_param_spec_boolean ("enable-navigation-events",
166 "Enable navigation events",
167 "When enabled, signals for navigation events are emitted",
168 DEFAULT_ENABLE_NAVIGATION_EVENTS,
169 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
171 g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE,
172 g_param_spec_flags ("fullscreen-toggle-mode",
173 "Full screen toggle mode",
174 "Full screen toggle mode used to trigger fullscreen mode change",
175 GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE,
176 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
178 g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
179 g_param_spec_boolean ("fullscreen",
181 "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
183 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
185 g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE,
186 g_param_spec_pointer ("window-handle",
187 "Window Handle", "External Window Handle",
188 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
189 G_PARAM_STATIC_STRINGS)));
191 #ifdef HAVE_DIRECT_WRITE
192 g_object_class_install_property (gobject_class, PROP_RENDER_STATS,
193 g_param_spec_boolean ("render-stats",
195 "Render statistics data (e.g., average framerate) on window",
196 DEFAULT_RENDER_STATS,
197 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
200 d3d11_window_signals[SIGNAL_KEY_EVENT] =
201 g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
202 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
203 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
205 d3d11_window_signals[SIGNAL_MOUSE_EVENT] =
206 g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
207 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
208 G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
212 gst_d3d11_window_init (GstD3D11Window * self)
214 self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
215 self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
216 self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
217 self->fullscreen = DEFAULT_FULLSCREEN;
218 self->render_stats = DEFAULT_RENDER_STATS;
221 (GstD3D11WindowPrivate *) gst_d3d11_window_get_instance_private (self);
225 gst_d3d11_window_set_property (GObject * object, guint prop_id,
226 const GValue * value, GParamSpec * pspec)
228 GstD3D11Window *self = GST_D3D11_WINDOW (object);
229 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
232 case PROP_D3D11_DEVICE:
233 self->device = (GstD3D11Device *) g_value_dup_object (value);
235 case PROP_FORCE_ASPECT_RATIO:
237 self->force_aspect_ratio = g_value_get_boolean (value);
238 if (self->swap_chain)
239 klass->update_swap_chain (self);
242 case PROP_ENABLE_NAVIGATION_EVENTS:
243 self->enable_navigation_events = g_value_get_boolean (value);
245 case PROP_FULLSCREEN_TOGGLE_MODE:
246 self->fullscreen_toggle_mode =
247 (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
249 case PROP_FULLSCREEN:
251 self->requested_fullscreen = g_value_get_boolean (value);
252 if (self->swap_chain)
253 klass->change_fullscreen_mode (self);
256 case PROP_WINDOW_HANDLE:
257 self->external_handle = (guintptr) g_value_get_pointer (value);
259 #ifdef HAVE_DIRECT_WRITE
260 case PROP_RENDER_STATS:
261 self->render_stats = g_value_get_boolean (value);
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
271 gst_d3d11_window_get_property (GObject * object, guint prop_id,
272 GValue * value, GParamSpec * pspec)
274 GstD3D11Window *self = GST_D3D11_WINDOW (object);
277 case PROP_ENABLE_NAVIGATION_EVENTS:
278 g_value_set_boolean (value, self->enable_navigation_events);
280 case PROP_FORCE_ASPECT_RATIO:
281 g_value_set_boolean (value, self->force_aspect_ratio);
283 case PROP_FULLSCREEN_TOGGLE_MODE:
284 g_value_set_flags (value, self->fullscreen_toggle_mode);
286 case PROP_FULLSCREEN:
287 g_value_set_boolean (value, self->fullscreen);
290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295 #ifdef HAVE_DIRECT_WRITE
297 gst_d3d11_window_release_dwrite_resources (GstD3D11Window * self)
299 GstD3D11WindowPrivate *priv = self->priv;
301 GST_D3D11_CLEAR_COM (priv->d2d_device_context);
302 GST_D3D11_CLEAR_COM (priv->d2d_factory);
303 GST_D3D11_CLEAR_COM (priv->d2d_device);
304 GST_D3D11_CLEAR_COM (priv->d2d_brush);
305 GST_D3D11_CLEAR_COM (priv->dwrite_factory);
306 GST_D3D11_CLEAR_COM (priv->dwrite_format);
310 gst_d3d11_window_prepare_dwrite_device (GstD3D11Window * self)
312 GstD3D11WindowPrivate *priv = self->priv;
315 ComPtr<IDXGIDevice> dxgi_device;
317 ID3D11Device *device_handle;
320 GST_ERROR_OBJECT (self, "D3D11Device is unavailable");
324 /* Already prepared */
325 if (priv->d2d_device)
328 hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
329 IID_PPV_ARGS (&priv->d2d_factory));
330 if (!gst_d3d11_result (hr, self->device))
333 device_handle = gst_d3d11_device_get_device_handle (self->device);
334 hr = device_handle->QueryInterface (IID_PPV_ARGS (&dxgi_device));
335 if (!gst_d3d11_result (hr, self->device))
338 hr = priv->d2d_factory->CreateDevice (dxgi_device.Get (), &priv->d2d_device);
339 if (!gst_d3d11_result (hr, self->device))
342 hr = priv->d2d_device->CreateDeviceContext
343 (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
344 &priv->d2d_device_context);
345 if (!gst_d3d11_result (hr, self->device))
349 d2d_device_context->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::
350 Yellow), &priv->d2d_brush);
351 if (!gst_d3d11_result (hr, self->device))
354 hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
355 __uuidof (IDWriteFactory), (IUnknown **) & priv->dwrite_factory);
356 if (!gst_d3d11_result (hr, self->device))
360 hr = priv->dwrite_factory->CreateTextFormat (L"Arial", NULL,
361 DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
362 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-US", &priv->dwrite_format);
363 if (!gst_d3d11_result (hr, self->device))
366 GST_DEBUG_OBJECT (self, "Direct2D device is prepared");
371 gst_d3d11_window_release_dwrite_resources (self);
375 gst_d3d11_window_dwrite_on_resize (GstD3D11Window * self,
376 ID3D11Texture2D * backbuffer)
378 GstD3D11WindowPrivate *priv = self->priv;
380 ComPtr<IDXGISurface> dxgi_surface;
381 ComPtr<ID2D1Bitmap1> bitmap;
383 D2D1_BITMAP_PROPERTIES1 prop;
386 if (!priv->d2d_device)
389 prop.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
390 prop.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
395 D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
396 prop.colorContext = NULL;
398 hr = backbuffer->QueryInterface (IID_PPV_ARGS (&dxgi_surface));
399 if (!gst_d3d11_result (hr, self->device))
402 hr = priv->d2d_device_context->
403 CreateBitmapFromDxgiSurface (dxgi_surface.Get (), &prop, &bitmap);
404 if (!gst_d3d11_result (hr, self->device))
407 priv->d2d_device_context->SetTarget (bitmap.Get ());
409 GST_LOG_OBJECT (self, "New D2D bitmap has been configured");
414 gst_d3d11_window_release_dwrite_resources (self);
420 gst_d3d11_window_release_resources (GstD3D11Device * device,
421 GstD3D11Window * window)
423 #ifdef HAVE_DIRECT_WRITE
424 gst_d3d11_window_release_dwrite_resources (window);
427 GST_D3D11_CLEAR_COM (window->rtv);
428 GST_D3D11_CLEAR_COM (window->pov);
429 GST_D3D11_CLEAR_COM (window->swap_chain);
433 gst_d3d11_window_dispose (GObject * object)
435 GstD3D11Window *self = GST_D3D11_WINDOW (object);
438 gst_d3d11_window_release_resources (self->device, self);
441 g_clear_pointer (&self->processor, gst_d3d11_video_processor_free);
442 g_clear_pointer (&self->converter, gst_d3d11_converter_free);
443 g_clear_pointer (&self->compositor, gst_d3d11_overlay_compositor_free);
445 gst_clear_buffer (&self->cached_buffer);
446 gst_clear_object (&self->device);
448 G_OBJECT_CLASS (parent_class)->dispose (object);
452 gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
455 #ifdef HAVE_DIRECT_WRITE
456 GstD3D11WindowPrivate *priv = window->priv;
459 ID3D11Device *device_handle;
460 D3D11_TEXTURE2D_DESC desc;
461 DXGI_SWAP_CHAIN_DESC swap_desc;
462 ID3D11Texture2D *backbuffer = NULL;
463 GstVideoRectangle src_rect, dst_rect, rst_rect;
464 IDXGISwapChain *swap_chain;
466 gst_d3d11_device_lock (window->device);
467 if (!window->swap_chain)
470 device_handle = gst_d3d11_device_get_device_handle (window->device);
471 swap_chain = window->swap_chain;
473 GST_D3D11_CLEAR_COM (window->rtv);
474 GST_D3D11_CLEAR_COM (window->pov);
476 #ifdef HAVE_DIRECT_WRITE
477 /* D2D bitmap need to be cleared before resizing swapchain buffer */
478 if (priv->d2d_device_context)
479 priv->d2d_device_context->SetTarget (NULL);
482 swap_chain->GetDesc (&swap_desc);
483 hr = swap_chain->ResizeBuffers (0, width, height, window->dxgi_format,
485 if (!gst_d3d11_result (hr, window->device)) {
486 GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
490 hr = swap_chain->GetBuffer (0, IID_ID3D11Texture2D, (void **) &backbuffer);
491 if (!gst_d3d11_result (hr, window->device)) {
492 GST_ERROR_OBJECT (window,
493 "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
497 backbuffer->GetDesc (&desc);
498 window->surface_width = desc.Width;
499 window->surface_height = desc.Height;
504 dst_rect.w = window->surface_width;
505 dst_rect.h = window->surface_height;
507 if (window->force_aspect_ratio) {
510 src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
511 src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
513 gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
519 window->render_rect.left = rst_rect.x;
520 window->render_rect.top = rst_rect.y;
521 window->render_rect.right = rst_rect.x + rst_rect.w;
522 window->render_rect.bottom = rst_rect.y + rst_rect.h;
524 GST_LOG_OBJECT (window,
525 "New client area %dx%d, render rect x: %d, y: %d, %dx%d",
526 desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h);
528 hr = device_handle->CreateRenderTargetView ((ID3D11Resource *) backbuffer,
530 if (!gst_d3d11_result (hr, window->device)) {
531 GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x",
537 if (window->processor) {
538 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC pov_desc;
540 pov_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
541 pov_desc.Texture2D.MipSlice = 0;
543 if (!gst_d3d11_video_processor_create_output_view (window->processor,
544 &pov_desc, (ID3D11Resource *) backbuffer, &window->pov))
547 #ifdef HAVE_DIRECT_WRITE
548 if (window->render_stats)
549 gst_d3d11_window_dwrite_on_resize (window, backbuffer);
552 window->first_present = TRUE;
554 /* redraw the last scene if cached buffer exits */
555 if (window->cached_buffer) {
556 gst_d3d111_window_present (window, window->cached_buffer, NULL,
557 window->pov, window->rtv);
561 GST_D3D11_CLEAR_COM (backbuffer);
563 gst_d3d11_device_unlock (window->device);
567 gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
570 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
572 if (!window->enable_navigation_events)
575 g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
579 gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
580 gint button, gdouble x, gdouble y)
582 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
584 if (!window->enable_navigation_events)
587 g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
588 event, button, x, y);
593 DXGI_FORMAT dxgi_format;
594 GstVideoFormat gst_format;
596 } GstD3D11WindowDisplayFormat;
599 gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
600 guint display_height, GstCaps * caps, gboolean * video_processor_available,
603 GstD3D11WindowClass *klass;
605 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
607 klass = GST_D3D11_WINDOW_GET_CLASS (window);
608 g_assert (klass->prepare != NULL);
610 GST_DEBUG_OBJECT (window, "Prepare window, display resolution %dx%d, caps %"
611 GST_PTR_FORMAT, display_width, display_height, caps);
613 return klass->prepare (window, display_width, display_height, caps,
614 video_processor_available, error);
618 gst_d3d11_window_prepare_default (GstD3D11Window * window, guint display_width,
619 guint display_height, GstCaps * caps, gboolean * video_processor_available,
622 GstD3D11WindowClass *klass;
623 guint swapchain_flags = 0;
624 ID3D11Device *device_handle;
626 guint num_supported_format = 0;
629 D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY;
630 UINT supported_flags = 0;
631 GstD3D11WindowDisplayFormat formats[] = {
632 {DXGI_FORMAT_R8G8B8A8_UNORM, GST_VIDEO_FORMAT_RGBA, FALSE},
633 {DXGI_FORMAT_B8G8R8A8_UNORM, GST_VIDEO_FORMAT_BGRA, FALSE},
634 {DXGI_FORMAT_R10G10B10A2_UNORM, GST_VIDEO_FORMAT_RGB10A2_LE, FALSE},
636 const GstD3D11WindowDisplayFormat *chosen_format = NULL;
637 const GstDxgiColorSpace *chosen_colorspace = NULL;
638 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
639 gboolean have_hdr10 = FALSE;
640 DXGI_COLOR_SPACE_TYPE native_colorspace_type =
641 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
643 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
644 DXGI_HDR_METADATA_HDR10 hdr10_metadata = { 0, };
647 /* Step 1: Clear old resources and objects */
648 gst_clear_buffer (&window->cached_buffer);
649 g_clear_pointer (&window->processor, gst_d3d11_video_processor_free);
650 g_clear_pointer (&window->converter, gst_d3d11_converter_free);
651 g_clear_pointer (&window->compositor, gst_d3d11_overlay_compositor_free);
653 window->processor_in_use = FALSE;
655 /* Step 2: Decide display color format
656 * If upstream format is 10bits, try DXGI_FORMAT_R10G10B10A2_UNORM first
657 * Otherwise, use DXGI_FORMAT_B8G8R8A8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM
659 gst_video_info_from_caps (&window->info, caps);
660 device_handle = gst_d3d11_device_get_device_handle (window->device);
661 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
662 hr = device_handle->CheckFormatSupport (formats[i].dxgi_format,
664 if (SUCCEEDED (hr) && (supported_flags & display_flags) == display_flags) {
665 GST_DEBUG_OBJECT (window, "Device supports format %s (DXGI_FORMAT %d)",
666 gst_video_format_to_string (formats[i].gst_format),
667 formats[i].dxgi_format);
668 formats[i].supported = TRUE;
669 num_supported_format++;
673 if (num_supported_format == 0) {
674 GST_ERROR_OBJECT (window, "Cannot determine render format");
675 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
676 "Cannot determine render format");
679 #ifdef HAVE_DIRECT_WRITE
680 if (window->render_stats && formats[1].supported) {
681 /* FIXME: D2D seems to be accepting only DXGI_FORMAT_B8G8R8A8_UNORM */
682 chosen_format = &formats[1];
686 for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
687 if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
688 if (formats[2].supported) {
689 chosen_format = &formats[2];
697 if (!chosen_format) {
698 /* prefer native format over conversion */
699 for (i = 0; i < 2; i++) {
700 if (formats[i].supported &&
701 formats[i].gst_format == GST_VIDEO_INFO_FORMAT (&window->info)) {
702 chosen_format = &formats[i];
707 /* choose any color space then */
708 if (!chosen_format) {
709 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
710 if (formats[i].supported) {
711 chosen_format = &formats[i];
718 g_assert (chosen_format != NULL);
720 GST_DEBUG_OBJECT (window, "chosen rendero format %s (DXGI_FORMAT %d)",
721 gst_video_format_to_string (chosen_format->gst_format),
722 chosen_format->dxgi_format);
724 /* Step 3: Create swapchain
725 * (or reuse old swapchain if the format is not changed) */
726 window->allow_tearing = FALSE;
727 g_object_get (window->device, "allow-tearing", &window->allow_tearing, NULL);
728 if (window->allow_tearing) {
729 GST_DEBUG_OBJECT (window, "device support tearning");
730 swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
733 gst_d3d11_device_lock (window->device);
734 window->dxgi_format = chosen_format->dxgi_format;
736 klass = GST_D3D11_WINDOW_GET_CLASS (window);
737 if (!window->swap_chain &&
738 !klass->create_swap_chain (window, window->dxgi_format,
739 display_width, display_height, swapchain_flags,
740 &window->swap_chain)) {
741 GST_ERROR_OBJECT (window, "Cannot create swapchain");
742 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
743 "Cannot create swapchain");
747 /* this rect struct will be used to calculate render area */
748 window->render_rect.left = 0;
749 window->render_rect.top = 0;
750 window->render_rect.right = display_width;
751 window->render_rect.bottom = display_height;
753 window->input_rect.left = 0;
754 window->input_rect.top = 0;
755 window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
756 window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
758 /* Step 4: Decide render color space and set it on converter/processor */
760 /* check HDR10 metadata. If HDR APIs are available, BT2020 primaries colorspcae
763 * FIXME: need to query h/w level support. If that's not the case, tone-mapping
764 * should be placed somewhere.
766 * FIXME: Non-HDR colorspace with BT2020 primaries will break rendering.
767 * https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/1175
768 * To workaround it, BT709 colorspace will be chosen for non-HDR case.
770 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
772 GstVideoMasteringDisplayInfo minfo;
773 GstVideoContentLightLevel cll;
775 if (gst_video_mastering_display_info_from_caps (&minfo, caps) &&
776 gst_video_content_light_level_from_caps (&cll, caps)) {
777 IDXGISwapChain4 *swapchain4 = NULL;
780 hr = window->swap_chain->QueryInterface (IID_IDXGISwapChain4,
781 (void **) &swapchain4);
782 if (gst_d3d11_result (hr, window->device)) {
783 GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain");
785 gst_d3d11_hdr_meta_data_to_dxgi (&minfo, &cll, &hdr10_metadata);
787 hr = swapchain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10,
788 sizeof (DXGI_HDR_METADATA_HDR10), &hdr10_metadata);
789 if (!gst_d3d11_result (hr, window->device)) {
790 GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x",
796 swapchain4->Release ();
802 /* Step 5: Choose display color space */
803 gst_video_info_set_format (&window->render_info,
804 chosen_format->gst_format, display_width, display_height);
806 /* preserve upstream colorimetry */
807 window->render_info.colorimetry.primaries =
808 window->info.colorimetry.primaries;
809 window->render_info.colorimetry.transfer = window->info.colorimetry.transfer;
810 /* prefer FULL range RGB. STUDIO range doesn't seem to be well supported
811 * color space by GPUs and we don't need to preserve color range for
812 * target display color space type */
813 window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
815 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
817 IDXGISwapChain3 *swapchain3 = NULL;
820 hr = window->swap_chain->QueryInterface (IID_IDXGISwapChain3,
821 (void **) &swapchain3);
823 if (gst_d3d11_result (hr, window->device)) {
825 gst_d3d11_find_swap_chain_color_space (&window->render_info,
826 swapchain3, have_hdr10);
827 if (chosen_colorspace) {
828 native_colorspace_type =
829 (DXGI_COLOR_SPACE_TYPE) chosen_colorspace->dxgi_color_space_type;
830 hr = swapchain3->SetColorSpace1 (native_colorspace_type);
831 if (!gst_d3d11_result (hr, window->device)) {
832 GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x",
833 native_colorspace_type, (guint) hr);
834 chosen_colorspace = NULL;
835 native_colorspace_type = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
837 GST_DEBUG_OBJECT (window,
838 "Set colorspace %d", native_colorspace_type);
840 /* update with selected display color space */
841 window->render_info.colorimetry.primaries =
842 chosen_colorspace->primaries;
843 window->render_info.colorimetry.transfer =
844 chosen_colorspace->transfer;
845 window->render_info.colorimetry.range = chosen_colorspace->range;
846 window->render_info.colorimetry.matrix = chosen_colorspace->matrix;
850 swapchain3->Release ();
855 /* otherwise, use most common DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
857 if (!chosen_colorspace) {
858 GST_DEBUG_OBJECT (window, "No selected render color space, use BT709");
859 window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
860 window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
861 window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
863 #if (GST_D3D11_DXGI_HEADER_VERSION >= 4)
864 if (chosen_colorspace) {
865 const GstDxgiColorSpace *in_color_space =
866 gst_d3d11_video_info_to_dxgi_color_space (&window->info);
867 const GstD3D11Format *in_format =
868 gst_d3d11_device_format_from_gst (window->device,
869 GST_VIDEO_INFO_FORMAT (&window->info));
870 gboolean hardware = FALSE;
871 GstD3D11VideoProcessor *processor = NULL;
873 if (in_color_space && in_format &&
874 in_format->dxgi_format != DXGI_FORMAT_UNKNOWN) {
875 g_object_get (window->device, "hardware", &hardware, NULL);
880 gst_d3d11_video_processor_new (window->device,
881 GST_VIDEO_INFO_WIDTH (&window->info),
882 GST_VIDEO_INFO_HEIGHT (&window->info), display_width, display_height);
886 DXGI_FORMAT in_dxgi_format = in_format->dxgi_format;
887 DXGI_FORMAT out_dxgi_format = chosen_format->dxgi_format;
888 DXGI_COLOR_SPACE_TYPE in_dxgi_color_space =
889 (DXGI_COLOR_SPACE_TYPE) in_color_space->dxgi_color_space_type;
890 DXGI_COLOR_SPACE_TYPE out_dxgi_color_space = native_colorspace_type;
892 if (!gst_d3d11_video_processor_check_format_conversion (processor,
893 in_dxgi_format, in_dxgi_color_space, out_dxgi_format,
894 out_dxgi_color_space)) {
895 GST_DEBUG_OBJECT (window, "Conversion is not supported by device");
896 gst_d3d11_video_processor_free (processor);
899 GST_DEBUG_OBJECT (window, "video processor supports conversion");
900 gst_d3d11_video_processor_set_input_dxgi_color_space (processor,
901 in_dxgi_color_space);
902 gst_d3d11_video_processor_set_output_dxgi_color_space (processor,
903 out_dxgi_color_space);
905 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
907 GST_DEBUG_OBJECT (window, "Set HDR metadata on video processor");
908 gst_d3d11_video_processor_set_input_hdr10_metadata (processor,
910 gst_d3d11_video_processor_set_output_hdr10_metadata (processor,
916 window->processor = processor;
920 *video_processor_available = !!window->processor;
922 /* configure shader even if video processor is available for fallback */
924 gst_d3d11_converter_new (window->device, &window->info,
925 &window->render_info);
927 if (!window->converter) {
928 GST_ERROR_OBJECT (window, "Cannot create converter");
929 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
930 "Cannot create converter");
935 gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
936 if (!window->compositor) {
937 GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
938 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
939 "Cannot create overlay compositor");
942 #ifdef HAVE_DIRECT_WRITE
943 if (window->render_stats)
944 gst_d3d11_window_prepare_dwrite_device (window);
946 gst_d3d11_device_unlock (window->device);
948 /* call resize to allocated resources */
949 klass->on_resize (window, display_width, display_height);
951 if (window->requested_fullscreen != window->fullscreen) {
952 klass->change_fullscreen_mode (window);
955 GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
960 gst_d3d11_device_unlock (window->device);
966 gst_d3d11_window_show (GstD3D11Window * window)
968 GstD3D11WindowClass *klass;
970 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
972 klass = GST_D3D11_WINDOW_GET_CLASS (window);
975 klass->show (window);
979 gst_d3d11_window_set_render_rectangle (GstD3D11Window * window, gint x, gint y,
980 gint width, gint height)
982 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
984 /* TODO: resize window and view */
988 gst_d3d11_window_buffer_ensure_processor_input (GstD3D11Window * self,
989 GstBuffer * buffer, ID3D11VideoProcessorInputView ** in_view)
992 ID3D11VideoProcessorInputView *piv;
994 if (!self->processor)
997 if (gst_buffer_n_memory (buffer) != 1)
1000 mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
1001 piv = gst_d3d11_video_processor_get_input_view (self->processor, mem);
1003 GST_LOG_OBJECT (self, "Failed to get processor input view");
1012 #ifdef HAVE_DIRECT_WRITE
1014 gst_d3d11_window_present_d2d (GstD3D11Window * self, GstStructure * stats)
1016 GstD3D11WindowPrivate *priv = self->priv;
1018 gdouble framerate = 0.0;
1019 guint64 dropped = 0;
1020 guint64 rendered = 0;
1021 std::wostringstream stats_str;
1023 ComPtr<IDWriteTextLayout> layout;
1028 if (!priv->d2d_device)
1034 gst_structure_get_double (stats, "average-rate", &framerate);
1035 gst_structure_get_uint64 (stats, "dropped", &dropped);
1036 gst_structure_get_uint64 (stats, "rendered", &rendered);
1038 stats_str.precision (5);
1039 stats_str << "Average-rate: " << framerate << std::endl;
1040 stats_str << "Dropped: " << dropped << std::endl;
1041 stats_str << "Rendered: " << rendered << std::endl;
1043 hr = priv->dwrite_factory->CreateTextLayout (stats_str.str ().c_str (),
1044 (UINT32) stats_str.str ().size (), priv->dwrite_format,
1045 self->render_rect.right - self->render_rect.left,
1046 self->render_rect.bottom - self->render_rect.top, &layout);
1047 if (!gst_d3d11_result (hr, self->device))
1050 left = self->render_rect.left + 5.0f;
1051 top = self->render_rect.top + 5.0f;
1053 priv->d2d_device_context->BeginDraw ();
1054 priv->d2d_device_context->DrawTextLayout (D2D1::Point2F (left, top),
1055 layout.Get (), priv->d2d_brush);
1057 hr = priv->d2d_device_context->EndDraw ();
1058 gst_d3d11_result (hr, self->device);
1065 gst_d3d11_window_do_processor (GstD3D11Window * self,
1066 ID3D11VideoProcessorInputView * piv, ID3D11VideoProcessorOutputView * pov)
1070 ret = gst_d3d11_video_processor_render_unlocked (self->processor,
1071 &self->input_rect, piv, &self->render_rect, pov);
1073 GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using processor");
1075 GST_TRACE_OBJECT (self, "Rendered using processor");
1076 self->processor_in_use = TRUE;
1083 gst_d3d11_window_do_convert (GstD3D11Window * self,
1084 ID3D11ShaderResourceView * srv[GST_VIDEO_MAX_PLANES],
1085 ID3D11RenderTargetView * rtv)
1087 if (!gst_d3d11_converter_convert_unlocked (self->converter,
1088 srv, &rtv, NULL, NULL)) {
1089 GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using converter");
1092 GST_TRACE_OBJECT (self, "Rendered using converter");
1098 static GstFlowReturn
1099 gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
1100 GstStructure * stats, ID3D11VideoProcessorOutputView * pov,
1101 ID3D11RenderTargetView * rtv)
1103 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
1104 GstFlowReturn ret = GST_FLOW_OK;
1105 guint present_flags = 0;
1111 GstMapInfo infos[GST_VIDEO_MAX_PLANES];
1112 ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES];
1113 ID3D11VideoProcessorInputView *piv = NULL;
1114 ID3D11Device *device_handle =
1115 gst_d3d11_device_get_device_handle (self->device);
1116 gboolean can_convert = FALSE;
1117 gboolean can_process = FALSE;
1118 gboolean convert_ret = FALSE;
1120 /* Map memory in any case so that we can upload pending stage texture */
1121 if (!gst_d3d11_buffer_map (buffer, device_handle, infos, GST_MAP_READ)) {
1122 GST_ERROR_OBJECT (self, "Couldn't map buffer");
1123 return GST_FLOW_ERROR;
1126 can_convert = gst_d3d11_buffer_get_shader_resource_view (buffer, srv);
1128 can_process = gst_d3d11_window_buffer_ensure_processor_input (self,
1132 if (!can_convert && !can_process) {
1133 GST_ERROR_OBJECT (self, "Input texture cannot be used for converter");
1134 return GST_FLOW_ERROR;
1137 if (self->first_present) {
1138 D3D11_VIEWPORT viewport;
1140 viewport.TopLeftX = self->render_rect.left;
1141 viewport.TopLeftY = self->render_rect.top;
1142 viewport.Width = self->render_rect.right - self->render_rect.left;
1143 viewport.Height = self->render_rect.bottom - self->render_rect.top;
1144 viewport.MinDepth = 0.0f;
1145 viewport.MaxDepth = 1.0f;
1146 gst_d3d11_converter_update_viewport (self->converter, &viewport);
1147 gst_d3d11_overlay_compositor_update_viewport (self->compositor,
1151 /* Converter preference order
1152 * 1) If this texture can be converted via processor, and we used processor
1153 * previously, use processor
1154 * 2) If SRV is available, use converter
1155 * 3) otherwise, use processor
1157 if (can_process && self->processor_in_use) {
1158 convert_ret = gst_d3d11_window_do_processor (self, piv, pov);
1159 } else if (can_convert) {
1160 convert_ret = gst_d3d11_window_do_convert (self, srv, rtv);
1161 } else if (can_process) {
1162 convert_ret = gst_d3d11_window_do_processor (self, piv, pov);
1164 g_assert_not_reached ();
1165 ret = GST_FLOW_ERROR;
1170 ret = GST_FLOW_ERROR;
1174 gst_d3d11_overlay_compositor_upload (self->compositor, buffer);
1175 gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &rtv);
1177 #if (GST_D3D11_DXGI_HEADER_VERSION >= 5)
1178 if (self->allow_tearing && self->fullscreen) {
1179 present_flags |= DXGI_PRESENT_ALLOW_TEARING;
1183 #if HAVE_DIRECT_WRITE
1184 gst_d3d11_window_present_d2d (self, stats);
1188 ret = klass->present (self, present_flags);
1190 self->first_present = FALSE;
1193 gst_d3d11_buffer_unmap (buffer, infos);
1200 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
1201 GstVideoRectangle * rect, GstStructure * stats)
1206 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1207 g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR);
1209 mem = gst_buffer_peek_memory (buffer, 0);
1210 if (!gst_is_d3d11_memory (mem)) {
1211 GST_ERROR_OBJECT (window, "Invalid buffer");
1214 gst_structure_free (stats);
1216 return GST_FLOW_ERROR;
1219 gst_d3d11_device_lock (window->device);
1220 gst_buffer_replace (&window->cached_buffer, buffer);
1222 ret = gst_d3d111_window_present (window, window->cached_buffer, stats,
1223 window->pov, window->rtv);
1224 gst_d3d11_device_unlock (window->device);
1227 gst_structure_free (stats);
1233 gst_d3d11_window_render_on_shared_handle (GstD3D11Window * window,
1234 GstBuffer * buffer, HANDLE shared_handle, guint texture_misc_flags,
1235 guint64 acquire_key, guint64 release_key)
1237 GstD3D11WindowClass *klass;
1239 GstFlowReturn ret = GST_FLOW_OK;
1240 GstD3D11WindowSharedHandleData data = { NULL, };
1241 ID3D11VideoProcessorOutputView *pov = NULL;
1242 ID3D11RenderTargetView *rtv = NULL;
1244 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1246 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1248 g_assert (klass->open_shared_handle != NULL);
1249 g_assert (klass->release_shared_handle != NULL);
1251 mem = gst_buffer_peek_memory (buffer, 0);
1252 if (!gst_is_d3d11_memory (mem)) {
1253 GST_ERROR_OBJECT (window, "Invalid buffer");
1255 return GST_FLOW_ERROR;
1258 data.shared_handle = shared_handle;
1259 data.texture_misc_flags = texture_misc_flags;
1260 data.acquire_key = acquire_key;
1261 data.release_key = release_key;
1263 gst_d3d11_device_lock (window->device);
1264 if (!klass->open_shared_handle (window, &data)) {
1265 GST_ERROR_OBJECT (window, "Couldn't open shared handle");
1266 gst_d3d11_device_unlock (window->device);
1270 if (data.fallback_rtv) {
1271 rtv = data.fallback_rtv;
1272 pov = data.fallback_pov;
1278 ret = gst_d3d111_window_present (window, buffer, NULL, pov, rtv);
1280 klass->release_shared_handle (window, &data);
1281 gst_d3d11_device_unlock (window->device);
1287 gst_d3d11_window_unlock (GstD3D11Window * window)
1289 GstD3D11WindowClass *klass;
1290 gboolean ret = TRUE;
1292 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1294 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1297 ret = klass->unlock (window);
1303 gst_d3d11_window_unlock_stop (GstD3D11Window * window)
1305 GstD3D11WindowClass *klass;
1306 gboolean ret = TRUE;
1308 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1310 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1312 if (klass->unlock_stop)
1313 ret = klass->unlock_stop (window);
1315 gst_d3d11_device_lock (window->device);
1316 gst_clear_buffer (&window->cached_buffer);
1317 gst_d3d11_device_unlock (window->device);
1323 gst_d3d11_window_unprepare (GstD3D11Window * window)
1325 GstD3D11WindowClass *klass;
1327 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
1329 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1331 if (klass->unprepare)
1332 klass->unprepare (window);
1335 GstD3D11WindowNativeType
1336 gst_d3d11_window_get_native_type_from_handle (guintptr handle)
1339 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1341 #if (!GST_D3D11_WINAPI_ONLY_APP)
1342 if (IsWindow ((HWND) handle))
1343 return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
1345 #if GST_D3D11_WINAPI_ONLY_APP
1348 ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
1349 ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
1350 ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
1353 if (SUCCEEDED (window.As (&core_window)))
1354 return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
1356 if (SUCCEEDED (window.As (&panel)))
1357 return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
1361 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1365 gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
1368 case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
1370 case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
1372 case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
1373 return "core-window";
1374 case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
1375 return "swap-chain-panel";