394c7046d9a8e86bd84ec0ffce2ef2528524f845
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / d3d11 / gstd3d11window.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstd3d11window.h"
26 #include "gstd3d11pluginutils.h"
27
28 #if GST_D3D11_WINAPI_APP
29 /* workaround for GetCurrentTime collision */
30 #ifdef GetCurrentTime
31 #undef GetCurrentTime
32 #endif
33 #include <windows.ui.xaml.h>
34 #include <windows.applicationmodel.core.h>
35 #endif
36
37 #include <wrl.h>
38
39 /* *INDENT-OFF* */
40 using namespace Microsoft::WRL;
41 /* *INDENT-ON* */
42
43 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
44 #define GST_CAT_DEFAULT gst_d3d11_window_debug
45
46
47 enum
48 {
49   PROP_0,
50   PROP_D3D11_DEVICE,
51   PROP_FORCE_ASPECT_RATIO,
52   PROP_ENABLE_NAVIGATION_EVENTS,
53   PROP_FULLSCREEN_TOGGLE_MODE,
54   PROP_FULLSCREEN,
55   PROP_WINDOW_HANDLE,
56   PROP_RENDER_STATS,
57   PROP_EMIT_PRESENT,
58 };
59
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
65
66 enum
67 {
68   SIGNAL_KEY_EVENT,
69   SIGNAL_MOUSE_EVENT,
70   SIGNAL_PRESENT,
71   SIGNAL_LAST
72 };
73
74 static guint d3d11_window_signals[SIGNAL_LAST] = { 0, };
75
76 GType
77 gst_d3d11_window_fullscreen_toggle_mode_type (void)
78 {
79   static GType mode_type = 0;
80
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},
90     };
91
92     mode_type = g_flags_register_static ("GstD3D11WindowFullscreenToggleMode",
93         mode_types);
94   } GST_D3D11_CALL_ONCE_END;
95
96   return mode_type;
97 }
98
99 #define gst_d3d11_window_parent_class parent_class
100 G_DEFINE_ABSTRACT_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
101
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);
114
115 static void
116 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
117 {
118   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119
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;
123
124   klass->on_resize = GST_DEBUG_FUNCPTR (gst_d3d11_window_on_resize_default);
125   klass->prepare = GST_DEBUG_FUNCPTR (gst_d3d11_window_prepare_default);
126
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)));
133
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)));
140
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)));
147
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)));
154
155   g_object_class_install_property (gobject_class, PROP_FULLSCREEN,
156       g_param_spec_boolean ("fullscreen",
157           "fullscreen",
158           "Ignored when \"fullscreen-toggle-mode\" does not include \"property\"",
159           DEFAULT_FULLSCREEN,
160           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
161
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)));
167
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)));
172
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);
177
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);
182
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);
187 }
188
189 static void
190 gst_d3d11_window_init (GstD3D11Window * self)
191 {
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;
197 }
198
199 static void
200 gst_d3d11_window_set_property (GObject * object, guint prop_id,
201     const GValue * value, GParamSpec * pspec)
202 {
203   GstD3D11Window *self = GST_D3D11_WINDOW (object);
204   GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (object);
205
206   switch (prop_id) {
207     case PROP_D3D11_DEVICE:
208       self->device = (GstD3D11Device *) g_value_dup_object (value);
209       break;
210     case PROP_FORCE_ASPECT_RATIO:
211     {
212       self->force_aspect_ratio = g_value_get_boolean (value);
213       if (self->swap_chain)
214         klass->update_swap_chain (self);
215       break;
216     }
217     case PROP_ENABLE_NAVIGATION_EVENTS:
218       self->enable_navigation_events = g_value_get_boolean (value);
219       break;
220     case PROP_FULLSCREEN_TOGGLE_MODE:
221       self->fullscreen_toggle_mode =
222           (GstD3D11WindowFullscreenToggleMode) g_value_get_flags (value);
223       break;
224     case PROP_FULLSCREEN:
225     {
226       self->requested_fullscreen = g_value_get_boolean (value);
227       if (self->swap_chain)
228         klass->change_fullscreen_mode (self);
229       break;
230     }
231     case PROP_WINDOW_HANDLE:
232       self->external_handle = (guintptr) g_value_get_pointer (value);
233       break;
234     case PROP_EMIT_PRESENT:
235       self->emit_present = g_value_get_boolean (value);
236       break;
237     default:
238       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
239       break;
240   }
241 }
242
243 static void
244 gst_d3d11_window_get_property (GObject * object, guint prop_id,
245     GValue * value, GParamSpec * pspec)
246 {
247   GstD3D11Window *self = GST_D3D11_WINDOW (object);
248
249   switch (prop_id) {
250     case PROP_ENABLE_NAVIGATION_EVENTS:
251       g_value_set_boolean (value, self->enable_navigation_events);
252       break;
253     case PROP_FORCE_ASPECT_RATIO:
254       g_value_set_boolean (value, self->force_aspect_ratio);
255       break;
256     case PROP_FULLSCREEN_TOGGLE_MODE:
257       g_value_set_flags (value, self->fullscreen_toggle_mode);
258       break;
259     case PROP_FULLSCREEN:
260       g_value_set_boolean (value, self->fullscreen);
261       break;
262     case PROP_EMIT_PRESENT:
263       g_value_set_boolean (value, self->emit_present);
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268   }
269 }
270
271 static void
272 gst_d3d11_window_dispose (GObject * object)
273 {
274   GstD3D11Window *self = GST_D3D11_WINDOW (object);
275
276   gst_clear_buffer (&self->backbuffer);
277   GST_D3D11_CLEAR_COM (self->swap_chain);
278
279   gst_clear_object (&self->compositor);
280   gst_clear_object (&self->converter);
281
282   gst_clear_buffer (&self->cached_buffer);
283   gst_clear_object (&self->device);
284
285   G_OBJECT_CLASS (parent_class)->dispose (object);
286 }
287
288 static void
289 gst_d3d11_window_on_resize_default (GstD3D11Window * self, guint width,
290     guint height)
291 {
292   GstD3D11Device *device = self->device;
293   HRESULT hr;
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;
299   GstMemory *mem;
300   GstD3D11Memory *dmem;
301   ID3D11RenderTargetView *rtv;
302   ID3D11DeviceContext *context;
303   gsize size;
304   GstD3D11DeviceLockGuard lk (device);
305   const FLOAT clear_color[] = { 0.0, 0.0, 0.0, 1.0 };
306
307   gst_clear_buffer (&self->backbuffer);
308   if (!self->swap_chain)
309     return;
310
311   swap_chain = self->swap_chain;
312   swap_chain->GetDesc (&swap_desc);
313   hr = swap_chain->ResizeBuffers (0, width, height, self->dxgi_format,
314       swap_desc.Flags);
315   if (!gst_d3d11_result (hr, device)) {
316     GST_ERROR_OBJECT (self, "Couldn't resize buffers, hr: 0x%x", (guint) hr);
317     return;
318   }
319
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);
324     return;
325   }
326
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) {
333     size *= 8;
334   } else {
335     size *= 4;
336   }
337
338   mem = gst_d3d11_allocator_alloc_wrapped (nullptr,
339       self->device, backbuffer.Get (), size, nullptr, nullptr);
340   if (!mem) {
341     GST_ERROR_OBJECT (self, "Couldn't allocate wrapped memory");
342     return;
343   }
344
345   dmem = GST_D3D11_MEMORY_CAST (mem);
346   rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
347   if (!rtv) {
348     GST_ERROR_OBJECT (self, "RTV is unavailable");
349     gst_memory_unref (mem);
350     return;
351   }
352
353   context = gst_d3d11_device_get_device_context_handle (self->device);
354   context->ClearRenderTargetView (rtv, clear_color);
355
356   self->backbuffer = gst_buffer_new ();
357   gst_buffer_append_memory (self->backbuffer, mem);
358
359   self->surface_width = desc.Width;
360   self->surface_height = desc.Height;
361
362   dst_rect.x = 0;
363   dst_rect.y = 0;
364   dst_rect.w = self->surface_width;
365   dst_rect.h = self->surface_height;
366
367   if (self->force_aspect_ratio) {
368     src_rect.x = 0;
369     src_rect.y = 0;
370
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);
378         break;
379       default:
380         src_rect.w = GST_VIDEO_INFO_WIDTH (&self->render_info);
381         src_rect.h = GST_VIDEO_INFO_HEIGHT (&self->render_info);
382         break;
383     }
384
385     gst_video_sink_center_rect (src_rect, dst_rect, &rst_rect, TRUE);
386   } else {
387     rst_rect = dst_rect;
388   }
389
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;
394
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);
398
399   self->first_present = TRUE;
400
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);
404 }
405
406 void
407 gst_d3d11_window_on_key_event (GstD3D11Window * window, const gchar * event,
408     const gchar * key)
409 {
410   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
411
412   if (!window->enable_navigation_events)
413     return;
414
415   g_signal_emit (window, d3d11_window_signals[SIGNAL_KEY_EVENT], 0, event, key);
416 }
417
418 void
419 gst_d3d11_window_on_mouse_event (GstD3D11Window * window, const gchar * event,
420     gint button, gdouble x, gdouble y)
421 {
422   RECT render_rect;
423   GstVideoOrientationMethod method;
424   LONG xpos, ypos;
425   gdouble display_w, display_h, src_w, src_h;
426   gint in_w, in_h;
427
428   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
429
430   if (!window->enable_navigation_events)
431     return;
432
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);
439
440   display_w = render_rect.right - render_rect.left;
441   display_h = render_rect.bottom - render_rect.top;
442   xpos = (LONG) x;
443   ypos = (LONG) y;
444
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) {
450     return;
451   }
452
453   switch (method) {
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:
458       src_w = in_h;
459       src_h = in_w;
460       break;
461     default:
462       src_w = in_w;
463       src_h = in_h;
464       break;
465   }
466
467   xpos = ((xpos - render_rect.left) / display_w) * src_w;
468   ypos = ((ypos - render_rect.top) / display_h) * src_h;
469
470   xpos = CLAMP (xpos, 0, (LONG) (src_w - 1));
471   ypos = CLAMP (ypos, 0, (LONG) (src_h - 1));
472
473   /* Reverse rotate/flip if needed */
474   switch (method) {
475     case GST_VIDEO_ORIENTATION_90R:
476       x = ypos;
477       y = src_w - xpos;
478       break;
479     case GST_VIDEO_ORIENTATION_90L:
480       x = src_h - ypos;
481       y = xpos;
482       break;
483     case GST_VIDEO_ORIENTATION_UR_LL:
484       x = src_h - ypos;
485       y = src_w - xpos;
486       break;
487     case GST_VIDEO_ORIENTATION_UL_LR:
488       x = ypos;
489       y = xpos;
490       break;
491     case GST_VIDEO_ORIENTATION_180:
492       x = src_w - xpos;
493       y = src_h - ypos;
494       break;
495     case GST_VIDEO_ORIENTATION_HORIZ:
496       x = src_w - xpos;
497       y = ypos;
498       break;
499     case GST_VIDEO_ORIENTATION_VERT:
500       x = xpos;
501       y = src_h - ypos;
502       break;
503     default:
504       x = xpos;
505       y = ypos;
506       break;
507   }
508
509   g_signal_emit (window, d3d11_window_signals[SIGNAL_MOUSE_EVENT], 0,
510       event, button, x, y);
511 }
512
513 typedef struct
514 {
515   DXGI_FORMAT dxgi_format;
516   GstVideoFormat gst_format;
517   gboolean supported;
518 } GstD3D11WindowDisplayFormat;
519
520 GstFlowReturn
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)
524 {
525   GstD3D11WindowClass *klass;
526
527   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
528
529   klass = GST_D3D11_WINDOW_GET_CLASS (window);
530   g_assert (klass->prepare != NULL);
531
532   GST_DEBUG_OBJECT (window, "Prepare window, display resolution %dx%d, caps %"
533       GST_PTR_FORMAT, display_width, display_height, caps);
534
535   return klass->prepare (window, display_width, display_height, caps, config,
536       display_format, error);
537 }
538
539 static GstFlowReturn
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)
543 {
544   GstD3D11Device *device = window->device;
545   GstD3D11WindowClass *klass;
546   guint swapchain_flags = 0;
547   ID3D11Device *device_handle;
548   guint num_supported_format = 0;
549   HRESULT hr;
550   UINT display_flags =
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},
557   };
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;
569   GstStructure *s;
570   const gchar *cll_str = nullptr;
571   const gchar *mdcv_str = nullptr;
572
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);
577
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
581    */
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,
586         &supported_flags);
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++;
593     }
594   }
595
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");
600     if (config)
601       gst_structure_free (config);
602
603     return GST_FLOW_ERROR;
604   }
605
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];
612         break;
613       }
614     }
615
616     if (!chosen_format) {
617       GST_ERROR_OBJECT (window, "Requested DXGI FORMAT %d is not supported",
618           display_format);
619       g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
620           "Cannot determine render format");
621       if (config)
622         gst_structure_free (config);
623
624       return GST_FLOW_ERROR;
625     }
626   } else {
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];
631         }
632         break;
633       }
634     }
635   }
636
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];
643         break;
644       }
645     }
646
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];
652           break;
653         }
654       }
655     }
656   }
657
658   g_assert (chosen_format != nullptr);
659
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);
663
664   /* Step 3: Create swapchain
665    * (or reuse old swapchain if the format is not changed) */
666   window->allow_tearing = FALSE;
667
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));
673   }
674
675   if (SUCCEEDED (hr) && allow_tearing)
676     window->allow_tearing = allow_tearing;
677
678   if (window->allow_tearing) {
679     GST_DEBUG_OBJECT (window, "device supports tearing");
680     swapchain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
681   }
682
683   GstD3D11DeviceLockGuard lk (device);
684   window->dxgi_format = chosen_format->dxgi_format;
685
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");
694     if (config)
695       gst_structure_free (config);
696
697     return GST_FLOW_ERROR;
698   }
699
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;
705
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);
710
711   window->prev_input_rect = window->input_rect;
712
713   gst_video_info_set_format (&window->render_info,
714       chosen_format->gst_format, display_width, display_height);
715
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;
724
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;
732   }
733
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;
743       } else {
744         ComPtr < IDXGISwapChain4 > swapchain4;
745
746         /* DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12, undefined in old
747          * mingw header */
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, };
752
753             GST_DEBUG_OBJECT (window,
754                 "Have HDR metadata, set to DXGI swapchain");
755
756             gst_d3d11_hdr_meta_data_to_dxgi (&mdcv, &cll, &hdr10_metadata);
757
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);
763             } else {
764               hdr10_aware = TRUE;
765             }
766           }
767         }
768       }
769     }
770   }
771
772   GST_DEBUG_OBJECT (window, "Set colorspace %d", swapchain_colorspace);
773
774   /* update with selected display color space */
775   gst_video_info_apply_dxgi_color_space (swapchain_colorspace,
776       &window->render_info);
777
778   window->converter = gst_d3d11_converter_new (device,
779       &window->info, &window->render_info, config);
780
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;
786   }
787
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);
791     if (hdr10_aware) {
792       g_object_set (window->converter, "dest-mastering-display-info", mdcv_str,
793           "dest-content-light-level", cll_str, nullptr);
794     }
795   }
796
797   window->compositor =
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;
804   }
805
806   /* call resize to allocated resources */
807   klass->on_resize (window, display_width, display_height);
808
809   if (window->requested_fullscreen != window->fullscreen)
810     klass->change_fullscreen_mode (window);
811
812   GST_DEBUG_OBJECT (window, "New swap chain 0x%p created", window->swap_chain);
813
814   return GST_FLOW_OK;
815 }
816
817 void
818 gst_d3d11_window_set_render_rectangle (GstD3D11Window * window,
819     const GstVideoRectangle * rect)
820 {
821   GstD3D11WindowClass *klass;
822
823   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
824
825   klass = GST_D3D11_WINDOW_GET_CLASS (window);
826
827   if (klass->set_render_rectangle)
828     klass->set_render_rectangle (window, rect);
829 }
830
831 void
832 gst_d3d11_window_set_title (GstD3D11Window * window, const gchar * title)
833 {
834   GstD3D11WindowClass *klass;
835
836   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
837
838   klass = GST_D3D11_WINDOW_GET_CLASS (window);
839
840   if (klass->set_title)
841     klass->set_title (window, title);
842 }
843
844 static GstFlowReturn
845 gst_d3d111_window_present (GstD3D11Window * self, GstBuffer * buffer,
846     GstBuffer * backbuffer)
847 {
848   GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (self);
849   GstFlowReturn ret = GST_FLOW_OK;
850   guint present_flags = 0;
851   GstVideoCropMeta *crop_meta;
852   RECT input_rect = self->input_rect;
853   RECT *prev_rect = &self->prev_input_rect;
854   ID3D11RenderTargetView *rtv;
855   GstMemory *mem;
856   GstD3D11Memory *dmem;
857
858   if (!buffer)
859     return GST_FLOW_OK;
860
861   if (!backbuffer) {
862     GST_ERROR_OBJECT (self, "Empty render target");
863     return GST_FLOW_ERROR;
864   }
865
866   mem = gst_buffer_peek_memory (backbuffer, 0);
867   if (!gst_is_d3d11_memory (mem)) {
868     GST_ERROR_OBJECT (self, "Invalid back buffer");
869     return GST_FLOW_ERROR;
870   }
871
872   dmem = GST_D3D11_MEMORY_CAST (mem);
873   rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
874   if (!rtv) {
875     GST_ERROR_OBJECT (self, "RTV is unavailable");
876     return GST_FLOW_ERROR;
877   }
878
879   /* We use flip mode swapchain and will not redraw borders.
880    * So backbuffer should be cleared manually in order to remove artifact of
881    * previous client's rendering on present signal */
882   if (self->emit_present) {
883     const FLOAT clear_color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
884     ID3D11DeviceContext *context =
885         gst_d3d11_device_get_device_context_handle (self->device);
886
887     context->ClearRenderTargetView (rtv, clear_color);
888   }
889
890   crop_meta = gst_buffer_get_video_crop_meta (buffer);
891   if (crop_meta) {
892     input_rect.left = crop_meta->x;
893     input_rect.right = crop_meta->x + crop_meta->width;
894     input_rect.top = crop_meta->y;
895     input_rect.bottom = crop_meta->y + crop_meta->height;
896   }
897
898   if (input_rect.left != prev_rect->left || input_rect.top != prev_rect->top ||
899       input_rect.right != prev_rect->right ||
900       input_rect.bottom != prev_rect->bottom) {
901     g_object_set (self->converter, "src-x", (gint) input_rect.left,
902         "src-y", (gint) input_rect.top,
903         "src-width", (gint) (input_rect.right - input_rect.left),
904         "src-height", (gint) (input_rect.bottom - input_rect.top), nullptr);
905
906     self->prev_input_rect = input_rect;
907   }
908
909   if (self->first_present) {
910     D3D11_VIEWPORT viewport;
911
912     viewport.TopLeftX = self->render_rect.left;
913     viewport.TopLeftY = self->render_rect.top;
914     viewport.Width = self->render_rect.right - self->render_rect.left;
915     viewport.Height = self->render_rect.bottom - self->render_rect.top;
916     viewport.MinDepth = 0.0f;
917     viewport.MaxDepth = 1.0f;
918
919     g_object_set (self->converter, "dest-x", (gint) self->render_rect.left,
920         "dest-y", (gint) self->render_rect.top,
921         "dest-width",
922         (gint) (self->render_rect.right - self->render_rect.left),
923         "dest-height",
924         (gint) (self->render_rect.bottom - self->render_rect.top),
925         "video-direction", self->method, nullptr);
926     gst_d3d11_overlay_compositor_update_viewport (self->compositor, &viewport);
927   }
928
929   if (!gst_d3d11_converter_convert_buffer_unlocked (self->converter,
930           buffer, backbuffer)) {
931     GST_ERROR_OBJECT (self, "Couldn't render buffer");
932     return GST_FLOW_ERROR;
933   }
934
935   gst_d3d11_overlay_compositor_upload (self->compositor, buffer);
936   gst_d3d11_overlay_compositor_draw_unlocked (self->compositor, &rtv);
937
938   if (self->allow_tearing && self->fullscreen)
939     present_flags |= DXGI_PRESENT_ALLOW_TEARING;
940
941   if (klass->present) {
942     if (self->emit_present) {
943       g_signal_emit (self, d3d11_window_signals[SIGNAL_PRESENT], 0,
944           self->device, rtv, nullptr);
945     }
946     ret = klass->present (self, present_flags);
947   }
948
949   self->first_present = FALSE;
950
951   return ret;
952 }
953
954 GstFlowReturn
955 gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer)
956 {
957   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
958
959   GstD3D11DeviceLockGuard lk (window->device);
960   if (buffer)
961     gst_buffer_replace (&window->cached_buffer, buffer);
962
963   return gst_d3d111_window_present (window, window->cached_buffer,
964       window->backbuffer);
965 }
966
967 GstFlowReturn
968 gst_d3d11_window_render_on_shared_handle (GstD3D11Window * window,
969     GstBuffer * buffer, HANDLE shared_handle, guint texture_misc_flags,
970     guint64 acquire_key, guint64 release_key)
971 {
972   GstD3D11WindowClass *klass;
973   GstFlowReturn ret = GST_FLOW_OK;
974   GstD3D11WindowSharedHandleData data = { nullptr, };
975
976   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
977
978   klass = GST_D3D11_WINDOW_GET_CLASS (window);
979
980   g_assert (klass->open_shared_handle != NULL);
981   g_assert (klass->release_shared_handle != NULL);
982
983   data.shared_handle = shared_handle;
984   data.texture_misc_flags = texture_misc_flags;
985   data.acquire_key = acquire_key;
986   data.release_key = release_key;
987
988   GstD3D11DeviceLockGuard (window->device);
989   if (!klass->open_shared_handle (window, &data)) {
990     GST_ERROR_OBJECT (window, "Couldn't open shared handle");
991     return GST_FLOW_OK;
992   }
993
994   ret = gst_d3d111_window_present (window, buffer, data.render_target);
995
996   klass->release_shared_handle (window, &data);
997
998   return ret;
999 }
1000
1001 gboolean
1002 gst_d3d11_window_unlock (GstD3D11Window * window)
1003 {
1004   GstD3D11WindowClass *klass;
1005   gboolean ret = TRUE;
1006
1007   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1008
1009   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1010
1011   if (klass->unlock)
1012     ret = klass->unlock (window);
1013
1014   return ret;
1015 }
1016
1017 gboolean
1018 gst_d3d11_window_unlock_stop (GstD3D11Window * window)
1019 {
1020   GstD3D11WindowClass *klass;
1021   gboolean ret = TRUE;
1022
1023   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
1024
1025   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1026
1027   if (klass->unlock_stop)
1028     ret = klass->unlock_stop (window);
1029
1030   GstD3D11DeviceLockGuard lk (window->device);
1031   gst_clear_buffer (&window->cached_buffer);
1032
1033   return ret;
1034 }
1035
1036 void
1037 gst_d3d11_window_unprepare (GstD3D11Window * window)
1038 {
1039   GstD3D11WindowClass *klass;
1040
1041   g_return_if_fail (GST_IS_D3D11_WINDOW (window));
1042
1043   klass = GST_D3D11_WINDOW_GET_CLASS (window);
1044
1045   if (klass->unprepare)
1046     klass->unprepare (window);
1047 }
1048
1049 GstD3D11WindowNativeType
1050 gst_d3d11_window_get_native_type_from_handle (guintptr handle)
1051 {
1052   if (!handle)
1053     return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1054
1055 #if (!GST_D3D11_WINAPI_ONLY_APP)
1056   if (IsWindow ((HWND) handle))
1057     return GST_D3D11_WINDOW_NATIVE_TYPE_HWND;
1058 #endif
1059 #if GST_D3D11_WINAPI_ONLY_APP
1060   {
1061     /* *INDENT-OFF* */
1062     ComPtr<IInspectable> window = reinterpret_cast<IInspectable*> (handle);
1063     ComPtr<ABI::Windows::UI::Core::ICoreWindow> core_window;
1064     ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> panel;
1065     /* *INDENT-ON* */
1066
1067     if (SUCCEEDED (window.As (&core_window)))
1068       return GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW;
1069
1070     if (SUCCEEDED (window.As (&panel)))
1071       return GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL;
1072   }
1073 #endif
1074
1075   return GST_D3D11_WINDOW_NATIVE_TYPE_NONE;
1076 }
1077
1078 const gchar *
1079 gst_d3d11_window_get_native_type_to_string (GstD3D11WindowNativeType type)
1080 {
1081   switch (type) {
1082     case GST_D3D11_WINDOW_NATIVE_TYPE_NONE:
1083       return "none";
1084     case GST_D3D11_WINDOW_NATIVE_TYPE_HWND:
1085       return "hwnd";
1086     case GST_D3D11_WINDOW_NATIVE_TYPE_CORE_WINDOW:
1087       return "core-window";
1088     case GST_D3D11_WINDOW_NATIVE_TYPE_SWAP_CHAIN_PANEL:
1089       return "swap-chain-panel";
1090     default:
1091       break;
1092   }
1093
1094   return "none";
1095 }
1096
1097 void
1098 gst_d3d11_window_set_orientation (GstD3D11Window * window,
1099     GstVideoOrientationMethod method)
1100 {
1101   if (method == GST_VIDEO_ORIENTATION_AUTO ||
1102       method == GST_VIDEO_ORIENTATION_CUSTOM) {
1103     return;
1104   }
1105
1106   GstD3D11DeviceLockGuard lk (window->device);
1107   if (window->method != method) {
1108     window->method = method;
1109     if (window->swap_chain) {
1110       GstD3D11WindowClass *klass = GST_D3D11_WINDOW_GET_CLASS (window);
1111
1112       klass->on_resize (window, window->surface_width, window->surface_height);
1113     }
1114   }
1115 }