diff --git a/subprojects/gst-plugins-bad/tests/check/elements/d3d11videosink.c b/subprojects/gst-plugins-bad/tests/check/elements/d3d11videosink.c new file mode 100644 index 0000000000..de51a5491a --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/check/elements/d3d11videosink.c @@ -0,0 +1,240 @@ +/* GStreamer + * + * Unit test for d3d11videosink element + * Copyright (C) 2023 Alexander Slobodeniuk + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#define WM_FIXTURE_RESTART (WM_USER + 123) +#define WM_FIXTURE_TRICKY_RESTART (WM_FIXTURE_RESTART + 1) + +static struct +{ + HWND hwnd; + GstElement *pipeline; + gboolean ever_started; + volatile gboolean stopping; +} fixture; + +static void +fixture_sleep_approx_ms (guint ms) +{ + Sleep (g_random_int_range (0, ms)); +} + +static gpointer +fixture_restart_playback (gpointer data) +{ + GST_DEBUG ("Restarting the pipeline"); + gst_element_set_state (fixture.pipeline, GST_STATE_NULL); + GST_DEBUG ("Stopped"); + gst_element_set_state (fixture.pipeline, GST_STATE_PLAYING); + GST_DEBUG ("Started"); + return NULL; +} + + +static LRESULT CALLBACK +test_win32window_window_proc (HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) +{ + switch (message) { + case WM_FIXTURE_RESTART: + fixture_restart_playback (NULL); + break; + case WM_FIXTURE_TRICKY_RESTART: + /* Synchronous stop, but the sink will have to + * release it's internal window asynchronously */ + g_thread_join (g_thread_new (NULL, fixture_restart_playback, NULL)); + break; + case WM_DESTROY: + PostQuitMessage (0); + break; + default: + /* Simulate application load */ + fixture_sleep_approx_ms (30); + return DefWindowProc (hwnd, message, wParam, lParam); + } + return 0; +} + +static void +test_win32window_create_window (void) +{ + HINSTANCE hinstance = GetModuleHandleA (NULL); + WNDCLASSEXA wc; + ATOM atom = 0; + const gchar *class_name = "d3d11videosink_test"; + + GST_DEBUG ("Creating a win32 window"); + atom = GetClassInfoExA (hinstance, class_name, &wc); + if (atom == 0) { + ZeroMemory (&wc, sizeof (WNDCLASSEXA)); + wc.cbSize = sizeof (WNDCLASSEX); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = test_win32window_window_proc; + wc.hInstance = hinstance; + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wc.lpszClassName = class_name; + + fail_unless (RegisterClassExA (&wc)); + } + + fixture.hwnd = + CreateWindowA (class_name, "d3d11videosink test", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hinstance, NULL); + fail_unless (fixture.hwnd); +} + +static void +test_win32window_msg_loop (void) +{ + MSG msg; + while (GetMessage (&msg, NULL, 0, 0)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } +} + +static gpointer +test_win32window_resize_thr2 (gpointer data) +{ + gint16 length = 0; + while (!fixture.stopping) { + if ((length += 1) > 164) { + length = 0; + } + + MoveWindow (fixture.hwnd, 0, 0, length, length, 1); + fixture_sleep_approx_ms (10); + } + return NULL; +} + +static gpointer +test_win32window_resize_thr1 (gpointer data) +{ + gint i; + const gint NUM_REPETITIONS = 150; + + GThread *thr = g_thread_new (NULL, + test_win32window_resize_thr2, NULL); + + for (i = 0; i < NUM_REPETITIONS; i++) { + gst_element_set_state (fixture.pipeline, GST_STATE_PLAYING); + /* Pause in playback */ + fixture_sleep_approx_ms (500); + + + if (0 == i % 4) { + GST_DEBUG ("Scheduling pipeline restart from the window thread"); + PostMessageA (fixture.hwnd, WM_FIXTURE_RESTART, 0, 0); + fixture_sleep_approx_ms (500); + } + + if (0 == i % 5) { + GST_DEBUG ("Scheduling pipeline restart blocking the window thread"); + PostMessageA (fixture.hwnd, WM_FIXTURE_TRICKY_RESTART, 0, 0); + fixture_sleep_approx_ms (500); + } + + gst_element_set_state (fixture.pipeline, GST_STATE_NULL); + /* Pause without playback */ + fixture_sleep_approx_ms (100); + } + + fixture.stopping = TRUE; + g_thread_join (thr); + + GST_INFO ("Closing the window"); + PostMessageA (fixture.hwnd, WM_CLOSE, 0, 0); + return NULL; +} + +static GstBusSyncReply +test_win32window_bus_sync_handle (GstBus * bus, + GstMessage * message, gpointer data) +{ + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT || + !gst_is_video_overlay_prepare_window_handle_message (message)) { + return GST_BUS_PASS; + } + + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY + (GST_MESSAGE_SRC (message)), (guintptr) fixture.hwnd); + fixture.ever_started = TRUE; + return GST_BUS_DROP; +} + +GST_START_TEST (test_win32window_resize) +{ + GstBus *bus; + GThread *thr; + + GST_INFO ("Start resising test"); + test_win32window_create_window (); + + fixture.pipeline = + gst_parse_launch + ("videotestsrc ! video/x-raw, width=128, height=128 ! d3d11videosink sync=false", + NULL); + bus = gst_element_get_bus (fixture.pipeline); + gst_bus_set_sync_handler (bus, test_win32window_bus_sync_handle, NULL, NULL); + gst_object_unref (bus); + bus = NULL; + + ShowWindow (fixture.hwnd, SW_SHOW); + UpdateWindow (fixture.hwnd); + + thr = g_thread_new (NULL, test_win32window_resize_thr1, NULL); + + test_win32window_msg_loop (); + + g_thread_join (thr); + gst_object_unref (fixture.pipeline); + fixture.hwnd = NULL; + + fail_unless (fixture.ever_started, "videosink didn't request window handle," + "probably it couldn't start"); + GST_INFO ("resising test ok"); +} + +GST_END_TEST; + +static Suite * +d3d11videosink_suite (void) +{ + Suite *s = suite_create ("d3d11videosink"); + TCase *tc_basic = tcase_create ("general"); + + suite_add_tcase (s, tc_basic); + tcase_add_test (tc_basic, test_win32window_resize); + + return s; +} + +GST_CHECK_MAIN (d3d11videosink); diff --git a/subprojects/gst-plugins-bad/tests/check/meson.build b/subprojects/gst-plugins-bad/tests/check/meson.build index 78d79d905f..0708074286 100644 --- a/subprojects/gst-plugins-bad/tests/check/meson.build +++ b/subprojects/gst-plugins-bad/tests/check/meson.build @@ -35,6 +35,7 @@ base_tests = [ [['elements/cudaconvert.c'], false, [gstgl_dep, gmodule_dep]], [['elements/cudafilter.c'], false, [gstgl_dep, gmodule_dep]], [['elements/d3d11colorconvert.c'], host_machine.system() != 'windows', ], + [['elements/d3d11videosink.c'], host_machine.system() != 'windows', ], [['elements/fdkaac.c'], not fdkaac_dep.found(), ], [['elements/gdpdepay.c'], get_option('gdp').disabled()], [['elements/gdppay.c'], get_option('gdp').disabled()],