mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
d3d12videosink: Add support for fullscreen mode
Adding "fullscreen-on-alt-enter" and "fullscreen" properties so that use can control fullscreen mode switch Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5891>
This commit is contained in:
parent
00bbac6541
commit
de331217aa
3 changed files with 260 additions and 12 deletions
|
@ -35,12 +35,16 @@ enum
|
|||
PROP_FORCE_ASPECT_RATIO,
|
||||
PROP_ENABLE_NAVIGATION_EVENTS,
|
||||
PROP_ROTATE_METHOD,
|
||||
PROP_FULLSCREEN_ON_ALT_ENTER,
|
||||
PROP_FULLSCREEN,
|
||||
};
|
||||
|
||||
#define DEFAULT_ADAPTER -1
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
#define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
|
||||
#define DEFAULT_ROTATE_METHOD GST_VIDEO_ORIENTATION_IDENTITY
|
||||
#define DEFAULT_FULLSCREEN_ON_ALT_ENTER FALSE
|
||||
#define DEFAULT_FULLSCREEN FALSE
|
||||
|
||||
static GstStaticPadTemplate sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
|
@ -96,6 +100,8 @@ struct GstD3D12VideoSinkPrivate
|
|||
GstVideoOrientationMethod orientation = DEFAULT_ROTATE_METHOD;
|
||||
GstVideoOrientationMethod orientation_from_tag = DEFAULT_ROTATE_METHOD;
|
||||
GstVideoOrientationMethod orientation_selected = DEFAULT_ROTATE_METHOD;
|
||||
gboolean fullscreen_on_alt_enter = DEFAULT_FULLSCREEN_ON_ALT_ENTER;
|
||||
gboolean fullscreen = DEFAULT_FULLSCREEN;
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
|
@ -138,6 +144,8 @@ static void gst_d3d12_video_sink_key_event (GstD3D12Window * window,
|
|||
static void gst_d3d12_video_sink_mouse_event (GstD3D12Window * window,
|
||||
const gchar * event, gint button, gdouble x, gdouble y,
|
||||
GstD3D12VideoSink * self);
|
||||
static void gst_d3d12_video_sink_on_fullscreen (GstD3D12Window * window,
|
||||
gboolean is_fullscreen, GstD3D12VideoSink * self);
|
||||
|
||||
static void
|
||||
gst_d3d12_video_sink_video_overlay_init (GstVideoOverlayInterface * iface);
|
||||
|
@ -193,6 +201,19 @@ gst_d3d12_video_sink_class_init (GstD3D12VideoSinkClass * klass)
|
|||
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_FULLSCREEN_ON_ALT_ENTER,
|
||||
g_param_spec_boolean ("fullscreen-on-alt-enter",
|
||||
"Fullscreen on Alt Enter",
|
||||
"Enable fullscreen toggle on alt+enter key input",
|
||||
DEFAULT_FULLSCREEN_ON_ALT_ENTER,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_FULLSCREEN,
|
||||
g_param_spec_boolean ("fullscreen", "Fullscreen",
|
||||
"Fullscreen mode", DEFAULT_FULLSCREEN,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
element_class->set_context =
|
||||
GST_DEBUG_FUNCPTR (gst_d3d12_video_sink_set_context);
|
||||
|
||||
|
@ -228,6 +249,8 @@ gst_d3d12_video_sink_init (GstD3D12VideoSink * self)
|
|||
G_CALLBACK (gst_d3d12_video_sink_key_event), self);
|
||||
g_signal_connect (priv->window, "mouse-event",
|
||||
G_CALLBACK (gst_d3d12_video_sink_mouse_event), self);
|
||||
g_signal_connect (priv->window, "fullscreen",
|
||||
G_CALLBACK (gst_d3d12_video_sink_on_fullscreen), self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -267,6 +290,15 @@ gst_d3d12_videosink_set_property (GObject * object, guint prop_id,
|
|||
gst_d3d12_video_sink_set_orientation (self,
|
||||
(GstVideoOrientationMethod) g_value_get_enum (value), FALSE);
|
||||
break;
|
||||
case PROP_FULLSCREEN_ON_ALT_ENTER:
|
||||
priv->fullscreen_on_alt_enter = g_value_get_boolean (value);
|
||||
gst_d3d12_window_enable_fullscreen_on_alt_enter (priv->window,
|
||||
priv->fullscreen_on_alt_enter);
|
||||
break;
|
||||
case PROP_FULLSCREEN:
|
||||
priv->fullscreen = g_value_get_boolean (value);
|
||||
gst_d3d12_window_set_fullscreen (priv->window, priv->fullscreen);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -294,6 +326,12 @@ gst_d3d12_videosink_get_property (GObject * object, guint prop_id,
|
|||
case PROP_ROTATE_METHOD:
|
||||
g_value_set_enum (value, priv->orientation);
|
||||
break;
|
||||
case PROP_FULLSCREEN_ON_ALT_ENTER:
|
||||
g_value_set_boolean (value, priv->fullscreen_on_alt_enter);
|
||||
break;
|
||||
case PROP_FULLSCREEN:
|
||||
g_value_set_boolean (value, priv->fullscreen);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -442,6 +480,25 @@ gst_d3d12_video_sink_mouse_event (GstD3D12Window * window, const gchar * event,
|
|||
gst_navigation_send_event_simple (GST_NAVIGATION (self), mouse_event);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_video_sink_on_fullscreen (GstD3D12Window * window,
|
||||
gboolean is_fullscreen, GstD3D12VideoSink * self)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
gboolean notify = FALSE;
|
||||
|
||||
{
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
if (priv->fullscreen != is_fullscreen) {
|
||||
priv->fullscreen = is_fullscreen;
|
||||
notify = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (notify)
|
||||
g_object_notify (G_OBJECT (self), "fullscreen");
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_d3d12_video_sink_update_window (GstD3D12VideoSink * self)
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_d3d12_window_debug);
|
|||
#define WS_GST_D3D12 (WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW)
|
||||
#define EXTERNAL_PROC_PROP_NAME L"gst-d3d12-hwnd-external-proc"
|
||||
#define D3D12_WINDOW_PROP_NAME L"gst-d3d12-hwnd-obj"
|
||||
#define WM_GST_D3D12_FULLSCREEN (WM_USER + 1)
|
||||
#define WM_GST_D3D12_CONSTRUCT_INTERNAL_WINDOW (WM_USER + 2)
|
||||
#define WM_GST_D3D12_DESTROY_INTERNAL_WINDOW (WM_USER + 3)
|
||||
#define WM_GST_D3D12_UPDATE_RENDER_RECT (WM_USER + 4)
|
||||
|
@ -54,18 +55,12 @@ enum
|
|||
{
|
||||
SIGNAL_KEY_EVENT,
|
||||
SIGNAL_MOUSE_EVENT,
|
||||
SIGNAL_FULLSCREEN,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
static guint d3d12_window_signals[SIGNAL_LAST] = { 0, };
|
||||
|
||||
enum HwndState
|
||||
{
|
||||
HWND_STATE_INIT,
|
||||
HWND_STATE_OPENED,
|
||||
HWND_STATE_CLOSED,
|
||||
};
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
struct SwapBuffer
|
||||
{
|
||||
|
@ -177,6 +172,13 @@ struct GstD3D12WindowPrivate
|
|||
|
||||
GstVideoOrientationMethod orientation = GST_VIDEO_ORIENTATION_IDENTITY;
|
||||
|
||||
/* fullscreen related variables */
|
||||
gboolean fullscreen_on_alt_enter = TRUE;
|
||||
gboolean requested_fullscreen = FALSE;
|
||||
gboolean applied_fullscreen = FALSE;
|
||||
LONG restore_style;
|
||||
WINDOWPLACEMENT restore_placement;
|
||||
|
||||
GstD3D12FenceDataPool *fence_data_pool;
|
||||
|
||||
/* User specified rect */
|
||||
|
@ -249,6 +251,11 @@ gst_d3d12_window_class_init (GstD3D12WindowClass * klass)
|
|||
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
|
||||
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
|
||||
|
||||
d3d12_window_signals[SIGNAL_FULLSCREEN] =
|
||||
g_signal_new ("fullscreen", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, nullptr, nullptr, nullptr,
|
||||
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_d3d12_window_debug,
|
||||
"d3d12window", 0, "d3d12window");
|
||||
}
|
||||
|
@ -417,6 +424,83 @@ gst_d3d12_window_on_mouse_event (GstD3D12Window * self, UINT msg, WPARAM wparam,
|
|||
event, button, final_x, final_y);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_d3d12_window_toggle_fullscreen_mode (GstD3D12Window * self,
|
||||
gboolean emit_signal)
|
||||
{
|
||||
auto priv = self->priv;
|
||||
HWND hwnd = nullptr;
|
||||
gboolean is_fullscreen;
|
||||
ComPtr < IDXGISwapChain > swapchain;
|
||||
|
||||
{
|
||||
std::lock_guard < std::mutex > hlk (priv->hwnd_lock);
|
||||
hwnd = priv->external_hwnd ? priv->external_hwnd : priv->hwnd;
|
||||
|
||||
if (!hwnd)
|
||||
return;
|
||||
|
||||
if (priv->requested_fullscreen == priv->applied_fullscreen)
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
if (priv->ctx)
|
||||
swapchain = priv->ctx->swapchain;
|
||||
}
|
||||
|
||||
if (!swapchain)
|
||||
return;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Change mode to %s",
|
||||
priv->requested_fullscreen ? "fullscreen" : "windowed");
|
||||
|
||||
priv->applied_fullscreen = priv->requested_fullscreen;
|
||||
is_fullscreen = priv->applied_fullscreen;
|
||||
}
|
||||
|
||||
if (!is_fullscreen) {
|
||||
SetWindowLongW (hwnd, GWL_STYLE, priv->restore_style);
|
||||
SetWindowPlacement (hwnd, &priv->restore_placement);
|
||||
} else {
|
||||
ComPtr < IDXGIOutput > output;
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
|
||||
/* remember current placement to restore window later */
|
||||
GetWindowPlacement (hwnd, &priv->restore_placement);
|
||||
|
||||
/* show window before change style */
|
||||
ShowWindow (hwnd, SW_SHOW);
|
||||
|
||||
priv->restore_style = GetWindowLong (hwnd, GWL_STYLE);
|
||||
|
||||
/* Make the window borderless so that the client area can fill the screen */
|
||||
SetWindowLongA (hwnd, GWL_STYLE,
|
||||
priv->restore_style &
|
||||
~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
|
||||
WS_THICKFRAME | WS_MAXIMIZE));
|
||||
|
||||
swapchain->GetContainingOutput (&output);
|
||||
output->GetDesc (&output_desc);
|
||||
|
||||
SetWindowPos (hwnd, HWND_TOP,
|
||||
output_desc.DesktopCoordinates.left,
|
||||
output_desc.DesktopCoordinates.top,
|
||||
output_desc.DesktopCoordinates.right,
|
||||
output_desc.DesktopCoordinates.bottom,
|
||||
SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
|
||||
ShowWindow (hwnd, SW_MAXIMIZE);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Fullscreen mode change done");
|
||||
|
||||
if (emit_signal) {
|
||||
g_signal_emit (self, d3d12_window_signals[SIGNAL_FULLSCREEN],
|
||||
0, is_fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
static GstD3D12Window *
|
||||
gst_d3d12_window_from_hwnd (HWND hwnd)
|
||||
{
|
||||
|
@ -464,6 +548,42 @@ gst_d3d12_window_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_GST_D3D12_FULLSCREEN:
|
||||
{
|
||||
auto self = gst_d3d12_window_from_hwnd (hwnd);
|
||||
if (self) {
|
||||
gst_d3d12_window_toggle_fullscreen_mode (self, FALSE);
|
||||
gst_object_unref (self);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_SYSKEYDOWN:
|
||||
{
|
||||
WORD state = GetKeyState (VK_RETURN);
|
||||
BYTE high = HIBYTE (state);
|
||||
|
||||
if (high & 0x1) {
|
||||
auto self = gst_d3d12_window_from_hwnd (hwnd);
|
||||
if (self) {
|
||||
auto priv = self->priv;
|
||||
bool do_toggle = false;
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (priv->hwnd_lock);
|
||||
if (priv->fullscreen_on_alt_enter) {
|
||||
priv->requested_fullscreen = !priv->applied_fullscreen;
|
||||
do_toggle = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_toggle)
|
||||
gst_d3d12_window_toggle_fullscreen_mode (self, TRUE);
|
||||
|
||||
gst_object_unref (self);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SIZE:
|
||||
{
|
||||
auto self = gst_d3d12_window_from_hwnd (hwnd);
|
||||
|
@ -611,7 +731,7 @@ gst_d3d12_window_create_hwnd (GstD3D12Window * self)
|
|||
|
||||
priv->hwnd = CreateWindowExW (0, L"GstD3D12Hwnd", title.c_str (),
|
||||
style, x, y, w, h, (HWND) nullptr, (HMENU) nullptr, inst, self);
|
||||
|
||||
priv->applied_fullscreen = FALSE;
|
||||
priv->internal_hwnd_thread = g_thread_self ();
|
||||
}
|
||||
|
||||
|
@ -697,6 +817,26 @@ sub_class_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
|||
gst_object_unref (self);
|
||||
return 0;
|
||||
}
|
||||
case WM_SYSKEYDOWN:
|
||||
{
|
||||
WORD state = GetKeyState (VK_RETURN);
|
||||
BYTE high = HIBYTE (state);
|
||||
|
||||
if (high & 0x1) {
|
||||
bool do_toggle = false;
|
||||
{
|
||||
std::lock_guard < std::mutex > lk (priv->hwnd_lock);
|
||||
if (priv->fullscreen_on_alt_enter) {
|
||||
priv->requested_fullscreen = !priv->applied_fullscreen;
|
||||
do_toggle = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_toggle)
|
||||
gst_d3d12_window_toggle_fullscreen_mode (self, TRUE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SIZE:
|
||||
if (priv->render_rect.w > 0 || priv->render_rect.h > 0) {
|
||||
MoveWindow (priv->hwnd,
|
||||
|
@ -834,7 +974,7 @@ gst_d3d12_window_prepare_hwnd (GstD3D12Window * self, guintptr window_handle)
|
|||
priv->hwnd_cond.wait (lk);
|
||||
}
|
||||
|
||||
if (priv->state != HWND_STATE_OPENED) {
|
||||
if (priv->state != GST_D3D12_WINDOW_STATE_OPENED) {
|
||||
if (priv->flushing) {
|
||||
GST_DEBUG_OBJECT (self, "We are flushing");
|
||||
return GST_FLOW_FLUSHING;
|
||||
|
@ -889,10 +1029,12 @@ gst_d3d12_window_unprepare (GstD3D12Window * window)
|
|||
g_main_loop_quit (priv->loop);
|
||||
g_clear_pointer (&priv->main_loop_thread, g_thread_join);
|
||||
|
||||
std::lock_guard < std::mutex > lk (priv->hwnd_lock);
|
||||
priv->hwnd = nullptr;
|
||||
priv->external_hwnd = nullptr;
|
||||
priv->internal_hwnd_thread = nullptr;
|
||||
priv->state = GST_D3D12_WINDOW_STATE_INIT;
|
||||
priv->applied_fullscreen = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -927,7 +1069,8 @@ gst_d3d12_window_on_resize (GstD3D12Window * self)
|
|||
if (!priv->ctx)
|
||||
return GST_FLOW_OK;
|
||||
|
||||
priv->ctx->WaitGpu ();
|
||||
if (priv->ctx->fence_val != 0)
|
||||
priv->ctx->WaitGpu ();
|
||||
priv->ctx->swap_buffers.clear ();
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC desc = { };
|
||||
|
@ -1010,7 +1153,7 @@ gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::lock_guard < std::recursive_mutex > lk (priv->lock);
|
||||
std::unique_lock < std::recursive_mutex > lk (priv->lock);
|
||||
HRESULT hr;
|
||||
|
||||
if (window->device != device) {
|
||||
|
@ -1046,6 +1189,19 @@ gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
|
|||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
ComPtr < IDXGIFactory1 > parent_factory;
|
||||
hr = swapchain->GetParent (IID_PPV_ARGS (&parent_factory));
|
||||
if (!gst_d3d12_result (hr, window->device)) {
|
||||
GST_WARNING_OBJECT (window, "Couldn't get parent factory");
|
||||
} else {
|
||||
hr = parent_factory->MakeWindowAssociation (priv->hwnd,
|
||||
DXGI_MWA_NO_ALT_ENTER);
|
||||
if (!gst_d3d12_result (hr, window->device)) {
|
||||
GST_WARNING_OBJECT (window, "MakeWindowAssociation failed, hr: 0x%x",
|
||||
(guint) hr);
|
||||
}
|
||||
}
|
||||
|
||||
hr = swapchain.As (&ctx->swapchain);
|
||||
if (!gst_d3d12_result (hr, window->device)) {
|
||||
GST_ERROR_OBJECT (window, "IDXGISwapChain4 interface is unavailable");
|
||||
|
@ -1089,7 +1245,17 @@ gst_d3d12_window_prepare (GstD3D12Window * window, GstD3D12Device * device,
|
|||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
return gst_d3d12_window_on_resize (window);
|
||||
ret = gst_d3d12_window_on_resize (window);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
lk.unlock ();
|
||||
|
||||
std::lock_guard < std::mutex > hlk (priv->hwnd_lock);
|
||||
if (priv->requested_fullscreen != priv->applied_fullscreen)
|
||||
PostMessageW (priv->hwnd, WM_GST_D3D12_FULLSCREEN, 0, 0);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
|
@ -1434,3 +1600,22 @@ gst_d3d12_window_get_state (GstD3D12Window * window)
|
|||
|
||||
return priv->state;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_window_enable_fullscreen_on_alt_enter (GstD3D12Window * window,
|
||||
gboolean enable)
|
||||
{
|
||||
auto priv = window->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->hwnd_lock);
|
||||
priv->fullscreen_on_alt_enter = enable;
|
||||
}
|
||||
|
||||
void
|
||||
gst_d3d12_window_set_fullscreen (GstD3D12Window * window, gboolean enable)
|
||||
{
|
||||
auto priv = window->priv;
|
||||
std::lock_guard < std::mutex > lk (priv->hwnd_lock);
|
||||
priv->requested_fullscreen = enable;
|
||||
if (priv->hwnd && priv->applied_fullscreen != priv->requested_fullscreen)
|
||||
PostMessageW (priv->hwnd, WM_GST_D3D12_FULLSCREEN, 0, 0);
|
||||
}
|
||||
|
|
|
@ -77,5 +77,11 @@ void gst_d3d12_window_set_orientation (GstD3D12Window * window,
|
|||
void gst_d3d12_window_set_title (GstD3D12Window * window,
|
||||
const gchar * title);
|
||||
|
||||
void gst_d3d12_window_enable_fullscreen_on_alt_enter (GstD3D12Window * window,
|
||||
gboolean enable);
|
||||
|
||||
void gst_d3d12_window_set_fullscreen (GstD3D12Window * window,
|
||||
gboolean enable);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
Loading…
Reference in a new issue