examples: win32-videooverlay: Add test option for threading scenario

Add an option to test the case where window thread and pipeline handling
thread are different. Mainly to test the HWND leak fixed by
https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2302

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/1188>
This commit is contained in:
Seungha Yang 2021-06-03 19:15:22 +09:00
parent 32170aa6c2
commit f886766a6b

View file

@ -29,11 +29,15 @@
#include <string.h> #include <string.h>
static GMainLoop *loop = NULL; static GMainLoop *loop = NULL;
static GMainLoop *pipeline_loop = NULL;
static gboolean visible = FALSE; static gboolean visible = FALSE;
static gboolean test_reuse = FALSE; static gboolean test_reuse = FALSE;
static HWND hwnd = NULL; static HWND hwnd = NULL;
static gboolean test_fullscreen = FALSE; static gboolean test_fullscreen = FALSE;
static gboolean fullscreen = FALSE; static gboolean fullscreen = FALSE;
static gchar *video_sink = NULL;
static gboolean run_thread = FALSE;
static LONG prev_style = 0; static LONG prev_style = 0;
static RECT prev_rect = { 0, }; static RECT prev_rect = { 0, };
@ -126,9 +130,12 @@ window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_DESTROY: case WM_DESTROY:
hwnd = NULL; hwnd = NULL;
if (loop) { if (loop)
g_main_loop_quit (loop); g_main_loop_quit (loop);
}
if (pipeline_loop)
g_main_loop_quit (pipeline_loop);
return 0; return 0;
case WM_KEYUP: case WM_KEYUP:
if (!test_fullscreen) if (!test_fullscreen)
@ -208,21 +215,97 @@ timeout_cb (gpointer user_data)
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
gint static gpointer
main (gint argc, gchar ** argv) pipeline_runner_func (gpointer user_data)
{ {
GstElement *pipeline, *src, *sink; GstElement *pipeline, *src, *sink;
GstStateChangeReturn sret; GstStateChangeReturn sret;
gint num_repeat = 0;
GMainContext *context = NULL;
GMainLoop *this_loop;
if (run_thread) {
/* We are in runner thread, create our loop */
context = g_main_context_new ();
pipeline_loop = g_main_loop_new (context, FALSE);
g_main_context_push_thread_default (context);
this_loop = pipeline_loop;
} else {
this_loop = loop;
}
/* prepare the pipeline */
pipeline = gst_pipeline_new ("win32-overlay");
src = gst_element_factory_make ("videotestsrc", NULL);
sink = gst_element_factory_make (video_sink, NULL);
if (!sink) {
g_printerr ("%s element is not available\n", video_sink);
exit (1);
}
gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
gst_element_link (src, sink);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_msg, pipeline);
do {
gst_print ("Running loop %d\n", num_repeat++);
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink),
(guintptr) hwnd);
sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
if (sret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Pipeline doesn't want to pause\n");
break;
} else {
/* add timer to repeat and reuse pipeline */
if (test_reuse) {
GSource *timeout_source = g_timeout_source_new_seconds (3);
g_source_set_callback (timeout_source,
(GSourceFunc) timeout_cb, this_loop, NULL);
g_source_attach (timeout_source, NULL);
g_source_unref (timeout_source);
}
g_main_loop_run (this_loop);
}
gst_element_set_state (pipeline, GST_STATE_NULL);
visible = FALSE;
} while (test_reuse);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline);
if (run_thread) {
g_main_context_pop_thread_default (context);
g_main_context_unref (context);
g_main_loop_quit (loop);
g_main_loop_unref (pipeline_loop);
}
return NULL;
}
gint
main (gint argc, gchar ** argv)
{
WNDCLASSEX wc = { 0, }; WNDCLASSEX wc = { 0, };
HINSTANCE hinstance = GetModuleHandle (NULL); HINSTANCE hinstance = GetModuleHandle (NULL);
GIOChannel *msg_io_channel; GIOChannel *msg_io_channel;
GOptionContext *option_ctx; GOptionContext *option_ctx;
GError *error = NULL; GError *error = NULL;
gchar *video_sink = NULL;
gchar *title = NULL; gchar *title = NULL;
RECT wr = { 0, 0, 320, 240 }; RECT wr = { 0, 0, 320, 240 };
gint exitcode = 0; gint exitcode = 0;
gboolean ret; gboolean ret;
GThread *thread = NULL;
GOptionEntry options[] = { GOptionEntry options[] = {
{"videosink", 0, 0, G_OPTION_ARG_STRING, &video_sink, {"videosink", 0, 0, G_OPTION_ARG_STRING, &video_sink,
"Video sink to use (default is glimagesink)", NULL} "Video sink to use (default is glimagesink)", NULL}
@ -234,9 +317,11 @@ main (gint argc, gchar ** argv)
"Test full screen (borderless topmost) mode switching via " "Test full screen (borderless topmost) mode switching via "
"\"SPACE\" key or \"right mouse button\" click", NULL} "\"SPACE\" key or \"right mouse button\" click", NULL}
, ,
{"run-thread", 0, 0, G_OPTION_ARG_NONE, &run_thread,
"Run pipeline from non-window thread", NULL}
,
{NULL} {NULL}
}; };
gint num_repeat = 0;
option_ctx = g_option_context_new ("WIN32 video overlay example"); option_ctx = g_option_context_new ("WIN32 video overlay example");
g_option_context_add_main_entries (option_ctx, options, NULL); g_option_context_add_main_entries (option_ctx, options, NULL);
@ -276,58 +361,18 @@ main (gint argc, gchar ** argv)
msg_io_channel = g_io_channel_win32_new_messages (0); msg_io_channel = g_io_channel_win32_new_messages (0);
g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL); g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
/* prepare the pipeline */ if (run_thread) {
pipeline = gst_pipeline_new ("win32-overlay"); thread = g_thread_new ("pipeline-thread",
src = gst_element_factory_make ("videotestsrc", NULL); (GThreadFunc) pipeline_runner_func, NULL);
sink = gst_element_factory_make (video_sink, NULL); g_main_loop_run (loop);
} else {
if (!sink) { pipeline_runner_func (NULL);
g_printerr ("%s element is not available\n", video_sink);
exitcode = 1;
goto terminate;
} }
gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
gst_element_link (src, sink);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_msg, pipeline);
do {
gst_print ("Running loop %d\n", num_repeat++);
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink),
(guintptr) hwnd);
sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
if (sret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Pipeline doesn't want to pause\n");
break;
} else {
/* add timer to repeat and reuse pipeline */
if (test_reuse) {
GSource *timeout_source = g_timeout_source_new_seconds (3);
g_source_set_callback (timeout_source,
(GSourceFunc) timeout_cb, loop, NULL);
g_source_attach (timeout_source, NULL);
g_source_unref (timeout_source);
}
g_main_loop_run (loop);
}
gst_element_set_state (pipeline, GST_STATE_NULL);
visible = FALSE;
} while (test_reuse);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
terminate: terminate:
if (hwnd) if (hwnd)
DestroyWindow (hwnd); DestroyWindow (hwnd);
gst_object_unref (pipeline);
g_io_channel_unref (msg_io_channel); g_io_channel_unref (msg_io_channel);
g_main_loop_unref (loop); g_main_loop_unref (loop);
g_free (title); g_free (title);