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>
40 using namespace Microsoft::WRL;
43 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
44 #define GST_CAT_DEFAULT gst_d3d11_window_debug
51 PROP_FORCE_ASPECT_RATIO,
52 PROP_ENABLE_NAVIGATION_EVENTS,
53 PROP_FULLSCREEN_TOGGLE_MODE,
60 #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
61 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
62 #define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
63 #define DEFAULT_FULLSCREEN FALSE
64 #define DEFAULT_EMIT_PRESENT FALSE
74 static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
77 gst_d3d11_window_fullscreen_toggle_mode_type (void)
79 static GType mode_type = 0;
81 GST_D3D11_CALL_ONCE_BEGIN {
82 static const GFlagsValue mode_types[] = {
83 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE,
84 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"},
85 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER,
86 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"},
87 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY,
88 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"},
89 {0, nullptr, nullptr},
92 mode_type = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
94 } GST_D3D11_CALL_ONCE_END;
99 #define gst_d3d11_window_parent_class parent_class
100 G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
102 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
103 const GValue * value, GParamSpec * pspec);
104 static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
105 GValue * value, GParamSpec * pspec);
106 static void gst_d3d11_window_dispose (GObject * object);
107 static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
108 GstBuffer * buffer, GstBuffer * render_target);
109 static void gst_d3d11_window_on_resize_default (GstD3D11Window * window,
110 guint width, guint height);
111 static GstFlowReturn gst_d3d11_window_prepare_default (GstD3D11Window * window,
112 guint display_width, guint display_height, GstCaps * caps,
113 GstStructure * config, DXGI_FORMAT display_format, GError ** error);
116 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
118 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120 gobject_class->set_property = gst_d3d11_window_set_property;
121 gobject_class->get_property = gst_d3d11_window_get_property;
122 gobject_class->dispose = gst_d3d11_window_dispose;
124 klass->on_resize = GST_DEBUG_FUNCPTR (gst_d3d11_window_on_resize_default);
125 klass->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_prepare_default);
127 g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
128 g_param_spec_object ("d3d11device", "D3D11 Device",
129 "GstD3D11Device object for creating swapchain",
130 GST_TYPE_D3D11_DEVICE,
131 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
132 G_PARAM_STATIC_STRINGS)));
134 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
135 g_param_spec_boolean ("force-aspect-ratio",
136 "Force aspect ratio",
137 "When enabled, scaling will respect original aspect ratio",
138 DEFAULT_FORCE_ASPECT_RATIO,
139 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
141 g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
142 g_param_spec_boolean ("enable-navigation-events",
143 "Enable navigation events",
144 "When enabled, signals for navigation events are emitted",
145 DEFAULT_ENABLE_NAVIGATION_EVENTS,
146 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
148 g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE,
149 g_param_spec_flags ("fullscreen-toggle-mode",
150 "Full screen toggle mode",
151 "Full screen toggle mode used to trigger fullscreen mode change",
152 GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE,
153 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
155 g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
156 g_param_spec_boolean ("fullscreen",
158 "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
160 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
162 g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE,
163 g_param_spec_pointer ("window-handle",
164 "Window Handle", "External Window Handle",
165 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
166 G_PARAM_STATIC_STRINGS)));
168 g_object_class_install_property (gobject_class, PROP_EMIT_PRESENT,
169 g_param_spec_boolean ("emit-present", "Emit Present",
170 "Emit present signal", DEFAULT_EMIT_PRESENT,
171 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
173 d3d11_window_signals[SIGNAL_KEY_EVENT] =
174 g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
175 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
176 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
178 d3d11_window_signals[SIGNAL_MOUSE_EVENT] =
179 g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
180 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
181 G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
183 d3d11_window_signals[SIGNAL_PRESENT] =
184 g_signal_new ("present", G_TYPE_FROM_CLASS (klass),
185 G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
186 G_TYPE_NONE, 2, GST_TYPE_D3D11_DEVICE, G_TYPE_POINTER);
190 gst_d3d11_window_init (GstD3D11Window * self)
192 self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
193 self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
194 self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
195 self->fullscreen = DEFAULT_FULLSCREEN;
196 self->emit_present = DEFAULT_EMIT_PRESENT;
200 gst_d3d11_window_set_property (GObject * object, guint prop_id,
201 const GValue * value, GParamSpec * pspec)
203 GstD3D11Window *self = GST_D3D11_WINDOW (object);
204 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
207 case PROP_D3D11_DEVICE:
208 self->device = (GstD3D11Device *) g_value_dup_object (value);
210 case PROP_FORCE_ASPECT_RATIO:
212 self->force_aspect_ratio = g_value_get_boolean (value);
213 if (self->swap_chain)
214 klass->update_swap_chain (self);
217 case PROP_ENABLE_NAVIGATION_EVENTS:
218 self->enable_navigation_events = g_value_get_boolean (value);
220 case PROP_FULLSCREEN_TOGGLE_MODE:
221 self->fullscreen_toggle_mode =
222 (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
224 case PROP_FULLSCREEN:
226 self->requested_fullscreen = g_value_get_boolean (value);
227 if (self->swap_chain)
228 klass->change_fullscreen_mode (self);
231 case PROP_WINDOW_HANDLE:
232 self->external_handle = (guintptr) g_value_get_pointer (value);
234 case PROP_EMIT_PRESENT:
235 self->emit_present = g_value_get_boolean (value);
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244 gst_d3d11_window_get_property (GObject * object, guint prop_id,
245 GValue * value, GParamSpec * pspec)
247 GstD3D11Window *self = GST_D3D11_WINDOW (object);
250 case PROP_ENABLE_NAVIGATION_EVENTS:
251 g_value_set_boolean (value, self->enable_navigation_events);
253 case PROP_FORCE_ASPECT_RATIO:
254 g_value_set_boolean (value, self->force_aspect_ratio);
256 case PROP_FULLSCREEN_TOGGLE_MODE:
257 g_value_set_flags (value, self->fullscreen_toggle_mode);
259 case PROP_FULLSCREEN:
260 g_value_set_boolean (value, self->fullscreen);
262 case PROP_EMIT_PRESENT:
263 g_value_set_boolean (value, self->emit_present);
266 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272 gst_d3d11_window_dispose (GObject * object)
274 GstD3D11Window *self = GST_D3D11_WINDOW (object);
276 gst_clear_buffer (&self->backbuffer);
277 GST_D3D11_CLEAR_COM (self->swap_chain);
279 gst_clear_object (&self->compositor);
280 gst_clear_object (&self->converter);
282 gst_clear_buffer (&self->cached_buffer);
283 gst_clear_object (&self->device);
285 G_OBJECT_CLASS (parent_class)->dispose (object);
289 gst_d3d11_window_on_resize_default (GstD3D11Window * self, guint width,
292 GstD3D11Device *device = self->device;
294 D3D11_TEXTURE2D_DESC desc;
295 DXGI_SWAP_CHAIN_DESC swap_desc;
296 ComPtr < ID3D11Texture2D > backbuffer;
297 GstVideoRectangle src_rect, dst_rect, rst_rect;
298 IDXGISwapChain *swap_chain;
300 GstD3D11Memory *dmem;
301 ID3D11RenderTargetView *rtv;
302 ID3D11DeviceContext *context;
304 GstD3D11DeviceLockGuard lk (device);
305 const FLOAT clear_color[] = { 0.0, 0.0, 0.0, 1.0 };
307 gst_clear_buffer (&self->backbuffer);
308 if (!self->swap_chain)
311 swap_chain = self->swap_chain;
312 swap_chain->GetDesc (&swap_desc);
313 hr = swap_chain->ResizeBuffers (0, width, height, self->dxgi_format,
315 if (!gst_d3d11_result (hr, device)) {
316 GST_ERROR_OBJECT (self, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
320 hr = swap_chain->GetBuffer (0, IID_PPV_ARGS (&backbuffer));
321 if (!gst_d3d11_result (hr, device)) {
322 GST_ERROR_OBJECT (self,
323 "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
327 backbuffer->GetDesc (&desc);
328 size = desc.Width * desc.Height;
329 /* flip mode swapchain supports only 4 formats, rgba/bgra/rgb10a2/rgba64.
330 * The size passed in alloc_wrapped() is not important here, since we never
331 * try mapping this for CPU access */
332 if (desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT) {
338 mem = gst_d3d11_allocator_alloc_wrapped (nullptr,
339 self->device, backbuffer.Get (), size, nullptr, nullptr);
341 GST_ERROR_OBJECT (self, "Couldn't allocate wrapped memory");
345 dmem = GST_D3D11_MEMORY_CAST (mem);
346 rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
348 GST_ERROR_OBJECT (self, "RTV is unavailable");
349 gst_memory_unref (mem);
353 context = gst_d3d11_device_get_device_context_handle (self->device);
354 context->ClearRenderTargetView (rtv, clear_color);
356 self->backbuffer = gst_buffer_new ();
357 gst_buffer_append_memory (self->backbuffer, mem);
359 self->surface_width = desc.Width;
360 self->surface_height = desc.Height;
364 dst_rect.w = self->surface_width;
365 dst_rect.h = self->surface_height;
367 if (self->force_aspect_ratio) {
371 switch (self->method) {
372 case GST_VIDEO_ORIENTATION_90R:
373 case GST_VIDEO_ORIENTATION_90L:
374 case GST_VIDEO_ORIENTATION_UL_LR:
375 case GST_VIDEO_ORIENTATION_UR_LL:
376 src_rect.w = GST_VIDEO_INFO_HEIGHT (&self->render_info);
377 src_rect.h = GST_VIDEO_INFO_WIDTH (&self->render_info);
380 src_rect.w = GST_VIDEO_INFO_WIDTH (&self->render_info);
381 src_rect.h = GST_VIDEO_INFO_HEIGHT (&self->render_info);
385 gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
390 self->render_rect.left = rst_rect.x;
391 self->render_rect.top = rst_rect.y;
392 self->render_rect.right = rst_rect.x + rst_rect.w;
393 self->render_rect.bottom = rst_rect.y + rst_rect.h;
395 GST_LOG_OBJECT (self,
396 "New client area %dx%d, render rect x: %d, y: %d, %dx%d",
397 desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h);
399 self->first_present = TRUE;
401 /* redraw the last scene if cached buffer exits */
402 if (self->cached_buffer)
403 gst_d3d111_window_present (self, self->cached_buffer, self->backbuffer);
407 gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
410 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
412 if (!window->enable_navigation_events)
415 g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
419 gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
420 gint button, gdouble x, gdouble y)
423 GstVideoOrientationMethod method;
425 gdouble display_w, display_h, src_w, src_h;
428 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
430 if (!window->enable_navigation_events)
433 gst_d3d11_device_lock (window->device);
434 method = window->method;
435 render_rect = window->render_rect;
436 in_w = window->info.width;
437 in_h = window->info.height;
438 gst_d3d11_device_unlock (window->device);
440 display_w = render_rect.right - render_rect.left;
441 display_h = render_rect.bottom - render_rect.top;
445 /* if backbuffer surface size is unknown or mouse point located at
446 * outside of render area, ignore it */
447 if (display_w <= 0 || display_h <= 0 || in_w <= 0 || in_h <= 0 ||
448 xpos < render_rect.left || xpos >= render_rect.right ||
449 ypos < render_rect.top || ypos >= render_rect.bottom) {
454 case GST_VIDEO_ORIENTATION_90R:
455 case GST_VIDEO_ORIENTATION_90L:
456 case GST_VIDEO_ORIENTATION_UL_LR:
457 case GST_VIDEO_ORIENTATION_UR_LL:
467 xpos = ((xpos - render_rect.left) / display_w) * src_w;
468 ypos = ((ypos - render_rect.top) / display_h) * src_h;
470 xpos = CLAMP (xpos, 0, (LONG) (src_w - 1));
471 ypos = CLAMP (ypos, 0, (LONG) (src_h - 1));
473 /* Reverse rotate/flip if needed */
475 case GST_VIDEO_ORIENTATION_90R:
479 case GST_VIDEO_ORIENTATION_90L:
483 case GST_VIDEO_ORIENTATION_UR_LL:
487 case GST_VIDEO_ORIENTATION_UL_LR:
491 case GST_VIDEO_ORIENTATION_180:
495 case GST_VIDEO_ORIENTATION_HORIZ:
499 case GST_VIDEO_ORIENTATION_VERT:
509 g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
510 event, button, x, y);
515 DXGI_FORMAT dxgi_format;
516 GstVideoFormat gst_format;
518 } GstD3D11WindowDisplayFormat;
521 gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
522 guint display_height, GstCaps * caps, GstStructure * config,
523 DXGI_FORMAT display_format, GError ** error)
525 GstD3D11WindowClass *klass;
527 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
529 klass = GST_D3D11_WINDOW_GET_CLASS (window);
530 g_assert (klass->prepare != NULL);
532 GST_DEBUG_OBJECT (window, "Prepare window, display resolution %dx%d, caps %"
533 GST_PTR_FORMAT, display_width, display_height, caps);
535 return klass->prepare (window, display_width, display_height, caps, config,
536 display_format, error);
540 gst_d3d11_window_prepare_default (GstD3D11Window * window, guint display_width,
541 guint display_height, GstCaps * caps, GstStructure * config,
542 DXGI_FORMAT display_format, GError ** error)
544 GstD3D11Device *device = window->device;
545 GstD3D11WindowClass *klass;
546 guint swapchain_flags = 0;
547 ID3D11Device *device_handle;
548 guint num_supported_format = 0;
551 D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY;
552 UINT supported_flags = 0;
553 GstD3D11WindowDisplayFormat formats[] = {
554 {DXGI_FORMAT_B8G8R8A8_UNORM, GST_VIDEO_FORMAT_BGRA, FALSE},
555 {DXGI_FORMAT_R8G8B8A8_UNORM, GST_VIDEO_FORMAT_RGBA, FALSE},
556 {DXGI_FORMAT_R10G10B10A2_UNORM, GST_VIDEO_FORMAT_RGB10A2_LE, FALSE},
558 const GstD3D11WindowDisplayFormat *chosen_format = nullptr;
559 DXGI_COLOR_SPACE_TYPE swapchain_colorspace =
560 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
561 gboolean hdr10_aware = FALSE;
562 gboolean have_hdr10_meta = FALSE;
563 ComPtr < IDXGIFactory5 > factory5;
564 IDXGIFactory1 *factory_handle;
565 BOOL allow_tearing = FALSE;
566 GstVideoMasteringDisplayInfo mdcv;
567 GstVideoContentLightLevel cll;
568 ComPtr < IDXGISwapChain3 > swapchain3;
570 const gchar *cll_str = nullptr;
571 const gchar *mdcv_str = nullptr;
573 /* Step 1: Clear old resources and objects */
574 gst_clear_buffer (&window->cached_buffer);
575 gst_clear_object (&window->compositor);
576 gst_clear_object (&window->converter);
578 /* Step 2: Decide display color format
579 * If upstream format is 10bits, try DXGI_FORMAT_R10G10B10A2_UNORM first
580 * Otherwise, use DXGI_FORMAT_B8G8R8A8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM
582 gst_video_info_from_caps (&window->info, caps);
583 device_handle = gst_d3d11_device_get_device_handle (device);
584 for (guint i = 0; i < G_N_ELEMENTS (formats); i++) {
585 hr = device_handle->CheckFormatSupport (formats[i].dxgi_format,
587 if (SUCCEEDED (hr) && (supported_flags & display_flags) == display_flags) {
588 GST_DEBUG_OBJECT (window, "Device supports format %s (DXGI_FORMAT %d)",
589 gst_video_format_to_string (formats[i].gst_format),
590 formats[i].dxgi_format);
591 formats[i].supported = TRUE;
592 num_supported_format++;
596 if (num_supported_format == 0) {
597 GST_ERROR_OBJECT (window, "Cannot determine render format");
598 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
599 "Cannot determine render format");
601 gst_structure_free (config);
603 return GST_FLOW_ERROR;
606 if (display_format != DXGI_FORMAT_UNKNOWN) {
607 for (guint i = 0; i < G_N_ELEMENTS (formats); i++) {
608 if (display_format == formats[i].dxgi_format && formats[i].supported) {
609 GST_DEBUG_OBJECT (window, "Requested format %s is supported",
610 gst_d3d11_dxgi_format_to_string (display_format));
611 chosen_format = &formats[i];
616 if (!chosen_format) {
617 GST_ERROR_OBJECT (window, "Requested DXGI FORMAT %d is not supported",
619 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
620 "Cannot determine render format");
622 gst_structure_free (config);
624 return GST_FLOW_ERROR;
627 for (guint i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
628 if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
629 if (formats[2].supported) {
630 chosen_format = &formats[2];
637 if (!chosen_format) {
638 /* prefer native format over conversion */
639 for (guint i = 0; i < 2; i++) {
640 if (formats[i].supported &&
641 formats[i].gst_format == GST_VIDEO_INFO_FORMAT (&window->info)) {
642 chosen_format = &formats[i];
647 /* choose any color space then */
648 if (!chosen_format) {
649 for (guint i = 0; i < G_N_ELEMENTS (formats); i++) {
650 if (formats[i].supported) {
651 chosen_format = &formats[i];
658 g_assert (chosen_format != nullptr);
660 GST_DEBUG_OBJECT (window, "chosen render format %s (DXGI_FORMAT %d)",
661 gst_video_format_to_string (chosen_format->gst_format),
662 chosen_format->dxgi_format);
664 /* Step 3: Create swapchain
665 * (or reuse old swapchain if the format is not changed) */
666 window->allow_tearing = FALSE;
668 factory_handle = gst_d3d11_device_get_dxgi_factory_handle (device);
669 hr = factory_handle->QueryInterface (IID_PPV_ARGS (&factory5));
670 if (SUCCEEDED (hr)) {
671 hr = factory5->CheckFeatureSupport (DXGI_FEATURE_PRESENT_ALLOW_TEARING,
672 (void *) &allow_tearing, sizeof (allow_tearing));
675 if (SUCCEEDED (hr) && allow_tearing)
676 window->allow_tearing = allow_tearing;
678 if (window->allow_tearing) {
679 GST_DEBUG_OBJECT (window, "device supports tearing");
680 swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
683 GstD3D11DeviceLockGuard lk (device);
684 window->dxgi_format = chosen_format->dxgi_format;
686 klass = GST_D3D11_WINDOW_GET_CLASS (window);
687 if (!window->swap_chain &&
688 !klass->create_swap_chain (window, window->dxgi_format,
689 display_width, display_height, swapchain_flags,
690 &window->swap_chain)) {
691 GST_ERROR_OBJECT (window, "Cannot create swapchain");
692 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
693 "Cannot create swapchain");
695 gst_structure_free (config);
697 return GST_FLOW_ERROR;
700 /* this rect struct will be used to calculate render area */
701 window->render_rect.left = 0;
702 window->render_rect.top = 0;
703 window->render_rect.right = display_width;
704 window->render_rect.bottom = display_height;
706 window->input_rect.left = 0;
707 window->input_rect.top = 0;
708 window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
709 window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
711 window->prev_input_rect = window->input_rect;
713 gst_video_info_set_format (&window->render_info,
714 chosen_format->gst_format, display_width, display_height);
716 /* preserve upstream colorimetry */
717 window->render_info.colorimetry.primaries =
718 window->info.colorimetry.primaries;
719 window->render_info.colorimetry.transfer = window->info.colorimetry.transfer;
720 /* prefer FULL range RGB. STUDIO range doesn't seem to be well supported
721 * color space by GPUs and we don't need to preserve color range for
722 * target display color space type */
723 window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
725 s = gst_caps_get_structure (caps, 0);
726 mdcv_str = gst_structure_get_string (s, "mastering-display-info");
727 cll_str = gst_structure_get_string (s, "content-light-level");
728 if (mdcv_str && cll_str &&
729 gst_video_mastering_display_info_from_string (&mdcv, mdcv_str) &&
730 gst_video_content_light_level_from_string (&cll, cll_str)) {
731 have_hdr10_meta = TRUE;
734 hr = window->swap_chain->QueryInterface (IID_PPV_ARGS (&swapchain3));
735 if (gst_d3d11_result (hr, device)) {
736 if (gst_d3d11_find_swap_chain_color_space (&window->render_info,
737 swapchain3.Get (), &swapchain_colorspace)) {
738 hr = swapchain3->SetColorSpace1 (swapchain_colorspace);
739 if (!gst_d3d11_result (hr, window->device)) {
740 GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x",
741 swapchain_colorspace, (guint) hr);
742 swapchain_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
744 ComPtr < IDXGISwapChain4 > swapchain4;
746 /* DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12, undefined in old
748 if (swapchain_colorspace == 12 && have_hdr10_meta) {
749 hr = swapchain3.As (&swapchain4);
750 if (gst_d3d11_result (hr, device)) {
751 DXGI_HDR_METADATA_HDR10 hdr10_metadata = { 0, };
753 GST_DEBUG_OBJECT (window,
754 "Have HDR metadata, set to DXGI swapchain");
756 gst_d3d11_hdr_meta_data_to_dxgi (&mdcv, &cll, &hdr10_metadata);
758 hr = swapchain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10,
759 sizeof (DXGI_HDR_METADATA_HDR10), &hdr10_metadata);
760 if (!gst_d3d11_result (hr, device)) {
761 GST_WARNING_OBJECT (window,
762 "Couldn't set HDR metadata, hr 0x%x", (guint) hr);
772 GST_DEBUG_OBJECT (window, "Set colorspace %d", swapchain_colorspace);
774 /* update with selected display color space */
775 gst_video_info_apply_dxgi_color_space (swapchain_colorspace,
776 &window->render_info);
778 window->converter = gst_d3d11_converter_new (device,
779 &window->info, &window->render_info, config);
781 if (!window->converter) {
782 GST_ERROR_OBJECT (window, "Cannot create converter");
783 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
784 "Cannot create converter");
785 return GST_FLOW_ERROR;
788 if (have_hdr10_meta) {
789 g_object_set (window->converter, "src-mastering-display-info", mdcv_str,
790 "src-content-light-level", cll_str, nullptr);
792 g_object_set (window->converter, "dest-mastering-display-info", mdcv_str,
793 "dest-content-light-level", cll_str, nullptr);
798 gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
799 if (!window->compositor) {
800 GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
801 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
802 "Cannot create overlay compositor");
803 return GST_FLOW_ERROR;
806 /* call resize to allocated resources */
807 klass->on_resize (window, display_width, display_height);
809 if (window->requested_fullscreen != window->fullscreen)
810 klass->change_fullscreen_mode (window);
812 GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
818 gst_d3d11_window_show (GstD3D11Window * window)
820 GstD3D11WindowClass *klass;
822 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
824 klass = GST_D3D11_WINDOW_GET_CLASS (window);
827 klass->show (window);
831 gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
832 const GstVideoRectangle * rect)
834 GstD3D11WindowClass *klass;
836 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
838 klass = GST_D3D11_WINDOW_GET_CLASS (window);
840 if (klass->set_render_rectangle)
841 klass->set_render_rectangle (window, rect);
845 gst_d3d11_window_set_title (GstD3D11Window * window, const gchar * title)
847 GstD3D11WindowClass *klass;
849 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
851 klass = GST_D3D11_WINDOW_GET_CLASS (window);
853 if (klass->set_title)
854 klass->set_title (window, title);
858 gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
859 GstBuffer * backbuffer)
861 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
862 GstFlowReturn ret = GST_FLOW_OK;
863 guint present_flags = 0;
864 GstVideoCropMeta *crop_meta;
865 RECT input_rect = self->input_rect;
866 RECT *prev_rect = &self->prev_input_rect;
867 ID3D11RenderTargetView *rtv;
869 GstD3D11Memory *dmem;
875 GST_ERROR_OBJECT (self, "Empty render target");
876 return GST_FLOW_ERROR;
879 mem = gst_buffer_peek_memory (backbuffer, 0);
880 if (!gst_is_d3d11_memory (mem)) {
881 GST_ERROR_OBJECT (self, "Invalid back buffer");
882 return GST_FLOW_ERROR;
885 dmem = GST_D3D11_MEMORY_CAST (mem);
886 rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
888 GST_ERROR_OBJECT (self, "RTV is unavailable");
889 return GST_FLOW_ERROR;
892 /* We use flip mode swapchain and will not redraw borders.
893 * So backbuffer should be cleared manually in order to remove artifact of
894 * previous client's rendering on present signal */
895 if (self->emit_present) {
896 const FLOAT clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
897 ID3D11DeviceContext *context =
898 gst_d3d11_device_get_device_context_handle (self->device);
900 context->ClearRenderTargetView (rtv, clear_color);
903 crop_meta = gst_buffer_get_video_crop_meta (buffer);
905 input_rect.left = crop_meta->x;
906 input_rect.right = crop_meta->x + crop_meta->width;
907 input_rect.top = crop_meta->y;
908 input_rect.bottom = crop_meta->y + crop_meta->height;
911 if (input_rect.left != prev_rect->left || input_rect.top != prev_rect->top ||
912 input_rect.right != prev_rect->right ||
913 input_rect.bottom != prev_rect->bottom) {
914 g_object_set (self->converter, "src-x", (gint) input_rect.left,
915 "src-y", (gint) input_rect.top,
916 "src-width", (gint) (input_rect.right - input_rect.left),
917 "src-height", (gint) (input_rect.bottom - input_rect.top), nullptr);
919 self->prev_input_rect = input_rect;
922 if (self->first_present) {
923 D3D11_VIEWPORT viewport;
925 viewport.TopLeftX = self->render_rect.left;
926 viewport.TopLeftY = self->render_rect.top;
927 viewport.Width = self->render_rect.right - self->render_rect.left;
928 viewport.Height = self->render_rect.bottom - self->render_rect.top;
929 viewport.MinDepth = 0.0f;
930 viewport.MaxDepth = 1.0f;
932 g_object_set (self->converter, "dest-x", (gint) self->render_rect.left,
933 "dest-y", (gint) self->render_rect.top,
935 (gint) (self->render_rect.right - self->render_rect.left),
937 (gint) (self->render_rect.bottom - self->render_rect.top),
938 "video-direction", self->method, nullptr);
939 gst_d3d11_overlay_compositor_update_viewport (self->compositor, &viewport);
942 if (!gst_d3d11_converter_convert_buffer_unlocked (self->converter,
943 buffer, backbuffer)) {
944 GST_ERROR_OBJECT (self, "Couldn't render buffer");
945 return GST_FLOW_ERROR;
948 gst_d3d11_overlay_compositor_upload (self->compositor, buffer);
949 gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &rtv);
951 if (self->allow_tearing && self->fullscreen)
952 present_flags |= DXGI_PRESENT_ALLOW_TEARING;
954 if (klass->present) {
955 if (self->emit_present) {
956 g_signal_emit (self, d3d11_window_signals[SIGNAL_PRESENT], 0,
957 self->device, rtv, nullptr);
959 ret = klass->present (self, present_flags);
962 self->first_present = FALSE;
968 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer)
970 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
972 GstD3D11DeviceLockGuard lk (window->device);
974 gst_buffer_replace (&window->cached_buffer, buffer);
976 return gst_d3d111_window_present (window, window->cached_buffer,
981 gst_d3d11_window_render_on_shared_handle (GstD3D11Window * window,
982 GstBuffer * buffer, HANDLE shared_handle, guint texture_misc_flags,
983 guint64 acquire_key, guint64 release_key)
985 GstD3D11WindowClass *klass;
986 GstFlowReturn ret = GST_FLOW_OK;
987 GstD3D11WindowSharedHandleData data = { nullptr, };
989 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
991 klass = GST_D3D11_WINDOW_GET_CLASS (window);
993 g_assert (klass->open_shared_handle != NULL);
994 g_assert (klass->release_shared_handle != NULL);
996 data.shared_handle = shared_handle;
997 data.texture_misc_flags = texture_misc_flags;
998 data.acquire_key = acquire_key;
999 data.release_key = release_key;
1001 GstD3D11DeviceLockGuard (window->device);
1002 if (!klass->open_shared_handle (window, &data)) {
1003 GST_ERROR_OBJECT (window, "Couldn't open shared handle");
1007 ret = gst_d3d111_window_present (window, buffer, data.render_target);
1009 klass->release_shared_handle (window, &data);
1015 gst_d3d11_window_unlock (GstD3D11Window * window)
1017 GstD3D11WindowClass *klass;
1018 gboolean ret = TRUE;
1020 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1022 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1025 ret = klass->unlock (window);
1031 gst_d3d11_window_unlock_stop (GstD3D11Window * window)
1033 GstD3D11WindowClass *klass;
1034 gboolean ret = TRUE;
1036 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1038 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1040 if (klass->unlock_stop)
1041 ret = klass->unlock_stop (window);
1043 GstD3D11DeviceLockGuard lk (window->device);
1044 gst_clear_buffer (&window->cached_buffer);
1050 gst_d3d11_window_unprepare (GstD3D11Window * window)
1052 GstD3D11WindowClass *klass;
1054 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
1056 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1058 if (klass->unprepare)
1059 klass->unprepare (window);
1062 GstD3D11WindowNativeType
1063 gst_d3d11_window_get_native_type_from_handle (guintptr handle)
1066 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1068 #if (!GST_D3D11_WINAPI_ONLY_APP)
1069 if (IsWindow ((HWND) handle))
1070 return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
1072 #if GST_D3D11_WINAPI_ONLY_APP
1075 ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
1076 ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
1077 ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
1080 if (SUCCEEDED (window.As (&core_window)))
1081 return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
1083 if (SUCCEEDED (window.As (&panel)))
1084 return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
1088 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1092 gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
1095 case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
1097 case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
1099 case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
1100 return "core-window";
1101 case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
1102 return "swap-chain-panel";
1111 gst_d3d11_window_set_orientation (GstD3D11Window * window,
1112 GstVideoOrientationMethod method)
1114 if (method == GST_VIDEO_ORIENTATION_AUTO ||
1115 method == GST_VIDEO_ORIENTATION_CUSTOM) {
1119 GstD3D11DeviceLockGuard lk (window->device);
1120 if (window->method != method) {
1121 window->method = method;
1122 if (window->swap_chain) {
1123 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (window);
1125 klass->on_resize (window, window->surface_width, window->surface_height);