3 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
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.
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.
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.
26 #include <gst/video/videooverlay.h>
27 #include <gst/video/gstvideosink.h>
31 static GMainLoop *loop = NULL;
32 static gboolean visible = FALSE;
33 static gboolean test_reuse = FALSE;
34 static HWND hwnd = NULL;
35 static gboolean test_fullscreen = FALSE;
36 static gboolean fullscreen = FALSE;
37 static LONG prev_style = 0;
38 static RECT prev_rect = { 0, };
40 #define DEFAULT_VIDEO_SINK "glimagesink"
43 get_monitor_size (RECT * rect)
45 HMONITOR monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONEAREST);
46 MONITORINFOEX monitor_info;
49 monitor_info.cbSize = sizeof (monitor_info);
50 if (!GetMonitorInfo (monitor, (LPMONITORINFO) & monitor_info)) {
54 dev_mode.dmSize = sizeof (dev_mode);
55 dev_mode.dmDriverExtra = sizeof (POINTL);
56 dev_mode.dmFields = DM_POSITION;
57 if (!EnumDisplaySettings
58 (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
62 SetRect (rect, 0, 0, dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
68 switch_fullscreen_mode (void)
73 fullscreen = !fullscreen;
75 gst_print ("Full screen %s\n", fullscreen ? "on" : "off");
78 /* Restore the window's attributes and size */
79 SetWindowLong (hwnd, GWL_STYLE, prev_style);
81 SetWindowPos (hwnd, HWND_NOTOPMOST,
84 prev_rect.right - prev_rect.left,
85 prev_rect.bottom - prev_rect.top, SWP_FRAMECHANGED | SWP_NOACTIVATE);
87 ShowWindow (hwnd, SW_NORMAL);
91 /* show window before change style */
92 ShowWindow (hwnd, SW_SHOW);
94 /* Save the old window rect so we can restore it when exiting
96 GetWindowRect (hwnd, &prev_rect);
97 prev_style = GetWindowLong (hwnd, GWL_STYLE);
99 if (!get_monitor_size (&fullscreen_rect)) {
100 g_warning ("Couldn't get monitor size");
102 fullscreen = !fullscreen;
106 /* Make the window borderless so that the client area can fill the screen */
107 SetWindowLong (hwnd, GWL_STYLE,
109 ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
112 SetWindowPos (hwnd, HWND_NOTOPMOST,
113 fullscreen_rect.left,
115 fullscreen_rect.right,
116 fullscreen_rect.bottom, SWP_FRAMECHANGED | SWP_NOACTIVATE);
118 ShowWindow (hwnd, SW_MAXIMIZE);
122 static LRESULT CALLBACK
123 window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
130 g_main_loop_quit (loop);
134 if (!test_fullscreen)
137 if (wParam == VK_SPACE)
138 switch_fullscreen_mode ();
141 if (!test_fullscreen)
144 switch_fullscreen_mode ();
150 return DefWindowProc (hWnd, message, wParam, lParam);
154 bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
156 GstElement *pipeline = GST_ELEMENT (user_data);
157 switch (GST_MESSAGE_TYPE (msg)) {
158 case GST_MESSAGE_ASYNC_DONE:
159 /* make window visible when we have something to show */
160 if (!visible && hwnd) {
161 ShowWindow (hwnd, SW_SHOW);
165 gst_element_set_state (pipeline, GST_STATE_PLAYING);
167 case GST_MESSAGE_ERROR:{
171 gst_message_parse_error (msg, &err, &dbg);
172 g_printerr ("ERROR %s \n", err->message);
174 g_printerr ("ERROR debug information: %s\n", dbg);
175 g_clear_error (&err);
179 g_main_loop_quit (loop);
190 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
194 if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
195 return G_SOURCE_CONTINUE;
197 TranslateMessage (&msg);
198 DispatchMessage (&msg);
200 return G_SOURCE_CONTINUE;
204 timeout_cb (gpointer user_data)
206 g_main_loop_quit ((GMainLoop *) user_data);
208 return G_SOURCE_REMOVE;
212 main (gint argc, gchar ** argv)
214 GstElement *pipeline, *src, *sink;
215 GstStateChangeReturn sret;
216 WNDCLASSEX wc = { 0, };
217 HINSTANCE hinstance = GetModuleHandle (NULL);
218 GIOChannel *msg_io_channel;
219 GOptionContext *option_ctx;
220 GError *error = NULL;
221 gchar *video_sink = NULL;
223 RECT wr = { 0, 0, 320, 240 };
226 GOptionEntry options[] = {
227 {"videosink", 0, 0, G_OPTION_ARG_STRING, &video_sink,
228 "Video sink to use (default is glimagesink)", NULL}
230 {"repeat", 0, 0, G_OPTION_ARG_NONE, &test_reuse,
231 "Test reuse video sink element", NULL}
233 {"fullscreen", 0, 0, G_OPTION_ARG_NONE, &test_fullscreen,
234 "Test full screen (borderless topmost) mode switching via "
235 "\"SPACE\" key or \"right mouse button\" click", NULL}
241 option_ctx = g_option_context_new ("WIN32 video overlay example");
242 g_option_context_add_main_entries (option_ctx, options, NULL);
243 g_option_context_add_group (option_ctx, gst_init_get_option_group ());
244 ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
245 g_option_context_free (option_ctx);
248 g_printerr ("option parsing failed: %s\n", error->message);
249 g_clear_error (&error);
254 wc.cbSize = sizeof (WNDCLASSEX);
255 wc.style = CS_HREDRAW | CS_VREDRAW;
256 wc.lpfnWndProc = (WNDPROC) window_proc;
257 wc.hInstance = hinstance;
258 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
259 wc.lpszClassName = "GstWIN32VideoOverlay";
260 RegisterClassEx (&wc);
263 video_sink = g_strdup (DEFAULT_VIDEO_SINK);
265 title = g_strdup_printf ("%s - Win32-VideoOverlay", video_sink);
267 AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
268 hwnd = CreateWindowEx (0, wc.lpszClassName,
270 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
271 CW_USEDEFAULT, CW_USEDEFAULT,
272 wr.right - wr.left, wr.bottom - wr.top, (HWND) NULL, (HMENU) NULL,
275 loop = g_main_loop_new (NULL, FALSE);
276 msg_io_channel = g_io_channel_win32_new_messages (0);
277 g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
279 /* prepare the pipeline */
280 pipeline = gst_pipeline_new ("win32-overlay");
281 src = gst_element_factory_make ("videotestsrc", NULL);
282 sink = gst_element_factory_make (video_sink, NULL);
285 g_printerr ("%s element is not available\n", video_sink);
291 gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
292 gst_element_link (src, sink);
294 gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_msg, pipeline);
297 gst_print ("Running loop %d\n", num_repeat++);
299 gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink),
302 sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
303 if (sret == GST_STATE_CHANGE_FAILURE) {
304 g_printerr ("Pipeline doesn't want to pause\n");
307 /* add timer to repeat and reuse pipeline */
309 GSource *timeout_source = g_timeout_source_new_seconds (3);
311 g_source_set_callback (timeout_source,
312 (GSourceFunc) timeout_cb, loop, NULL);
313 g_source_attach (timeout_source, NULL);
314 g_source_unref (timeout_source);
317 g_main_loop_run (loop);
319 gst_element_set_state (pipeline, GST_STATE_NULL);
322 } while (test_reuse);
324 gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
328 DestroyWindow (hwnd);
330 gst_object_unref (pipeline);
331 g_io_channel_unref (msg_io_channel);
332 g_main_loop_unref (loop);