• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/gst.h>
26 #include <gst/video/video.h>
27 
28 #include <windows.h>
29 #include <string.h>
30 #include "d3d11videosink-kb.h"
31 
32 static GMainLoop *loop = NULL;
33 static gboolean visible = FALSE;
34 static gboolean test_reuse = FALSE;
35 static HWND hwnd = NULL;
36 
37 typedef struct
38 {
39   GstElement *pipeline;
40   GstElement *sink;
41   gboolean fullscreen;
42   gboolean force_aspect_ratio;
43 } CallbackData;
44 
45 static LRESULT CALLBACK
window_proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)46 window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
47 {
48   switch (message) {
49     case WM_DESTROY:
50       hwnd = NULL;
51       g_print ("destroy\n");
52       if (loop) {
53         g_main_loop_quit (loop);
54       }
55       return 0;
56     default:
57       break;
58   }
59 
60   return DefWindowProc (hWnd, message, wParam, lParam);
61 }
62 
63 static void
keyboard_cb(gchar key_input,CallbackData * data)64 keyboard_cb (gchar key_input, CallbackData * data)
65 {
66   gchar key;
67 
68   key = g_ascii_tolower (key_input);
69 
70   switch (key) {
71     case 'q':
72     case 'Q':
73       gst_element_send_event (data->pipeline, gst_event_new_eos ());
74       if (hwnd)
75         PostMessage (hwnd, WM_CLOSE, 0, 0);
76       else
77         g_main_loop_quit (loop);
78       break;
79     case 27:                   /* ESC */
80       gst_element_send_event (data->pipeline, gst_event_new_eos ());
81       if (hwnd)
82         PostMessage (hwnd, WM_CLOSE, 0, 0);
83       else
84         g_main_loop_quit (loop);
85       break;
86     case ' ':
87       data->fullscreen = !data->fullscreen;
88       gst_print ("change to %s mode\n", data->fullscreen ?
89           "fullscreen" : "windowed");
90       g_object_set (data->sink, "fullscreen", data->fullscreen, NULL);
91       break;
92     case 'f':
93       data->force_aspect_ratio = !data->force_aspect_ratio;
94       g_object_set (data->sink,
95           "force-aspect-ratio", data->force_aspect_ratio, NULL);
96       break;
97     default:
98       break;
99   }
100 }
101 
102 static gboolean
bus_msg(GstBus * bus,GstMessage * msg,CallbackData * data)103 bus_msg (GstBus * bus, GstMessage * msg, CallbackData * data)
104 {
105   GstElement *pipeline = data->pipeline;
106   switch (GST_MESSAGE_TYPE (msg)) {
107     case GST_MESSAGE_ASYNC_DONE:
108       /* make window visible when we have something to show */
109       if (!visible && hwnd) {
110         ShowWindow (hwnd, SW_SHOW);
111         visible = TRUE;
112       }
113 
114       gst_element_set_state (pipeline, GST_STATE_PLAYING);
115       break;
116     case GST_MESSAGE_ERROR:{
117       GError *err;
118       gchar *dbg;
119 
120       gst_message_parse_error (msg, &err, &dbg);
121       g_printerr ("ERROR %s \n", err->message);
122       if (dbg != NULL)
123         g_printerr ("ERROR debug information: %s\n", dbg);
124       g_clear_error (&err);
125       g_free (dbg);
126       test_reuse = FALSE;
127 
128       g_main_loop_quit (loop);
129       break;
130     }
131     case GST_MESSAGE_ELEMENT:
132     {
133       GstNavigationMessageType mtype = gst_navigation_message_get_type (msg);
134       if (mtype == GST_NAVIGATION_MESSAGE_EVENT) {
135         GstEvent *ev = NULL;
136 
137         if (gst_navigation_message_parse_event (msg, &ev)) {
138           GstNavigationEventType e_type = gst_navigation_event_get_type (ev);
139           if (e_type == GST_NAVIGATION_EVENT_KEY_PRESS) {
140             const gchar *key;
141 
142             if (gst_navigation_event_parse_key_event (ev, &key)) {
143               if (strcmp (key, "space") == 0 || strcmp (key, "Space") == 0) {
144                 keyboard_cb (' ', data);
145               } else {
146                 keyboard_cb (key[0], data);
147               }
148             }
149           }
150         }
151         if (ev)
152           gst_event_unref (ev);
153       }
154       break;
155     }
156     default:
157       break;
158   }
159 
160   return TRUE;
161 }
162 
163 static gboolean
msg_cb(GIOChannel * source,GIOCondition condition,gpointer data)164 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
165 {
166   MSG msg;
167 
168   if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
169     return G_SOURCE_CONTINUE;
170 
171   TranslateMessage (&msg);
172   DispatchMessage (&msg);
173 
174   return G_SOURCE_CONTINUE;
175 }
176 
177 static gboolean
timeout_cb(gpointer user_data)178 timeout_cb (gpointer user_data)
179 {
180   g_main_loop_quit ((GMainLoop *) user_data);
181 
182   return G_SOURCE_REMOVE;
183 }
184 
185 static void
print_keyboard_help(void)186 print_keyboard_help (void)
187 {
188   static struct
189   {
190     const gchar *key_desc;
191     const gchar *key_help;
192   } key_controls[] = {
193     {
194     "q or ESC", "Quit"}, {
195     "SPACE", "Toggle fullscreen mode"}, {
196     "f", "Toggle force-aspect-ratio"}
197   };
198   guint i, chars_to_pad, desc_len, max_desc_len = 0;
199 
200   gst_print ("\n%s\n", "Keyboard controls:");
201 
202   for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) {
203     desc_len = g_utf8_strlen (key_controls[i].key_desc, -1);
204     max_desc_len = MAX (max_desc_len, desc_len);
205   }
206   ++max_desc_len;
207 
208   for (i = 0; i < G_N_ELEMENTS (key_controls); ++i) {
209     chars_to_pad = max_desc_len - g_utf8_strlen (key_controls[i].key_desc, -1);
210     gst_print ("\t%s", key_controls[i].key_desc);
211     gst_print ("%-*s: ", chars_to_pad, "");
212     gst_print ("%s\n", key_controls[i].key_help);
213   }
214   gst_print ("\n");
215 }
216 
217 gint
main(gint argc,gchar ** argv)218 main (gint argc, gchar ** argv)
219 {
220   GstElement *pipeline, *src, *sink;
221   GstStateChangeReturn sret;
222   WNDCLASSEX wc = { 0, };
223   HINSTANCE hinstance = GetModuleHandle (NULL);
224   GIOChannel *msg_io_channel = NULL;
225   GOptionContext *option_ctx;
226   GError *error = NULL;
227   RECT wr = { 0, 0, 320, 240 };
228   gint exitcode = 0;
229   gboolean ret;
230   gboolean use_overlay = FALSE;
231   gboolean start_fullscreen = FALSE;
232   GOptionEntry options[] = {
233     {"use-overlay", 0, 0, G_OPTION_ARG_NONE, &use_overlay,
234         "Test reuse video sink element", NULL}
235     ,
236     {"repeat", 0, 0, G_OPTION_ARG_NONE, &test_reuse,
237         "Test reuse video sink element", NULL}
238     ,
239     {"start-fullscreen", 0, 0, G_OPTION_ARG_NONE, &start_fullscreen,
240         "Run pipeline in fullscreen mode", NULL}
241     ,
242     {NULL}
243   };
244   gint num_repeat = 0;
245   CallbackData cb_data = { 0, };
246 
247   option_ctx = g_option_context_new ("WIN32 video overlay example");
248   g_option_context_add_main_entries (option_ctx, options, NULL);
249   g_option_context_add_group (option_ctx, gst_init_get_option_group ());
250   ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
251   g_option_context_free (option_ctx);
252 
253   if (!ret) {
254     g_printerr ("option parsing failed: %s\n", error->message);
255     g_clear_error (&error);
256     exit (1);
257   }
258 
259   print_keyboard_help ();
260 
261   loop = g_main_loop_new (NULL, FALSE);
262 
263   if (use_overlay) {
264     /* prepare window */
265     wc.cbSize = sizeof (WNDCLASSEX);
266     wc.style = CS_HREDRAW | CS_VREDRAW;
267     wc.lpfnWndProc = (WNDPROC) window_proc;
268     wc.hInstance = hinstance;
269     wc.hCursor = LoadCursor (NULL, IDC_ARROW);
270     wc.lpszClassName = "GstD3D11VideoSinkExample";
271     RegisterClassEx (&wc);
272 
273     AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
274 
275     hwnd = CreateWindowEx (0, wc.lpszClassName, "GstD3D11VideoSinkExample",
276         WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
277         CW_USEDEFAULT, CW_USEDEFAULT,
278         wr.right - wr.left, wr.bottom - wr.top, (HWND) NULL, (HMENU) NULL,
279         hinstance, NULL);
280 
281     msg_io_channel = g_io_channel_win32_new_messages (0);
282     g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
283   }
284 
285   /* prepare the pipeline */
286   pipeline = gst_pipeline_new ("d3d11videosink-pipeline");
287   src = gst_element_factory_make ("videotestsrc", NULL);
288   sink = gst_element_factory_make ("d3d11videosink", NULL);
289 
290   cb_data.fullscreen = start_fullscreen;
291   cb_data.force_aspect_ratio = TRUE;
292   cb_data.pipeline = pipeline;
293   cb_data.sink = sink;
294 
295   g_object_set (sink, "fullscreen-toggle-mode", 0x2 | 0x4, NULL);
296 
297   gst_bin_add_many (GST_BIN (pipeline), src, sink, NULL);
298   gst_element_link (src, sink);
299 
300   gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg,
301       &cb_data);
302 
303   gst_d3d11_video_sink_kb_set_key_handler ((GstD3D11VideoSinkKbFunc)
304       keyboard_cb, &cb_data);
305 
306   if (start_fullscreen)
307     g_object_set (sink, "fullscreen", TRUE, NULL);
308 
309   do {
310     gst_print ("Running loop %d\n", num_repeat++);
311 
312     if (use_overlay) {
313       gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink),
314           (guintptr) hwnd);
315     }
316 
317     sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
318     if (sret == GST_STATE_CHANGE_FAILURE) {
319       g_printerr ("Pipeline doesn't want to pause\n");
320       break;
321     } else {
322       /* add timer to repeat and reuse pipeline  */
323       if (test_reuse) {
324         GSource *timeout_source = g_timeout_source_new_seconds (3);
325 
326         g_source_set_callback (timeout_source,
327             (GSourceFunc) timeout_cb, loop, NULL);
328         g_source_attach (timeout_source, NULL);
329         g_source_unref (timeout_source);
330       }
331 
332       g_main_loop_run (loop);
333     }
334     gst_element_set_state (pipeline, GST_STATE_NULL);
335 
336     visible = FALSE;
337   } while (test_reuse);
338 
339   gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
340 
341 terminate:
342   if (hwnd)
343     DestroyWindow (hwnd);
344 
345   gst_object_unref (pipeline);
346   if (msg_io_channel)
347     g_io_channel_unref (msg_io_channel);
348   g_main_loop_unref (loop);
349 
350   return exitcode;
351 }
352