3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
5 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
27 #include "gstd3d11window_win32.h"
31 using namespace Microsoft::WRL;
34 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
35 #define GST_CAT_DEFAULT gst_d3d11_window_debug
37 G_LOCK_DEFINE_STATIC (create_lock);
39 #define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc"
40 #define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_win32_object"
42 #define WM_GST_D3D11_FULLSCREEN (WM_USER + 1)
43 #define WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW (WM_USER + 2)
44 #define WM_GST_D3D11_DESTROY_INTERNAL_WINDOW (WM_USER + 3)
45 #define WM_GST_D3D11_MOVE_WINDOW (WM_USER + 4)
47 static LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
49 static LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
54 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE = 0,
55 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED,
56 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED,
57 } GstD3D11WindowWin32OverlayState;
59 struct _GstD3D11WindowWin32
61 GstD3D11Window parent;
66 GMainContext *main_context;
72 GIOChannel *msg_io_channel;
76 GThread *internal_hwnd_thread;
80 GstD3D11WindowWin32OverlayState overlay_state;
83 gboolean first_present;
84 gboolean have_swapchain1;
87 gint pending_fullscreen_count;
88 gint pending_move_window;
90 /* fullscreen related */
94 /* Handle set_render_rectangle */
95 GstVideoRectangle render_rect;
98 #define gst_d3d11_window_win32_parent_class parent_class
99 G_DEFINE_TYPE (GstD3D11WindowWin32, gst_d3d11_window_win32,
100 GST_TYPE_D3D11_WINDOW);
102 static void gst_d3d11_window_win32_constructed (GObject * object);
103 static void gst_d3d11_window_win32_dispose (GObject * object);
104 static void gst_d3d11_window_win32_finalize (GObject * object);
106 static void gst_d3d11_window_win32_show (GstD3D11Window * window);
107 static void gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window);
109 gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window);
111 gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
112 DXGI_FORMAT format, guint width, guint height,
113 guint swapchain_flags, IDXGISwapChain ** swap_chain);
114 static GstFlowReturn gst_d3d11_window_win32_present (GstD3D11Window * window,
115 guint present_flags);
117 static gpointer gst_d3d11_window_win32_thread_func (gpointer data);
119 gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self);
120 static void gst_d3d11_window_win32_destroy_internal_window (HWND hwnd);
121 static void gst_d3d11_window_win32_release_external_handle (HWND hwnd);
123 gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
126 gst_d3d11_window_win32_on_resize (GstD3D11Window * window,
127 guint width, guint height);
128 static void gst_d3d11_window_win32_unprepare (GstD3D11Window * window);
130 gst_d3d11_window_win32_set_render_rectangle (GstD3D11Window * window,
131 const GstVideoRectangle * rect);
132 static void gst_d3d11_window_win32_set_title (GstD3D11Window * window,
133 const gchar * title);
136 gst_d3d11_window_win32_class_init (GstD3D11WindowWin32Class * klass)
138 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
139 GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
141 gobject_class->constructed = gst_d3d11_window_win32_constructed;
142 gobject_class->dispose = gst_d3d11_window_win32_dispose;
143 gobject_class->finalize = gst_d3d11_window_win32_finalize;
145 window_class->show = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_show);
146 window_class->update_swap_chain =
147 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_update_swap_chain);
148 window_class->change_fullscreen_mode =
149 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_change_fullscreen_mode);
150 window_class->create_swap_chain =
151 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_create_swap_chain);
152 window_class->present = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_present);
153 window_class->on_resize =
154 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_on_resize);
155 window_class->unprepare =
156 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_unprepare);
157 window_class->set_render_rectangle =
158 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_set_render_rectangle);
159 window_class->set_title =
160 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_set_title);
164 gst_d3d11_window_win32_init (GstD3D11WindowWin32 * self)
166 g_mutex_init (&self->lock);
167 g_cond_init (&self->cond);
169 self->main_context = g_main_context_new ();
173 gst_d3d11_window_win32_constructed (GObject * object)
175 GstD3D11Window *window = GST_D3D11_WINDOW (object);
176 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
178 if (window->external_handle) {
179 gst_d3d11_window_win32_set_window_handle (self, window->external_handle);
183 g_mutex_lock (&self->lock);
184 self->loop = g_main_loop_new (self->main_context, FALSE);
185 self->thread = g_thread_new ("GstD3D11WindowWin32",
186 (GThreadFunc) gst_d3d11_window_win32_thread_func, self);
187 while (!g_main_loop_is_running (self->loop))
188 g_cond_wait (&self->cond, &self->lock);
189 g_mutex_unlock (&self->lock);
192 G_OBJECT_CLASS (parent_class)->constructed (object);
196 gst_d3d11_window_win32_dispose (GObject * object)
198 gst_d3d11_window_win32_unprepare (GST_D3D11_WINDOW (object));
200 G_OBJECT_CLASS (parent_class)->dispose (object);
204 gst_d3d11_window_win32_unprepare (GstD3D11Window * window)
206 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
208 if (self->external_hwnd) {
209 gst_d3d11_window_win32_release_external_handle (self->external_hwnd);
210 RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
212 if (self->internal_hwnd_thread == g_thread_self ()) {
213 /* State changing thread is identical to internal window thread.
214 * window can be closed here */
216 GST_INFO_OBJECT (self, "Closing internal window immediately");
217 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
219 /* We cannot destroy internal window from non-window thread.
220 * and we cannot use synchronously SendMessage() method at this point
221 * since window thread might be wait for current thread and SendMessage()
222 * will be blocked until it's called from window thread.
223 * Instead, posts message so that it can be closed from window thread
225 GST_INFO_OBJECT (self, "Posting custom destory message");
226 PostMessage (self->internal_hwnd, WM_GST_D3D11_DESTROY_INTERNAL_WINDOW,
230 self->external_hwnd = NULL;
231 self->internal_hwnd = NULL;
232 self->internal_hwnd_thread = NULL;
236 g_main_loop_quit (self->loop);
240 g_thread_join (self->thread);
245 g_main_loop_unref (self->loop);
249 if (self->main_context) {
250 g_main_context_unref (self->main_context);
251 self->main_context = NULL;
256 gst_d3d11_window_win32_set_render_rectangle (GstD3D11Window * window,
257 const GstVideoRectangle * rect)
259 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
261 if (self->external_hwnd && self->internal_hwnd) {
262 g_atomic_int_add (&self->pending_move_window, 1);
263 self->render_rect = *rect;
265 if (self->internal_hwnd_thread == g_thread_self ()) {
266 /* We are on message pumping thread already, handle this synchroniously */
267 SendMessage (self->internal_hwnd, WM_GST_D3D11_MOVE_WINDOW, 0, 0);
269 /* Post message to message pumping thread. Handling HWND specific message
270 * on message pumping thread is not a worst idea in generall */
271 PostMessage (self->internal_hwnd, WM_GST_D3D11_MOVE_WINDOW, 0, 0);
274 /* XXX: Not sure what's expected behavior if we are drawing on internal
275 * HWND but user wants to specify rectangle.
277 * - Should we move window to corresponding desktop coordinates ?
278 * - Or should crop correspondingly by modifying viewport of
279 * render target view of swapchian's backbuffer or so ?
280 * - Or should we ignore set_render_rectangle if we are drawing on
281 * internal HWND without external HWND ?
287 gst_d3d11_window_win32_set_title (GstD3D11Window * window, const gchar * title)
289 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
291 /* Do this only when we are rendring on our own HWND */
292 if (!self->external_hwnd && self->internal_hwnd) {
293 gunichar2 *str = g_utf8_to_utf16 (title, -1, nullptr, nullptr, nullptr);
296 SetWindowTextW (self->internal_hwnd, (LPCWSTR) str);
303 gst_d3d11_window_win32_finalize (GObject * object)
305 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
307 g_mutex_clear (&self->lock);
308 g_cond_clear (&self->cond);
310 G_OBJECT_CLASS (parent_class)->finalize (object);
314 running_cb (gpointer user_data)
316 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (user_data);
318 GST_TRACE_OBJECT (self, "Main loop running now");
320 g_mutex_lock (&self->lock);
321 g_cond_signal (&self->cond);
322 g_mutex_unlock (&self->lock);
324 return G_SOURCE_REMOVE;
328 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
332 if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
333 return G_SOURCE_CONTINUE;
335 TranslateMessage (&msg);
336 DispatchMessage (&msg);
338 return G_SOURCE_CONTINUE;
342 gst_d3d11_window_win32_thread_func (gpointer data)
344 GstD3D11Window *window = GST_D3D11_WINDOW (data);
345 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (data);
348 GST_DEBUG_OBJECT (self, "Enter loop");
349 g_main_context_push_thread_default (self->main_context);
351 window->initialized = gst_d3d11_window_win32_create_internal_window (self);
353 self->msg_io_channel =
354 g_io_channel_win32_new_messages ((guintptr) self->internal_hwnd);
355 self->msg_source = g_io_create_watch (self->msg_io_channel, G_IO_IN);
356 g_source_set_callback (self->msg_source, (GSourceFunc) msg_cb, self, NULL);
357 g_source_attach (self->msg_source, self->main_context);
359 source = g_idle_source_new ();
360 g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL);
361 g_source_attach (source, self->main_context);
362 g_source_unref (source);
364 g_main_loop_run (self->loop);
366 RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
367 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
368 self->internal_hwnd = NULL;
369 self->internal_hwnd_thread = NULL;
371 if (self->msg_source) {
372 g_source_destroy (self->msg_source);
373 g_source_unref (self->msg_source);
374 self->msg_source = NULL;
377 if (self->msg_io_channel) {
378 g_io_channel_unref (self->msg_io_channel);
379 self->msg_io_channel = NULL;
382 g_main_context_pop_thread_default (self->main_context);
384 GST_DEBUG_OBJECT (self, "Exit loop");
390 gst_d3d11_window_win32_destroy_internal_window (HWND hwnd)
395 SetParent (hwnd, NULL);
397 GST_INFO ("Destroying internal window %" G_GUINTPTR_FORMAT, (guintptr) hwnd);
399 if (!DestroyWindow (hwnd))
400 GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT
401 ", 0x%x", (guintptr) hwnd, (guint) GetLastError ());
405 gst_d3d11_window_win32_set_external_handle (GstD3D11WindowWin32 * self)
407 WNDPROC external_window_proc;
409 external_window_proc =
410 (WNDPROC) GetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC);
412 GST_DEBUG_OBJECT (self, "set external window %" G_GUINTPTR_FORMAT
413 ", original window procedure %p", (guintptr) self->external_hwnd,
414 external_window_proc);
416 SetProp (self->external_hwnd, EXTERNAL_PROC_PROP_NAME,
417 (HANDLE) external_window_proc);
418 SetProp (self->external_hwnd, D3D11_WINDOW_PROP_NAME, self);
419 SetWindowLongPtr (self->external_hwnd, GWLP_WNDPROC,
420 (LONG_PTR) sub_class_proc);
422 /* Will create our internal window on parent window's thread */
423 SendMessage (self->external_hwnd, WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW,
428 gst_d3d11_window_win32_release_external_handle (HWND hwnd)
430 WNDPROC external_proc;
435 external_proc = (WNDPROC) GetProp (hwnd, EXTERNAL_PROC_PROP_NAME);
436 if (!external_proc) {
437 GST_WARNING ("Failed to get original window procedure");
441 GST_DEBUG ("release external window %" G_GUINTPTR_FORMAT
442 ", original window procedure %p", (guintptr) hwnd, external_proc);
444 RemoveProp (hwnd, EXTERNAL_PROC_PROP_NAME);
445 RemoveProp (hwnd, D3D11_WINDOW_PROP_NAME);
447 if (!SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) external_proc))
448 GST_WARNING ("Couldn't restore original window procedure");
452 gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self)
456 HINSTANCE hinstance = GetModuleHandle (NULL);
458 GST_LOG_OBJECT (self, "Attempting to create a win32 window");
460 G_LOCK (create_lock);
461 atom = GetClassInfoEx (hinstance, "GSTD3D11", &wc);
463 GST_LOG_OBJECT (self, "Register internal window class");
464 ZeroMemory (&wc, sizeof (WNDCLASSEX));
466 wc.cbSize = sizeof (WNDCLASSEX);
467 wc.lpfnWndProc = window_proc;
468 wc.hInstance = hinstance;
469 wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
470 wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
471 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
472 wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
473 wc.lpszClassName = "GSTD3D11";
475 atom = RegisterClassEx (&wc);
478 G_UNLOCK (create_lock);
479 GST_ERROR_OBJECT (self, "Failed to register window class 0x%x",
480 (unsigned int) GetLastError ());
484 GST_LOG_OBJECT (self, "window class was already registered");
487 self->device_handle = 0;
488 self->internal_hwnd = 0;
489 self->visible = FALSE;
491 self->internal_hwnd = CreateWindowEx (0,
493 "Direct3D11 renderer",
494 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
495 CW_USEDEFAULT, CW_USEDEFAULT,
496 0, 0, (HWND) NULL, (HMENU) NULL, hinstance, self);
498 G_UNLOCK (create_lock);
500 if (!self->internal_hwnd) {
501 GST_ERROR_OBJECT (self, "Failed to create d3d11 window");
505 GST_DEBUG_OBJECT (self, "d3d11 window created: %" G_GUINTPTR_FORMAT,
506 (guintptr) self->internal_hwnd);
508 /* device_handle is set in the window_proc */
509 if (!self->device_handle) {
510 GST_ERROR_OBJECT (self, "device handle is not available");
514 GST_LOG_OBJECT (self,
515 "Created a internal d3d11 window %p", self->internal_hwnd);
517 self->internal_hwnd_thread = g_thread_self ();
522 /* always called from window thread */
524 gst_d3d11_window_win32_change_fullscreen_mode_internal (GstD3D11WindowWin32 *
527 GstD3D11Window *window = GST_D3D11_WINDOW (self);
528 HWND hwnd = self->external_hwnd ? self->external_hwnd : self->internal_hwnd;
530 if (!window->swap_chain)
533 if (window->requested_fullscreen == window->fullscreen)
536 GST_DEBUG_OBJECT (self, "Change mode to %s",
537 window->requested_fullscreen ? "fullscreen" : "windowed");
539 window->fullscreen = !window->fullscreen;
541 if (!window->fullscreen) {
542 /* Restore the window's attributes and size */
543 SetWindowLong (hwnd, GWL_STYLE, self->restore_style);
545 SetWindowPos (hwnd, HWND_NOTOPMOST,
546 self->restore_rect.left,
547 self->restore_rect.top,
548 self->restore_rect.right - self->restore_rect.left,
549 self->restore_rect.bottom - self->restore_rect.top,
550 SWP_FRAMECHANGED | SWP_NOACTIVATE);
552 ShowWindow (hwnd, SW_NORMAL);
554 ComPtr < IDXGIOutput > output;
555 DXGI_OUTPUT_DESC output_desc;
556 IDXGISwapChain *swap_chain = window->swap_chain;
558 /* show window before change style */
559 ShowWindow (hwnd, SW_SHOW);
561 /* Save the old window rect so we can restore it when exiting
563 GetWindowRect (hwnd, &self->restore_rect);
564 self->restore_style = GetWindowLong (hwnd, GWL_STYLE);
566 /* Make the window borderless so that the client area can fill the screen */
567 SetWindowLong (hwnd, GWL_STYLE,
568 self->restore_style &
569 ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
572 swap_chain->GetContainingOutput (&output);
573 output->GetDesc (&output_desc);
575 SetWindowPos (hwnd, HWND_TOPMOST,
576 output_desc.DesktopCoordinates.left,
577 output_desc.DesktopCoordinates.top,
578 output_desc.DesktopCoordinates.right,
579 output_desc.DesktopCoordinates.bottom,
580 SWP_FRAMECHANGED | SWP_NOACTIVATE);
582 ShowWindow (hwnd, SW_MAXIMIZE);
585 GST_DEBUG_OBJECT (self, "Fullscreen mode change done");
589 gst_d3d11_window_win32_on_key_event (GstD3D11WindowWin32 * self,
590 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
592 GstD3D11Window *window = GST_D3D11_WINDOW (self);
593 gunichar2 wcrep[128];
596 if (!window->enable_navigation_events)
599 if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
600 gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
602 if (uMsg == WM_KEYDOWN)
605 event = "key-release";
607 gst_d3d11_window_on_key_event (window, event, utfrep);
614 gst_d3d11_window_win32_on_mouse_event (GstD3D11WindowWin32 * self,
615 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
617 GstD3D11Window *window = GST_D3D11_WINDOW (self);
619 const gchar *event = NULL;
621 if (!window->enable_navigation_events)
624 /* FIXME: convert to render coordinate */
628 event = "mouse-move";
632 event = "mouse-button-press";
636 event = "mouse-button-release";
640 event = "mouse-button-press";
644 event = "mouse-button-release";
648 event = "mouse-button-press";
652 event = "mouse-button-release";
659 gst_d3d11_window_on_mouse_event (window,
660 event, button, (gdouble) LOWORD (lParam), (gdouble) HIWORD (lParam));
664 gst_d3d11_window_win32_handle_window_proc (GstD3D11WindowWin32 * self,
665 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
667 GstD3D11Window *window = GST_D3D11_WINDOW (self);
671 gst_d3d11_window_win32_on_resize (window, 0, 0);
674 if (self->internal_hwnd) {
675 RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
676 ShowWindow (self->internal_hwnd, SW_HIDE);
677 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
678 self->internal_hwnd = NULL;
679 self->internal_hwnd_thread = NULL;
684 gst_d3d11_window_win32_on_key_event (self, hWnd, uMsg, wParam, lParam);
693 /* To handle mouse event only once, do this only for internal window */
694 if (self->internal_hwnd && self->internal_hwnd == hWnd)
695 gst_d3d11_window_win32_on_mouse_event (self, hWnd, uMsg, wParam,
698 /* DefWindowProc will not chain up mouse event to parent window */
699 if (self->external_hwnd && self->external_hwnd != hWnd)
700 SendMessage (self->external_hwnd, uMsg, wParam, lParam);
703 if ((window->fullscreen_toggle_mode &
704 GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER)
705 == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) {
706 WORD state = GetKeyState (VK_RETURN);
707 BYTE high = HIBYTE (state);
710 window->requested_fullscreen = !window->fullscreen;
711 gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
715 case WM_GST_D3D11_FULLSCREEN:
716 if (g_atomic_int_get (&self->pending_fullscreen_count)) {
717 g_atomic_int_dec_and_test (&self->pending_fullscreen_count);
718 if ((window->fullscreen_toggle_mode &
719 GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
720 == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
721 gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
724 case WM_GST_D3D11_MOVE_WINDOW:
725 if (g_atomic_int_get (&self->pending_move_window)) {
726 g_atomic_int_set (&self->pending_move_window, 0);
728 if (self->internal_hwnd && self->external_hwnd) {
729 if (self->render_rect.w < 0 || self->render_rect.h < 0) {
732 /* Reset render rect and back to full-size window */
733 if (GetClientRect (self->external_hwnd, &rect)) {
734 MoveWindow (self->internal_hwnd, 0, 0,
735 rect.right - rect.left, rect.bottom - rect.top, FALSE);
738 MoveWindow (self->internal_hwnd, self->render_rect.x,
739 self->render_rect.y, self->render_rect.w, self->render_rect.h,
752 static LRESULT CALLBACK
753 window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
755 GstD3D11WindowWin32 *self;
757 if (uMsg == WM_CREATE) {
758 self = GST_D3D11_WINDOW_WIN32 (((LPCREATESTRUCT) lParam)->lpCreateParams);
760 GST_LOG_OBJECT (self, "WM_CREATE");
762 self->device_handle = GetDC (hWnd);
763 /* Do this, otherwise we hang on exit. We can still use it (due to the
764 * CS_OWNDC flag in the WindowClass) after we have Released.
766 ReleaseDC (hWnd, self->device_handle);
768 SetProp (hWnd, D3D11_WINDOW_PROP_NAME, self);
769 } else if (GetProp (hWnd, D3D11_WINDOW_PROP_NAME)) {
770 HANDLE handle = GetProp (hWnd, D3D11_WINDOW_PROP_NAME);
772 if (!GST_IS_D3D11_WINDOW_WIN32 (handle)) {
773 GST_WARNING ("%p is not d3d11window object", handle);
777 self = GST_D3D11_WINDOW_WIN32 (handle);
779 g_assert (self->internal_hwnd == hWnd);
781 gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
783 } else if (uMsg == WM_GST_D3D11_DESTROY_INTERNAL_WINDOW) {
784 GST_INFO ("Handle destroy window message");
785 gst_d3d11_window_win32_destroy_internal_window (hWnd);
794 return DefWindowProc (hWnd, uMsg, wParam, lParam);
797 static LRESULT FAR PASCAL
798 sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
800 WNDPROC external_window_proc =
801 (WNDPROC) GetProp (hWnd, EXTERNAL_PROC_PROP_NAME);
802 GstD3D11WindowWin32 *self =
803 (GstD3D11WindowWin32 *) GetProp (hWnd, D3D11_WINDOW_PROP_NAME);
805 if (uMsg == WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW) {
806 GstD3D11Window *window = GST_D3D11_WINDOW (self);
809 GST_DEBUG_OBJECT (self, "Create internal window");
811 window->initialized = gst_d3d11_window_win32_create_internal_window (self);
813 SetWindowLongPtr (self->internal_hwnd, GWL_STYLE, WS_CHILD | WS_MAXIMIZE);
814 SetParent (self->internal_hwnd, self->external_hwnd);
816 /* take changes into account: SWP_FRAMECHANGED */
817 GetClientRect (self->external_hwnd, &rect);
818 SetWindowPos (self->internal_hwnd, HWND_TOP, rect.left, rect.top,
819 rect.right, rect.bottom,
820 SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
821 SWP_FRAMECHANGED | SWP_NOACTIVATE);
822 MoveWindow (self->internal_hwnd, rect.left, rect.top, rect.right,
825 /* don't need to be chained up to parent window procedure,
826 * as this is our custom message */
829 if (uMsg == WM_SIZE) {
830 MoveWindow (self->internal_hwnd, 0, 0, LOWORD (lParam), HIWORD (lParam),
832 } else if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) {
833 g_mutex_lock (&self->lock);
834 GST_WARNING_OBJECT (self, "external window is closing");
835 gst_d3d11_window_win32_release_external_handle (self->external_hwnd);
836 self->external_hwnd = NULL;
838 RemoveProp (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
839 ShowWindow (self->internal_hwnd, SW_HIDE);
840 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
841 self->internal_hwnd = NULL;
842 self->internal_hwnd_thread = NULL;
844 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED;
845 g_mutex_unlock (&self->lock);
847 gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
852 return CallWindowProc (external_window_proc, hWnd, uMsg, wParam, lParam);
856 gst_d3d11_window_win32_disable_alt_enter (GstD3D11WindowWin32 * self,
857 GstD3D11Device * device, IDXGISwapChain * swap_chain, HWND hwnd)
859 ComPtr < IDXGIFactory1 > factory;
862 hr = swap_chain->GetParent (IID_PPV_ARGS (&factory));
863 if (!gst_d3d11_result (hr, device) || !factory) {
864 GST_WARNING_OBJECT (self,
865 "Cannot get parent dxgi factory for swapchain %p, hr: 0x%x",
866 swap_chain, (guint) hr);
870 hr = factory->MakeWindowAssociation (hwnd, DXGI_MWA_NO_ALT_ENTER);
871 if (!gst_d3d11_result (hr, device)) {
872 GST_WARNING_OBJECT (self,
873 "MakeWindowAssociation failure, hr: 0x%x", (guint) hr);
877 static IDXGISwapChain *
878 create_swap_chain (GstD3D11WindowWin32 * self, GstD3D11Device * device,
879 DXGI_SWAP_CHAIN_DESC * desc)
882 IDXGISwapChain *swap_chain = NULL;
883 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
884 IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
886 gst_d3d11_device_lock (device);
887 hr = factory->CreateSwapChain (device_handle, desc, &swap_chain);
888 gst_d3d11_device_unlock (device);
890 if (!gst_d3d11_result (hr, device)) {
891 GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
899 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
900 static IDXGISwapChain1 *
901 create_swap_chain_for_hwnd (GstD3D11WindowWin32 * self, GstD3D11Device * device,
902 HWND hwnd, DXGI_SWAP_CHAIN_DESC1 * desc,
903 DXGI_SWAP_CHAIN_FULLSCREEN_DESC * fullscreen_desc, IDXGIOutput * output)
906 IDXGISwapChain1 *swap_chain = NULL;
907 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
908 IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
909 ComPtr < IDXGIFactory2 > factory2;
911 hr = factory->QueryInterface (IID_PPV_ARGS (&factory2));
912 if (!gst_d3d11_result (hr, device)) {
913 GST_WARNING_OBJECT (self, "IDXGIFactory2 interface is unavailable");
917 gst_d3d11_device_lock (device);
918 hr = factory2->CreateSwapChainForHwnd (device_handle, hwnd, desc,
919 fullscreen_desc, output, &swap_chain);
920 gst_d3d11_device_unlock (device);
922 if (!gst_d3d11_result (hr, device)) {
923 GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
933 gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
934 DXGI_FORMAT format, guint width, guint height,
935 guint swapchain_flags, IDXGISwapChain ** swap_chain)
937 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
938 DXGI_SWAP_CHAIN_DESC desc = { 0, };
939 IDXGISwapChain *new_swapchain = NULL;
940 GstD3D11Device *device = window->device;
942 self->have_swapchain1 = FALSE;
944 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
946 DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
949 desc1.Format = format;
950 /* FIXME: add support stereo */
951 desc1.Stereo = FALSE;
952 desc1.SampleDesc.Count = 1;
953 desc1.SampleDesc.Quality = 0;
954 desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
955 desc1.BufferCount = 2;
956 desc1.Scaling = DXGI_SCALING_STRETCH;
958 /* scaling-stretch would break aspect-ratio so we prefer to use scaling-none,
959 * but Windows7 does not support this method */
960 if (gst_d3d11_is_windows_8_or_greater ())
961 desc1.Scaling = DXGI_SCALING_NONE;
962 desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
963 desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
964 desc1.Flags = swapchain_flags;
966 new_swapchain = create_swap_chain_for_hwnd (self, device,
967 self->internal_hwnd, &desc1, NULL, NULL);
969 if (!new_swapchain) {
970 GST_WARNING_OBJECT (self, "Failed to create swapchain1");
972 self->have_swapchain1 = TRUE;
977 if (!new_swapchain) {
978 DXGI_SWAP_EFFECT swap_effect = DXGI_SWAP_EFFECT_DISCARD;
980 if (gst_d3d11_is_windows_8_or_greater ())
981 swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
983 /* we will get client area at on_resize */
984 desc.BufferDesc.Width = 0;
985 desc.BufferDesc.Height = 0;
986 /* don't care refresh rate */
987 desc.BufferDesc.RefreshRate.Numerator = 0;
988 desc.BufferDesc.RefreshRate.Denominator = 1;
989 desc.BufferDesc.Format = format;
990 desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
991 desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
992 desc.SampleDesc.Count = 1;
993 desc.SampleDesc.Quality = 0;
994 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
995 desc.BufferCount = 2;
996 desc.SwapEffect = swap_effect;
997 desc.OutputWindow = self->internal_hwnd;
998 desc.Windowed = TRUE;
999 desc.Flags = swapchain_flags;
1001 new_swapchain = create_swap_chain (self, device, &desc);
1004 if (!new_swapchain) {
1005 GST_ERROR_OBJECT (self, "Cannot create swapchain");
1009 /* disable alt+enter here. It should be manually handled */
1010 gst_d3d11_device_lock (device);
1011 gst_d3d11_window_win32_disable_alt_enter (self,
1012 device, new_swapchain, desc.OutputWindow);
1013 gst_d3d11_device_unlock (device);
1015 *swap_chain = new_swapchain;
1021 gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
1024 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE;
1026 self->external_hwnd = (HWND) handle;
1027 gst_d3d11_window_win32_set_external_handle (self);
1029 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED;
1033 gst_d3d11_window_win32_show (GstD3D11Window * window)
1035 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1038 width = GST_VIDEO_INFO_WIDTH (&window->render_info);
1039 height = GST_VIDEO_INFO_HEIGHT (&window->render_info);
1041 if (!self->visible) {
1042 /* if no parent the real size has to be set now because this has not been done
1043 * when at window creation */
1044 if (!self->external_hwnd) {
1046 GetClientRect (self->internal_hwnd, &rect);
1047 width += 2 * GetSystemMetrics (SM_CXSIZEFRAME);
1049 2 * GetSystemMetrics (SM_CYSIZEFRAME) +
1050 GetSystemMetrics (SM_CYCAPTION);
1051 MoveWindow (self->internal_hwnd, rect.left, rect.top, width,
1055 ShowWindow (self->internal_hwnd, SW_SHOW);
1056 self->visible = TRUE;
1060 static GstFlowReturn
1061 gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags)
1063 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1066 if ((!self->external_hwnd &&
1067 self->overlay_state == GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED)
1068 || !self->internal_hwnd) {
1069 GST_ERROR_OBJECT (self, "Output window was closed");
1071 return GST_D3D11_WINDOW_FLOW_CLOSED;
1073 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
1074 if (self->have_swapchain1) {
1075 IDXGISwapChain1 *swap_chain1 = (IDXGISwapChain1 *) window->swap_chain;
1076 DXGI_PRESENT_PARAMETERS present_params = { 0, };
1078 /* the first present should not specify dirty-rect */
1079 if (!window->first_present) {
1080 present_params.DirtyRectsCount = 1;
1081 present_params.pDirtyRects = &window->render_rect;
1084 hr = swap_chain1->Present1 (0, present_flags, &present_params);
1088 hr = window->swap_chain->Present (0, present_flags);
1091 if (!gst_d3d11_result (hr, window->device)) {
1092 GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
1100 gst_d3d11_window_win32_on_resize (GstD3D11Window * window,
1101 guint width, guint height)
1103 /* Set zero width and height here. dxgi will decide client area by itself */
1104 GST_D3D11_WINDOW_CLASS (parent_class)->on_resize (window, 0, 0);
1108 gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window)
1110 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1112 if (self->internal_hwnd)
1113 PostMessage (self->internal_hwnd, WM_SIZE, 0, 0);
1119 gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window)
1121 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1123 if (self->internal_hwnd) {
1124 g_atomic_int_add (&self->pending_fullscreen_count, 1);
1125 PostMessage (self->internal_hwnd, WM_GST_D3D11_FULLSCREEN, 0, 0);
1130 gst_d3d11_window_win32_new (GstD3D11Device * device, guintptr handle)
1132 GstD3D11Window *window;
1134 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1136 window = (GstD3D11Window *) g_object_new (GST_TYPE_D3D11_WINDOW_WIN32,
1137 "d3d11device", device, "window-handle", handle, NULL);
1138 if (!window->initialized) {
1139 gst_object_unref (window);
1143 g_object_ref_sink (window);