• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_swapchainpanel.h"
26 
27 /* workaround for GetCurrentTime collision */
28 #ifdef GetCurrentTime
29 #undef GetCurrentTime
30 #endif
31 #include <windows.ui.xaml.h>
32 #include <windows.ui.xaml.media.dxinterop.h>
33 #include <windows.applicationmodel.core.h>
34 #include <wrl.h>
35 #include <wrl/wrappers/corewrappers.h>
36 
37 /* *INDENT-OFF* */
38 
39 using namespace Microsoft::WRL;
40 using namespace Microsoft::WRL::Wrappers;
41 using namespace ABI::Windows::UI;
42 using namespace ABI::Windows::Foundation;
43 
44 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
45 #define GST_CAT_DEFAULT gst_d3d11_window_debug
46 
47 /* timeout to wait busy UI thread */
48 #define DEFAULT_ASYNC_TIMEOUT (10 * 1000)
49 
50 typedef struct _SwapChainPanelWinRTStorage
51 {
52   ComPtr<Xaml::Controls::ISwapChainPanel> panel;
53   ComPtr<Core::ICoreDispatcher> dispatcher;
54   ComPtr<IDXGISwapChain1> swapchain;
55   HANDLE cancellable;
56   EventRegistrationToken event_token;
57 } SwapChainPanelWinRTStorage;
58 /* *INDENT-ON* */
59 
60 struct _GstD3D11WindowSwapChainPanel
61 {
62   GstD3D11Window parent;
63 
64   SwapChainPanelWinRTStorage *storage;
65 };
66 
67 #define gst_d3d11_window_swap_chain_panel_parent_class parent_class
68 G_DEFINE_TYPE (GstD3D11WindowSwapChainPanel,
69     gst_d3d11_window_swap_chain_panel, GST_TYPE_D3D11_WINDOW);
70 
71 static void gst_d3d11_window_swap_chain_panel_constructed (GObject * object);
72 static void gst_d3d11_window_swap_chain_panel_dispose (GObject * object);
73 
74 static void
75 gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window);
76 static void
77 gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window *
78     window);
79 static gboolean
80 gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window,
81     DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
82     IDXGISwapChain ** swap_chain);
83 static GstFlowReturn
84 gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window,
85     guint present_flags);
86 static gboolean
87 gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window);
88 static gboolean
89 gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window);
90 static void
91 gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11Window * window,
92     guint width, guint height);
93 static void
94 gst_d3d11_window_swap_chain_panel_on_resize_sync (GstD3D11Window * window);
95 static void
96 gst_d3d11_window_swap_chain_panel_unprepare (GstD3D11Window * window);
97 
98 /* *INDENT-OFF* */
99 class PanelResizeHandler
100     : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
101         Xaml::ISizeChangedEventHandler>
102 {
103 public:
PanelResizeHandler()104   PanelResizeHandler () {}
RuntimeClassInitialize(GstD3D11Window * listener)105   HRESULT RuntimeClassInitialize (GstD3D11Window * listener)
106   {
107     if (!listener)
108       return E_INVALIDARG;
109 
110     window = listener;
111     return S_OK;
112   }
113 
IFACEMETHOD(Invoke)114   IFACEMETHOD(Invoke)
115   (IInspectable * sender, Xaml::ISizeChangedEventArgs * args)
116   {
117     if (window) {
118       Size new_size;
119       HRESULT hr = args->get_NewSize(&new_size);
120       if (SUCCEEDED(hr)) {
121         window->surface_width = new_size.Width;
122         window->surface_height = new_size.Height;
123         gst_d3d11_window_swap_chain_panel_on_resize_sync (window);
124       }
125     }
126 
127     return S_OK;
128   }
129 
130 private:
131   GstD3D11Window * window;
132 };
133 
134 template <typename CB>
135 static HRESULT
run_async(const ComPtr<Core::ICoreDispatcher> & dispatcher,HANDLE cancellable,DWORD timeout,CB && cb)136 run_async (const ComPtr<Core::ICoreDispatcher> &dispatcher, HANDLE cancellable,
137     DWORD timeout, CB &&cb)
138 {
139   ComPtr<IAsyncAction> async_action;
140   HRESULT hr;
141   HRESULT async_hr;
142   boolean can_now;
143   DWORD wait_ret;
144   HANDLE event_handle[2];
145 
146   hr = dispatcher->get_HasThreadAccess (&can_now);
147 
148   if (!gst_d3d11_result (hr, NULL))
149     return hr;
150 
151   if (can_now)
152     return cb ();
153 
154   Event event (CreateEventEx (NULL, NULL, CREATE_EVENT_MANUAL_RESET,
155       EVENT_ALL_ACCESS));
156 
157   if (!event.IsValid())
158     return E_FAIL;
159 
160   auto handler =
161       Callback<Implements<RuntimeClassFlags<ClassicCom>,
162           Core::IDispatchedHandler, FtmBase>>([&async_hr, &cb, &event] {
163         async_hr = cb ();
164         SetEvent (event.Get());
165         return S_OK;
166       });
167 
168   hr = dispatcher->RunAsync (Core::CoreDispatcherPriority_Normal, handler.Get(),
169       async_action.GetAddressOf());
170 
171   if (!gst_d3d11_result (hr, NULL))
172     return hr;
173 
174   event_handle[0] = event.Get();
175   event_handle[1] = cancellable;
176 
177   wait_ret = WaitForMultipleObjects (2, event_handle, FALSE, timeout);
178   if (wait_ret != WAIT_OBJECT_0)
179     return E_FAIL;
180 
181   return async_hr;
182 }
183 
184 static HRESULT
get_panel_size(const ComPtr<Core::ICoreDispatcher> & dispatcher,HANDLE cancellable,const ComPtr<Xaml::Controls::ISwapChainPanel> & panel,Size * size)185 get_panel_size (const ComPtr<Core::ICoreDispatcher> &dispatcher,
186     HANDLE cancellable,
187     const ComPtr<Xaml::Controls::ISwapChainPanel> &panel, Size *size)
188 {
189   ComPtr<Xaml::IUIElement> ui;
190   HRESULT hr = panel.As (&ui);
191 
192   if (!gst_d3d11_result (hr, NULL))
193     return hr;
194 
195   hr = run_async (dispatcher, cancellable, DEFAULT_ASYNC_TIMEOUT,
196       [ui, size] {
197         return ui->get_RenderSize (size);
198       });
199 
200   return hr;
201 }
202 /* *INDENT-ON* */
203 
204 static void
gst_d3d11_window_swap_chain_panel_class_init(GstD3D11WindowSwapChainPanelClass * klass)205 gst_d3d11_window_swap_chain_panel_class_init (GstD3D11WindowSwapChainPanelClass
206     * klass)
207 {
208   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
209   GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
210 
211   gobject_class->constructed = gst_d3d11_window_swap_chain_panel_constructed;
212   gobject_class->dispose = gst_d3d11_window_swap_chain_panel_dispose;
213 
214   window_class->update_swap_chain =
215       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_update_swap_chain);
216   window_class->change_fullscreen_mode =
217       GST_DEBUG_FUNCPTR
218       (gst_d3d11_window_swap_chain_panel_change_fullscreen_mode);
219   window_class->create_swap_chain =
220       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_create_swap_chain);
221   window_class->present =
222       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_present);
223   window_class->unlock =
224       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock);
225   window_class->unlock_stop =
226       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unlock_stop);
227   window_class->on_resize =
228       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_on_resize);
229   window_class->unprepare =
230       GST_DEBUG_FUNCPTR (gst_d3d11_window_swap_chain_panel_unprepare);
231 }
232 
233 static void
gst_d3d11_window_swap_chain_panel_init(GstD3D11WindowSwapChainPanel * self)234 gst_d3d11_window_swap_chain_panel_init (GstD3D11WindowSwapChainPanel * self)
235 {
236   self->storage = new SwapChainPanelWinRTStorage;
237   self->storage->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
238 }
239 
240 /* *INDENT-OFF* */
241 static void
gst_d3d11_window_swap_chain_panel_constructed(GObject * object)242 gst_d3d11_window_swap_chain_panel_constructed (GObject * object)
243 {
244   GstD3D11Window *window = GST_D3D11_WINDOW (object);
245   GstD3D11WindowSwapChainPanel *self =
246       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (object);
247   SwapChainPanelWinRTStorage *storage = self->storage;
248   HRESULT hr;
249   ComPtr<IInspectable> inspectable;
250   ComPtr<Xaml::IDependencyObject> dependency_obj;
251   Size size;
252   ComPtr<Xaml::ISizeChangedEventHandler> resize_handler;
253   ComPtr<Xaml::IFrameworkElement> framework;
254 
255   if (!window->external_handle) {
256     GST_ERROR_OBJECT (self, "No external window handle");
257     return;
258   }
259 
260   inspectable = reinterpret_cast<IInspectable*> (window->external_handle);
261 
262   hr = inspectable.As (&storage->panel);
263   if (!gst_d3d11_result (hr, NULL))
264     goto error;
265 
266   hr = storage->panel.As (&dependency_obj);
267   if (!gst_d3d11_result (hr, NULL))
268     goto error;
269 
270   hr = dependency_obj->get_Dispatcher(storage->dispatcher.GetAddressOf());
271   if (!gst_d3d11_result (hr, NULL))
272     goto error;
273 
274   hr = get_panel_size (storage->dispatcher,
275       storage->cancellable, storage->panel, &size);
276   if (!gst_d3d11_result (hr, NULL))
277     goto error;
278 
279   GST_DEBUG_OBJECT (self, "client size %dx%d", size.Width, size.Height);
280   window->surface_width = size.Width;
281   window->surface_height = size.Height;
282 
283   hr = MakeAndInitialize<PanelResizeHandler>(&resize_handler, window);
284   if (!gst_d3d11_result (hr, NULL))
285     goto error;
286 
287   hr = storage->panel.As (&framework);
288   if (!gst_d3d11_result (hr, NULL))
289     goto error;
290 
291   hr = run_async (storage->dispatcher,
292       storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
293       [self, framework, resize_handler] {
294         return framework->add_SizeChanged (resize_handler.Get(),
295             &self->storage->event_token);
296       });
297   if (!gst_d3d11_result (hr, NULL))
298     goto error;
299 
300   window->initialized = TRUE;
301 
302   return;
303 
304 error:
305   GST_ERROR_OBJECT (self, "Invalid window handle");
306   return;
307 }
308 /* *INDENT-ON* */
309 
310 static void
gst_d3d11_window_swap_chain_panel_dispose(GObject * object)311 gst_d3d11_window_swap_chain_panel_dispose (GObject * object)
312 {
313   gst_d3d11_window_swap_chain_panel_unprepare (GST_D3D11_WINDOW (object));
314 
315   G_OBJECT_CLASS (parent_class)->dispose (object);
316 }
317 
318 /* *INDENT-OFF* */
319 static void
gst_d3d11_window_swap_chain_panel_unprepare(GstD3D11Window * window)320 gst_d3d11_window_swap_chain_panel_unprepare (GstD3D11Window * window)
321 {
322   GstD3D11WindowSwapChainPanel *self =
323       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
324   SwapChainPanelWinRTStorage *storage = self->storage;
325 
326   if (storage) {
327     if (storage->panel && storage->dispatcher) {
328       ComPtr<Xaml::IFrameworkElement> framework;
329       HRESULT hr;
330 
331       hr = storage->panel.As (&framework);
332       if (SUCCEEDED (hr)) {
333         run_async (storage->dispatcher,
334             storage->cancellable, DEFAULT_ASYNC_TIMEOUT,
335             [self, framework] {
336               return framework->remove_SizeChanged (self->storage->event_token);
337             });
338       }
339 
340       CloseHandle (storage->cancellable);
341     }
342 
343     delete storage;
344   }
345 
346   self->storage = NULL;
347 }
348 /* *INDENT-ON* */
349 
350 static IDXGISwapChain1 *
create_swap_chain_for_composition(GstD3D11WindowSwapChainPanel * self,GstD3D11Device * device,DXGI_SWAP_CHAIN_DESC1 * desc,IDXGIOutput * output)351 create_swap_chain_for_composition (GstD3D11WindowSwapChainPanel * self,
352     GstD3D11Device * device, DXGI_SWAP_CHAIN_DESC1 * desc, IDXGIOutput * output)
353 {
354   HRESULT hr;
355   IDXGISwapChain1 *swap_chain = NULL;
356   ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
357   IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
358   ComPtr < IDXGIFactory2 > factory2;
359 
360   hr = factory->QueryInterface (IID_PPV_ARGS (&factory2));
361   if (!gst_d3d11_result (hr, device)) {
362     GST_WARNING_OBJECT (self, "IDXGIFactory2 interface is unavailable");
363     return NULL;
364   }
365 
366   gst_d3d11_device_lock (device);
367   hr = factory2->CreateSwapChainForComposition (device_handle,
368       desc, output, &swap_chain);
369   gst_d3d11_device_unlock (device);
370 
371   if (!gst_d3d11_result (hr, device)) {
372     GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
373         (guint) hr);
374     swap_chain = NULL;
375   }
376 
377   return swap_chain;
378 }
379 
380 /* *INDENT-OFF* */
381 static gboolean
gst_d3d11_window_swap_chain_panel_create_swap_chain(GstD3D11Window * window,DXGI_FORMAT format,guint width,guint height,guint swapchain_flags,IDXGISwapChain ** swap_chain)382 gst_d3d11_window_swap_chain_panel_create_swap_chain (GstD3D11Window * window,
383     DXGI_FORMAT format, guint width, guint height, guint swapchain_flags,
384     IDXGISwapChain ** swap_chain)
385 {
386   GstD3D11WindowSwapChainPanel *self =
387       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
388   SwapChainPanelWinRTStorage *storage = self->storage;
389   ComPtr<IDXGISwapChain1> new_swapchain;
390   ComPtr<ISwapChainPanelNative> panel_native;
391   GstD3D11Device *device = window->device;
392   DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
393   HRESULT hr;
394 
395   desc1.Width = width;
396   desc1.Height = height;
397   desc1.Format = format;
398   desc1.Stereo = FALSE;
399   desc1.SampleDesc.Count = 1;
400   desc1.SampleDesc.Quality = 0;
401   desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
402   desc1.BufferCount = 2;
403   desc1.Scaling = DXGI_SCALING_STRETCH;
404   desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
405   desc1.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
406   desc1.Flags = swapchain_flags;
407 
408   new_swapchain =
409     create_swap_chain_for_composition (self, device, &desc1, NULL);
410 
411   if (!new_swapchain) {
412     GST_ERROR_OBJECT (self, "Cannot create swapchain");
413     return FALSE;
414   }
415 
416   hr = storage->panel.As (&panel_native);
417   if (!gst_d3d11_result (hr, device))
418     return FALSE;
419 
420   hr = run_async (storage->dispatcher,
421       storage->cancellable, INFINITE,
422       [panel_native, new_swapchain] {
423         return panel_native->SetSwapChain(new_swapchain.Get());
424       });
425 
426   if (!gst_d3d11_result (hr, device))
427     return FALSE;
428 
429   new_swapchain.CopyTo (swap_chain);
430 
431   return TRUE;
432 }
433 /* *INDENT-ON* */
434 
435 static GstFlowReturn
gst_d3d11_window_swap_chain_panel_present(GstD3D11Window * window,guint present_flags)436 gst_d3d11_window_swap_chain_panel_present (GstD3D11Window * window,
437     guint present_flags)
438 {
439   GstD3D11WindowSwapChainPanel *self =
440       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
441   HRESULT hr;
442   DXGI_PRESENT_PARAMETERS present_params = { 0, };
443   IDXGISwapChain1 *swap_chain = (IDXGISwapChain1 *) window->swap_chain;
444 
445   /* the first present should not specify dirty-rect */
446   if (!window->first_present) {
447     present_params.DirtyRectsCount = 1;
448     present_params.pDirtyRects = &window->render_rect;
449   }
450 
451   hr = swap_chain->Present1 (0, present_flags, &present_params);
452 
453   if (!gst_d3d11_result (hr, window->device)) {
454     GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
455         (guint) hr);
456   }
457 
458   return GST_FLOW_OK;
459 }
460 
461 static gboolean
gst_d3d11_window_swap_chain_panel_unlock(GstD3D11Window * window)462 gst_d3d11_window_swap_chain_panel_unlock (GstD3D11Window * window)
463 {
464   GstD3D11WindowSwapChainPanel *self =
465       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
466   SwapChainPanelWinRTStorage *storage = self->storage;
467 
468   SetEvent (storage->cancellable);
469 
470   return TRUE;
471 }
472 
473 static gboolean
gst_d3d11_window_swap_chain_panel_unlock_stop(GstD3D11Window * window)474 gst_d3d11_window_swap_chain_panel_unlock_stop (GstD3D11Window * window)
475 {
476   GstD3D11WindowSwapChainPanel *self =
477       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
478   SwapChainPanelWinRTStorage *storage = self->storage;
479 
480   ResetEvent (storage->cancellable);
481 
482   return TRUE;
483 }
484 
485 static void
gst_d3d11_window_swap_chain_panel_update_swap_chain(GstD3D11Window * window)486 gst_d3d11_window_swap_chain_panel_update_swap_chain (GstD3D11Window * window)
487 {
488   gst_d3d11_window_swap_chain_panel_on_resize (window, window->surface_width,
489       window->surface_height);
490 
491   return;
492 }
493 
494 static void
gst_d3d11_window_swap_chain_panel_change_fullscreen_mode(GstD3D11Window * window)495 gst_d3d11_window_swap_chain_panel_change_fullscreen_mode (GstD3D11Window *
496     window)
497 {
498   GST_FIXME_OBJECT (window, "Implement fullscreen mode change");
499 
500   return;
501 }
502 
503 static void
gst_d3d11_window_swap_chain_panel_on_resize(GstD3D11Window * window,guint width,guint height)504 gst_d3d11_window_swap_chain_panel_on_resize (GstD3D11Window * window,
505     guint width, guint height)
506 {
507   GstD3D11WindowSwapChainPanel *self =
508       GST_D3D11_WINDOW_SWAP_CHAIN_PANEL (window);
509   SwapChainPanelWinRTStorage *storage = self->storage;
510 
511   run_async (storage->dispatcher, storage->cancellable, INFINITE,[window] {
512         gst_d3d11_window_swap_chain_panel_on_resize_sync (window);
513         return S_OK;
514       }
515   );
516 }
517 
518 static void
gst_d3d11_window_swap_chain_panel_on_resize_sync(GstD3D11Window * window)519 gst_d3d11_window_swap_chain_panel_on_resize_sync (GstD3D11Window * window)
520 {
521   GST_LOG_OBJECT (window,
522       "New size %dx%d", window->surface_width, window->surface_height);
523 
524   GST_D3D11_WINDOW_CLASS (parent_class)->on_resize (window,
525       window->surface_width, window->surface_height);
526 }
527 
528 GstD3D11Window *
gst_d3d11_window_swap_chain_panel_new(GstD3D11Device * device,guintptr handle)529 gst_d3d11_window_swap_chain_panel_new (GstD3D11Device * device, guintptr handle)
530 {
531   GstD3D11Window *window;
532 
533   g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
534   g_return_val_if_fail (handle, NULL);
535 
536   window = (GstD3D11Window *)
537       g_object_new (GST_TYPE_D3D11_WINDOW_SWAP_CHAIN_PANEL,
538       "d3d11device", device, "window-handle", handle, NULL);
539   if (!window->initialized) {
540     gst_object_unref (window);
541     return NULL;
542   }
543 
544   g_object_ref_sink (window);
545 
546   return window;
547 }
548