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,
59 #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
60 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
61 #define DEFAULT_FULLSCREEN_TOGGLE_MODE GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE
62 #define DEFAULT_FULLSCREEN FALSE
63 #define DEFAULT_RENDER_STATS FALSE
72 static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
75 gst_d3d11_window_fullscreen_toggle_mode_type (void)
77 static gsize mode_type = 0;
79 if (g_once_init_enter (&mode_type)) {
80 static const GFlagsValue mode_types[] = {
81 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE,
82 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE", "none"},
83 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER,
84 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER", "alt-enter"},
85 {GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY,
86 "GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY", "property"},
89 GType tmp = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
91 g_once_init_leave (&mode_type, tmp);
94 return (GType) mode_type;
97 #define gst_d3d11_window_parent_class parent_class
98 G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
100 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
101 const GValue * value, GParamSpec * pspec);
102 static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
103 GValue * value, GParamSpec * pspec);
104 static void gst_d3d11_window_dispose (GObject * object);
105 static GstFlowReturn gst_d3d111_window_present (GstD3D11Window * self,
106 GstBuffer * buffer, ID3D11VideoProcessorOutputView * pov,
107 ID3D11RenderTargetView * rtv);
108 static void gst_d3d11_window_on_resize_default (GstD3D11Window * window,
109 guint width, guint height);
110 static gboolean gst_d3d11_window_prepare_default (GstD3D11Window * window,
111 guint display_width, guint display_height, GstCaps * caps,
112 gboolean * video_processor_available, GError ** error);
115 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
117 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119 gobject_class->set_property = gst_d3d11_window_set_property;
120 gobject_class->get_property = gst_d3d11_window_get_property;
121 gobject_class->dispose = gst_d3d11_window_dispose;
123 klass->on_resize = GST_DEBUG_FUNCPTR (gst_d3d11_window_on_resize_default);
124 klass->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_prepare_default);
126 g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
127 g_param_spec_object ("d3d11device", "D3D11 Device",
128 "GstD3D11Device object for creating swapchain",
129 GST_TYPE_D3D11_DEVICE,
130 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
131 G_PARAM_STATIC_STRINGS)));
133 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
134 g_param_spec_boolean ("force-aspect-ratio",
135 "Force aspect ratio",
136 "When enabled, scaling will respect original aspect ratio",
137 DEFAULT_FORCE_ASPECT_RATIO,
138 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
140 g_object_class_install_property (gobject_class, PROP_ENABLE_NAVIGATION_EVENTS,
141 g_param_spec_boolean ("enable-navigation-events",
142 "Enable navigation events",
143 "When enabled, signals for navigation events are emitted",
144 DEFAULT_ENABLE_NAVIGATION_EVENTS,
145 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
147 g_object_class_install_property (gobject_class, PROP_FULLSCREEN_TOGGLE_MODE,
148 g_param_spec_flags ("fullscreen-toggle-mode",
149 "Full screen toggle mode",
150 "Full screen toggle mode used to trigger fullscreen mode change",
151 GST_D3D11_WINDOW_TOGGLE_MODE_GET_TYPE, DEFAULT_FULLSCREEN_TOGGLE_MODE,
152 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
154 g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
155 g_param_spec_boolean ("fullscreen",
157 "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
159 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
161 g_object_class_install_property (gobject_class, PROP_WINDOW_HANDLE,
162 g_param_spec_pointer ("window-handle",
163 "Window Handle", "External Window Handle",
164 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
165 G_PARAM_STATIC_STRINGS)));
167 d3d11_window_signals[SIGNAL_KEY_EVENT] =
168 g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
169 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
170 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
172 d3d11_window_signals[SIGNAL_MOUSE_EVENT] =
173 g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
174 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
175 G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
179 gst_d3d11_window_init (GstD3D11Window * self)
181 self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
182 self->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
183 self->fullscreen_toggle_mode = GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_NONE;
184 self->fullscreen = DEFAULT_FULLSCREEN;
185 self->render_stats = DEFAULT_RENDER_STATS;
189 gst_d3d11_window_set_property (GObject * object, guint prop_id,
190 const GValue * value, GParamSpec * pspec)
192 GstD3D11Window *self = GST_D3D11_WINDOW (object);
193 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
196 case PROP_D3D11_DEVICE:
197 self->device = (GstD3D11Device *) g_value_dup_object (value);
199 case PROP_FORCE_ASPECT_RATIO:
201 self->force_aspect_ratio = g_value_get_boolean (value);
202 if (self->swap_chain)
203 klass->update_swap_chain (self);
206 case PROP_ENABLE_NAVIGATION_EVENTS:
207 self->enable_navigation_events = g_value_get_boolean (value);
209 case PROP_FULLSCREEN_TOGGLE_MODE:
210 self->fullscreen_toggle_mode =
211 (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
213 case PROP_FULLSCREEN:
215 self->requested_fullscreen = g_value_get_boolean (value);
216 if (self->swap_chain)
217 klass->change_fullscreen_mode (self);
220 case PROP_WINDOW_HANDLE:
221 self->external_handle = (guintptr) g_value_get_pointer (value);
224 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230 gst_d3d11_window_get_property (GObject * object, guint prop_id,
231 GValue * value, GParamSpec * pspec)
233 GstD3D11Window *self = GST_D3D11_WINDOW (object);
236 case PROP_ENABLE_NAVIGATION_EVENTS:
237 g_value_set_boolean (value, self->enable_navigation_events);
239 case PROP_FORCE_ASPECT_RATIO:
240 g_value_set_boolean (value, self->force_aspect_ratio);
242 case PROP_FULLSCREEN_TOGGLE_MODE:
243 g_value_set_flags (value, self->fullscreen_toggle_mode);
245 case PROP_FULLSCREEN:
246 g_value_set_boolean (value, self->fullscreen);
249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255 gst_d3d11_window_release_resources (GstD3D11Device * device,
256 GstD3D11Window * window)
258 GST_D3D11_CLEAR_COM (window->rtv);
259 GST_D3D11_CLEAR_COM (window->pov);
260 GST_D3D11_CLEAR_COM (window->swap_chain);
264 gst_d3d11_window_dispose (GObject * object)
266 GstD3D11Window *self = GST_D3D11_WINDOW (object);
269 gst_d3d11_window_release_resources (self->device, self);
271 g_clear_pointer (&self->processor, gst_d3d11_video_processor_free);
272 gst_clear_object (&self->compositor);
273 gst_clear_object (&self->converter);
275 gst_clear_buffer (&self->cached_buffer);
276 gst_clear_object (&self->device);
278 G_OBJECT_CLASS (parent_class)->dispose (object);
282 gst_d3d11_window_on_resize_default (GstD3D11Window * window, guint width,
286 ID3D11Device *device_handle;
287 D3D11_TEXTURE2D_DESC desc;
288 DXGI_SWAP_CHAIN_DESC swap_desc;
289 ID3D11Texture2D *backbuffer = NULL;
290 GstVideoRectangle src_rect, dst_rect, rst_rect;
291 IDXGISwapChain *swap_chain;
293 gst_d3d11_device_lock (window->device);
294 if (!window->swap_chain)
297 device_handle = gst_d3d11_device_get_device_handle (window->device);
298 swap_chain = window->swap_chain;
300 GST_D3D11_CLEAR_COM (window->rtv);
301 GST_D3D11_CLEAR_COM (window->pov);
303 swap_chain->GetDesc (&swap_desc);
304 hr = swap_chain->ResizeBuffers (0, width, height, window->dxgi_format,
306 if (!gst_d3d11_result (hr, window->device)) {
307 GST_ERROR_OBJECT (window, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
311 hr = swap_chain->GetBuffer (0, IID_PPV_ARGS (&backbuffer));
312 if (!gst_d3d11_result (hr, window->device)) {
313 GST_ERROR_OBJECT (window,
314 "Cannot get backbuffer from swapchain, hr: 0x%x", (guint) hr);
318 backbuffer->GetDesc (&desc);
319 window->surface_width = desc.Width;
320 window->surface_height = desc.Height;
325 dst_rect.w = window->surface_width;
326 dst_rect.h = window->surface_height;
328 if (window->force_aspect_ratio) {
331 src_rect.w = GST_VIDEO_INFO_WIDTH (&window->render_info);
332 src_rect.h = GST_VIDEO_INFO_HEIGHT (&window->render_info);
334 gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
340 window->render_rect.left = rst_rect.x;
341 window->render_rect.top = rst_rect.y;
342 window->render_rect.right = rst_rect.x + rst_rect.w;
343 window->render_rect.bottom = rst_rect.y + rst_rect.h;
345 GST_LOG_OBJECT (window,
346 "New client area %dx%d, render rect x: %d, y: %d, %dx%d",
347 desc.Width, desc.Height, rst_rect.x, rst_rect.y, rst_rect.w, rst_rect.h);
349 hr = device_handle->CreateRenderTargetView (backbuffer, NULL, &window->rtv);
350 if (!gst_d3d11_result (hr, window->device)) {
351 GST_ERROR_OBJECT (window, "Cannot create render target view, hr: 0x%x",
357 if (window->processor) {
358 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC pov_desc;
360 pov_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
361 pov_desc.Texture2D.MipSlice = 0;
363 if (!gst_d3d11_video_processor_create_output_view (window->processor,
364 &pov_desc, backbuffer, &window->pov))
368 window->first_present = TRUE;
370 /* redraw the last scene if cached buffer exits */
371 if (window->cached_buffer) {
372 gst_d3d111_window_present (window, window->cached_buffer,
373 window->pov, window->rtv);
377 GST_D3D11_CLEAR_COM (backbuffer);
379 gst_d3d11_device_unlock (window->device);
383 gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
386 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
388 if (!window->enable_navigation_events)
391 g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
395 gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
396 gint button, gdouble x, gdouble y)
398 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
400 if (!window->enable_navigation_events)
403 g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
404 event, button, x, y);
409 DXGI_FORMAT dxgi_format;
410 GstVideoFormat gst_format;
412 } GstD3D11WindowDisplayFormat;
415 gst_d3d11_window_prepare (GstD3D11Window * window, guint display_width,
416 guint display_height, GstCaps * caps, gboolean * video_processor_available,
419 GstD3D11WindowClass *klass;
421 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
423 klass = GST_D3D11_WINDOW_GET_CLASS (window);
424 g_assert (klass->prepare != NULL);
426 GST_DEBUG_OBJECT (window, "Prepare window, display resolution %dx%d, caps %"
427 GST_PTR_FORMAT, display_width, display_height, caps);
429 return klass->prepare (window, display_width, display_height, caps,
430 video_processor_available, error);
434 gst_d3d11_window_prepare_default (GstD3D11Window * window, guint display_width,
435 guint display_height, GstCaps * caps, gboolean * video_processor_available,
438 GstD3D11WindowClass *klass;
439 guint swapchain_flags = 0;
440 ID3D11Device *device_handle;
442 guint num_supported_format = 0;
445 D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DISPLAY;
446 UINT supported_flags = 0;
447 GstD3D11WindowDisplayFormat formats[] = {
448 {DXGI_FORMAT_R8G8B8A8_UNORM, GST_VIDEO_FORMAT_RGBA, FALSE},
449 {DXGI_FORMAT_B8G8R8A8_UNORM, GST_VIDEO_FORMAT_BGRA, FALSE},
450 {DXGI_FORMAT_R10G10B10A2_UNORM, GST_VIDEO_FORMAT_RGB10A2_LE, FALSE},
452 const GstD3D11WindowDisplayFormat *chosen_format = NULL;
453 GstDxgiColorSpace swapchain_colorspace;
454 gboolean found_swapchain_colorspace = FALSE;
455 gboolean have_hdr10 = FALSE;
456 DXGI_COLOR_SPACE_TYPE native_colorspace_type =
457 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
458 DXGI_HDR_METADATA_HDR10 hdr10_metadata = { 0, };
459 GstDxgiColorSpace in_dxgi_colorspace;
460 GstD3D11ConverterMethod method = GST_D3D11_CONVERTER_METHOD_SHADER;
462 /* Step 1: Clear old resources and objects */
463 gst_clear_buffer (&window->cached_buffer);
464 g_clear_pointer (&window->processor, gst_d3d11_video_processor_free);
465 gst_clear_object (&window->compositor);
466 gst_clear_object (&window->converter);
468 window->processor_in_use = FALSE;
470 /* Step 2: Decide display color format
471 * If upstream format is 10bits, try DXGI_FORMAT_R10G10B10A2_UNORM first
472 * Otherwise, use DXGI_FORMAT_B8G8R8A8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM
474 gst_video_info_from_caps (&window->info, caps);
475 device_handle = gst_d3d11_device_get_device_handle (window->device);
476 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
477 hr = device_handle->CheckFormatSupport (formats[i].dxgi_format,
479 if (SUCCEEDED (hr) && (supported_flags & display_flags) == display_flags) {
480 GST_DEBUG_OBJECT (window, "Device supports format %s (DXGI_FORMAT %d)",
481 gst_video_format_to_string (formats[i].gst_format),
482 formats[i].dxgi_format);
483 formats[i].supported = TRUE;
484 num_supported_format++;
488 if (num_supported_format == 0) {
489 GST_ERROR_OBJECT (window, "Cannot determine render format");
490 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
491 "Cannot determine render format");
495 for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (&window->info); i++) {
496 if (GST_VIDEO_INFO_COMP_DEPTH (&window->info, i) > 8) {
497 if (formats[2].supported) {
498 chosen_format = &formats[2];
504 if (!chosen_format) {
505 /* prefer native format over conversion */
506 for (i = 0; i < 2; i++) {
507 if (formats[i].supported &&
508 formats[i].gst_format == GST_VIDEO_INFO_FORMAT (&window->info)) {
509 chosen_format = &formats[i];
514 /* choose any color space then */
515 if (!chosen_format) {
516 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
517 if (formats[i].supported) {
518 chosen_format = &formats[i];
525 g_assert (chosen_format != NULL);
527 GST_DEBUG_OBJECT (window, "chosen render format %s (DXGI_FORMAT %d)",
528 gst_video_format_to_string (chosen_format->gst_format),
529 chosen_format->dxgi_format);
531 /* Step 3: Create swapchain
532 * (or reuse old swapchain if the format is not changed) */
533 window->allow_tearing = FALSE;
536 ComPtr < IDXGIFactory5 > factory5;
537 IDXGIFactory1 *factory_handle;
538 BOOL allow_tearing = FALSE;
540 factory_handle = gst_d3d11_device_get_dxgi_factory_handle (window->device);
541 hr = factory_handle->QueryInterface (IID_PPV_ARGS (&factory5));
542 if (SUCCEEDED (hr)) {
543 hr = factory5->CheckFeatureSupport (DXGI_FEATURE_PRESENT_ALLOW_TEARING,
544 (void *) &allow_tearing, sizeof (allow_tearing));
547 if (SUCCEEDED (hr) && allow_tearing)
548 window->allow_tearing = allow_tearing;
551 if (window->allow_tearing) {
552 GST_DEBUG_OBJECT (window, "device supports tearing");
553 swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
556 gst_d3d11_device_lock (window->device);
557 window->dxgi_format = chosen_format->dxgi_format;
559 klass = GST_D3D11_WINDOW_GET_CLASS (window);
560 if (!window->swap_chain &&
561 !klass->create_swap_chain (window, window->dxgi_format,
562 display_width, display_height, swapchain_flags,
563 &window->swap_chain)) {
564 GST_ERROR_OBJECT (window, "Cannot create swapchain");
565 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
566 "Cannot create swapchain");
570 /* this rect struct will be used to calculate render area */
571 window->render_rect.left = 0;
572 window->render_rect.top = 0;
573 window->render_rect.right = display_width;
574 window->render_rect.bottom = display_height;
576 window->input_rect.left = 0;
577 window->input_rect.top = 0;
578 window->input_rect.right = GST_VIDEO_INFO_WIDTH (&window->info);
579 window->input_rect.bottom = GST_VIDEO_INFO_HEIGHT (&window->info);
581 window->prev_input_rect = window->input_rect;
583 /* Step 4: Decide render color space and set it on converter/processor */
585 GstVideoMasteringDisplayInfo minfo;
586 GstVideoContentLightLevel cll;
588 if (gst_video_mastering_display_info_from_caps (&minfo, caps) &&
589 gst_video_content_light_level_from_caps (&cll, caps)) {
590 ComPtr < IDXGISwapChain4 > swapchain4;
593 hr = window->swap_chain->QueryInterface (IID_PPV_ARGS (&swapchain4));
594 if (gst_d3d11_result (hr, window->device)) {
595 GST_DEBUG_OBJECT (window, "Have HDR metadata, set to DXGI swapchain");
597 gst_d3d11_hdr_meta_data_to_dxgi (&minfo, &cll, &hdr10_metadata);
599 hr = swapchain4->SetHDRMetaData (DXGI_HDR_METADATA_TYPE_HDR10,
600 sizeof (DXGI_HDR_METADATA_HDR10), &hdr10_metadata);
601 if (!gst_d3d11_result (hr, window->device)) {
602 GST_WARNING_OBJECT (window, "Couldn't set HDR metadata, hr 0x%x",
611 /* Step 5: Choose display color space */
612 gst_video_info_set_format (&window->render_info,
613 chosen_format->gst_format, display_width, display_height);
615 /* preserve upstream colorimetry */
616 window->render_info.colorimetry.primaries =
617 window->info.colorimetry.primaries;
618 window->render_info.colorimetry.transfer = window->info.colorimetry.transfer;
619 /* prefer FULL range RGB. STUDIO range doesn't seem to be well supported
620 * color space by GPUs and we don't need to preserve color range for
621 * target display color space type */
622 window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
625 ComPtr < IDXGISwapChain3 > swapchain3;
628 hr = window->swap_chain->QueryInterface (IID_PPV_ARGS (&swapchain3));
630 if (gst_d3d11_result (hr, window->device)) {
631 found_swapchain_colorspace =
632 gst_d3d11_find_swap_chain_color_space (&window->render_info,
633 swapchain3.Get (), &swapchain_colorspace);
634 if (found_swapchain_colorspace) {
635 native_colorspace_type =
636 (DXGI_COLOR_SPACE_TYPE) swapchain_colorspace.dxgi_color_space_type;
637 hr = swapchain3->SetColorSpace1 (native_colorspace_type);
638 if (!gst_d3d11_result (hr, window->device)) {
639 GST_WARNING_OBJECT (window, "Failed to set colorspace %d, hr: 0x%x",
640 native_colorspace_type, (guint) hr);
641 found_swapchain_colorspace = FALSE;
642 native_colorspace_type = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
644 GST_DEBUG_OBJECT (window,
645 "Set colorspace %d", native_colorspace_type);
647 /* update with selected display color space */
648 window->render_info.colorimetry.primaries =
649 swapchain_colorspace.primaries;
650 window->render_info.colorimetry.transfer =
651 swapchain_colorspace.transfer;
652 window->render_info.colorimetry.range = swapchain_colorspace.range;
653 window->render_info.colorimetry.matrix = swapchain_colorspace.matrix;
659 /* otherwise, use most common DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
661 if (!found_swapchain_colorspace) {
662 GST_DEBUG_OBJECT (window, "No selected render color space, use BT709");
663 window->render_info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
664 window->render_info.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
665 window->render_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
666 window->render_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
667 } else if (gst_d3d11_video_info_to_dxgi_color_space (&window->info,
668 &in_dxgi_colorspace)) {
669 GstD3D11Format in_format;
670 gboolean hardware = FALSE;
671 GstD3D11VideoProcessor *processor = NULL;
672 DXGI_FORMAT in_dxgi_format;
674 gst_d3d11_device_get_format (window->device,
675 GST_VIDEO_INFO_FORMAT (&window->info), &in_format);
676 in_dxgi_format = in_format.dxgi_format;
678 if (in_format.dxgi_format != DXGI_FORMAT_UNKNOWN) {
679 g_object_get (window->device, "hardware", &hardware, NULL);
684 gst_d3d11_video_processor_new (window->device,
685 GST_VIDEO_INFO_WIDTH (&window->info),
686 GST_VIDEO_INFO_HEIGHT (&window->info), display_width, display_height);
690 DXGI_FORMAT out_dxgi_format = chosen_format->dxgi_format;
691 DXGI_COLOR_SPACE_TYPE in_dxgi_color_space =
692 (DXGI_COLOR_SPACE_TYPE) in_dxgi_colorspace.dxgi_color_space_type;
693 DXGI_COLOR_SPACE_TYPE out_dxgi_color_space = native_colorspace_type;
695 if (!gst_d3d11_video_processor_check_format_conversion (processor,
696 in_dxgi_format, in_dxgi_color_space, out_dxgi_format,
697 out_dxgi_color_space)) {
698 GST_DEBUG_OBJECT (window, "Conversion is not supported by device");
699 gst_d3d11_video_processor_free (processor);
702 GST_DEBUG_OBJECT (window, "video processor supports conversion");
703 gst_d3d11_video_processor_set_input_dxgi_color_space (processor,
704 in_dxgi_color_space);
705 gst_d3d11_video_processor_set_output_dxgi_color_space (processor,
706 out_dxgi_color_space);
709 GST_DEBUG_OBJECT (window, "Set HDR metadata on video processor");
710 gst_d3d11_video_processor_set_input_hdr10_metadata (processor,
712 gst_d3d11_video_processor_set_output_hdr10_metadata (processor,
717 window->processor = processor;
721 *video_processor_available = !!window->processor;
723 /* configure shader even if video processor is available for fallback */
725 gst_d3d11_converter_new (window->device, &window->info,
726 &window->render_info, &method);
728 if (!window->converter) {
729 GST_ERROR_OBJECT (window, "Cannot create converter");
730 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
731 "Cannot create converter");
736 gst_d3d11_overlay_compositor_new (window->device, &window->render_info);
737 if (!window->compositor) {
738 GST_ERROR_OBJECT (window, "Cannot create overlay compositor");
739 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
740 "Cannot create overlay compositor");
743 gst_d3d11_device_unlock (window->device);
745 /* call resize to allocated resources */
746 klass->on_resize (window, display_width, display_height);
748 if (window->requested_fullscreen != window->fullscreen) {
749 klass->change_fullscreen_mode (window);
752 GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
757 gst_d3d11_device_unlock (window->device);
763 gst_d3d11_window_show (GstD3D11Window * window)
765 GstD3D11WindowClass *klass;
767 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
769 klass = GST_D3D11_WINDOW_GET_CLASS (window);
772 klass->show (window);
776 gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
777 const GstVideoRectangle * rect)
779 GstD3D11WindowClass *klass;
781 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
783 klass = GST_D3D11_WINDOW_GET_CLASS (window);
785 if (klass->set_render_rectangle)
786 klass->set_render_rectangle (window, rect);
790 gst_d3d11_window_set_title (GstD3D11Window * window, const gchar * title)
792 GstD3D11WindowClass *klass;
794 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
796 klass = GST_D3D11_WINDOW_GET_CLASS (window);
798 if (klass->set_title)
799 klass->set_title (window, title);
803 gst_d3d11_window_buffer_ensure_processor_input (GstD3D11Window * self,
804 GstBuffer * buffer, ID3D11VideoProcessorInputView ** in_view)
807 ID3D11VideoProcessorInputView *piv;
809 if (!self->processor)
812 if (gst_buffer_n_memory (buffer) != 1)
815 mem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
816 piv = gst_d3d11_video_processor_get_input_view (self->processor, mem);
818 GST_LOG_OBJECT (self, "Failed to get processor input view");
828 gst_d3d11_window_do_processor (GstD3D11Window * self,
829 ID3D11VideoProcessorInputView * piv, ID3D11VideoProcessorOutputView * pov,
834 ret = gst_d3d11_video_processor_render_unlocked (self->processor,
835 input_rect, piv, &self->render_rect, pov);
837 GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using processor");
839 GST_TRACE_OBJECT (self, "Rendered using processor");
840 self->processor_in_use = TRUE;
847 gst_d3d11_window_do_convert (GstD3D11Window * self,
848 ID3D11ShaderResourceView * srv[GST_VIDEO_MAX_PLANES],
849 ID3D11RenderTargetView * rtv, RECT * input_rect)
851 RECT *prev = &self->prev_input_rect;
853 if (input_rect->left != prev->left || input_rect->top != prev->top ||
854 input_rect->right != prev->right || input_rect->bottom != prev->bottom) {
855 g_object_set (self->converter, "src-x", (gint) input_rect->left,
856 "src-y", (gint) input_rect->top,
857 "src-width", (gint) (input_rect->right - input_rect->left),
858 "src-height", (gint) (input_rect->bottom - input_rect->top), nullptr);
863 if (!gst_d3d11_converter_convert_unlocked (self->converter, srv, &rtv)) {
864 GST_ERROR_OBJECT (self, "Couldn't render to backbuffer using converter");
867 GST_TRACE_OBJECT (self, "Rendered using converter");
874 gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
875 ID3D11VideoProcessorOutputView * pov, ID3D11RenderTargetView * rtv)
877 GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
878 GstFlowReturn ret = GST_FLOW_OK;
879 guint present_flags = 0;
885 GstMapInfo infos[GST_VIDEO_MAX_PLANES];
886 ID3D11ShaderResourceView *srv[GST_VIDEO_MAX_PLANES];
887 ID3D11VideoProcessorInputView *piv = NULL;
888 ID3D11Device *device_handle =
889 gst_d3d11_device_get_device_handle (self->device);
890 gboolean can_convert = FALSE;
891 gboolean can_process = FALSE;
892 gboolean convert_ret = FALSE;
893 RECT input_rect = self->input_rect;
894 GstVideoCropMeta *crop_meta;
896 /* Map memory in any case so that we can upload pending stage texture */
897 if (!gst_d3d11_buffer_map (buffer, device_handle, infos, GST_MAP_READ)) {
898 GST_ERROR_OBJECT (self, "Couldn't map buffer");
899 return GST_FLOW_ERROR;
902 can_convert = gst_d3d11_buffer_get_shader_resource_view (buffer, srv);
904 can_process = gst_d3d11_window_buffer_ensure_processor_input (self,
908 if (!can_convert && !can_process) {
909 GST_ERROR_OBJECT (self, "Input texture cannot be used for converter");
910 return GST_FLOW_ERROR;
913 crop_meta = gst_buffer_get_video_crop_meta (buffer);
914 /* Do minimal validate */
916 ID3D11Texture2D *texture = (ID3D11Texture2D *) infos[0].data;
917 D3D11_TEXTURE2D_DESC desc = { 0, };
919 texture->GetDesc (&desc);
921 if (desc.Width < crop_meta->x + crop_meta->width ||
922 desc.Height < crop_meta->y + crop_meta->height) {
923 GST_WARNING_OBJECT (self, "Invalid crop meta, ignore");
930 input_rect.left = crop_meta->x;
931 input_rect.right = crop_meta->x + crop_meta->width;
932 input_rect.top = crop_meta->y;
933 input_rect.bottom = crop_meta->y + crop_meta->height;
936 if (self->first_present) {
937 D3D11_VIEWPORT viewport;
939 viewport.TopLeftX = self->render_rect.left;
940 viewport.TopLeftY = self->render_rect.top;
941 viewport.Width = self->render_rect.right - self->render_rect.left;
942 viewport.Height = self->render_rect.bottom - self->render_rect.top;
943 viewport.MinDepth = 0.0f;
944 viewport.MaxDepth = 1.0f;
946 g_object_set (self->converter, "dest-x", (gint) self->render_rect.left,
947 "dest-y", (gint) self->render_rect.top,
949 (gint) (self->render_rect.right - self->render_rect.left),
951 (gint) (self->render_rect.bottom - self->render_rect.top), nullptr);
952 gst_d3d11_overlay_compositor_update_viewport (self->compositor,
956 /* Converter preference order
957 * 1) If this texture can be converted via processor, and we used processor
958 * previously, use processor
959 * 2) If SRV is available, use converter
960 * 3) otherwise, use processor
962 if (can_process && self->processor_in_use) {
963 convert_ret = gst_d3d11_window_do_processor (self, piv, pov, &input_rect);
964 } else if (can_convert) {
965 convert_ret = gst_d3d11_window_do_convert (self, srv, rtv, &input_rect);
966 } else if (can_process) {
967 convert_ret = gst_d3d11_window_do_processor (self, piv, pov, &input_rect);
969 g_assert_not_reached ();
970 ret = GST_FLOW_ERROR;
975 ret = GST_FLOW_ERROR;
979 gst_d3d11_overlay_compositor_upload (self->compositor, buffer);
980 gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &rtv);
982 if (self->allow_tearing && self->fullscreen) {
983 present_flags |= DXGI_PRESENT_ALLOW_TEARING;
987 ret = klass->present (self, present_flags);
989 self->first_present = FALSE;
992 gst_d3d11_buffer_unmap (buffer, infos);
999 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer)
1004 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1007 mem = gst_buffer_peek_memory (buffer, 0);
1008 if (!gst_is_d3d11_memory (mem)) {
1009 GST_ERROR_OBJECT (window, "Invalid buffer");
1011 return GST_FLOW_ERROR;
1015 gst_d3d11_device_lock (window->device);
1017 gst_buffer_replace (&window->cached_buffer, buffer);
1019 ret = gst_d3d111_window_present (window, window->cached_buffer,
1020 window->pov, window->rtv);
1021 gst_d3d11_device_unlock (window->device);
1027 gst_d3d11_window_render_on_shared_handle (GstD3D11Window * window,
1028 GstBuffer * buffer, HANDLE shared_handle, guint texture_misc_flags,
1029 guint64 acquire_key, guint64 release_key)
1031 GstD3D11WindowClass *klass;
1033 GstFlowReturn ret = GST_FLOW_OK;
1034 GstD3D11WindowSharedHandleData data = { NULL, };
1035 ID3D11VideoProcessorOutputView *pov = NULL;
1036 ID3D11RenderTargetView *rtv = NULL;
1038 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
1040 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1042 g_assert (klass->open_shared_handle != NULL);
1043 g_assert (klass->release_shared_handle != NULL);
1045 mem = gst_buffer_peek_memory (buffer, 0);
1046 if (!gst_is_d3d11_memory (mem)) {
1047 GST_ERROR_OBJECT (window, "Invalid buffer");
1049 return GST_FLOW_ERROR;
1052 data.shared_handle = shared_handle;
1053 data.texture_misc_flags = texture_misc_flags;
1054 data.acquire_key = acquire_key;
1055 data.release_key = release_key;
1057 gst_d3d11_device_lock (window->device);
1058 if (!klass->open_shared_handle (window, &data)) {
1059 GST_ERROR_OBJECT (window, "Couldn't open shared handle");
1060 gst_d3d11_device_unlock (window->device);
1064 if (data.fallback_rtv) {
1065 rtv = data.fallback_rtv;
1066 pov = data.fallback_pov;
1072 ret = gst_d3d111_window_present (window, buffer, pov, rtv);
1074 klass->release_shared_handle (window, &data);
1075 gst_d3d11_device_unlock (window->device);
1081 gst_d3d11_window_unlock (GstD3D11Window * window)
1083 GstD3D11WindowClass *klass;
1084 gboolean ret = TRUE;
1086 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1088 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1091 ret = klass->unlock (window);
1097 gst_d3d11_window_unlock_stop (GstD3D11Window * window)
1099 GstD3D11WindowClass *klass;
1100 gboolean ret = TRUE;
1102 g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1104 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1106 if (klass->unlock_stop)
1107 ret = klass->unlock_stop (window);
1109 gst_d3d11_device_lock (window->device);
1110 gst_clear_buffer (&window->cached_buffer);
1111 gst_d3d11_device_unlock (window->device);
1117 gst_d3d11_window_unprepare (GstD3D11Window * window)
1119 GstD3D11WindowClass *klass;
1121 g_return_if_fail (GST_IS_D3D11_WINDOW (window));
1123 klass = GST_D3D11_WINDOW_GET_CLASS (window);
1125 if (klass->unprepare)
1126 klass->unprepare (window);
1129 GstD3D11WindowNativeType
1130 gst_d3d11_window_get_native_type_from_handle (guintptr handle)
1133 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1135 #if (!GST_D3D11_WINAPI_ONLY_APP)
1136 if (IsWindow ((HWND) handle))
1137 return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
1139 #if GST_D3D11_WINAPI_ONLY_APP
1142 ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
1143 ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
1144 ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
1147 if (SUCCEEDED (window.As (&core_window)))
1148 return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
1150 if (SUCCEEDED (window.As (&panel)))
1151 return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
1155 return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1159 gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
1162 case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
1164 case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
1166 case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
1167 return "core-window";
1168 case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
1169 return "swap-chain-panel";