• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.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/videooverlay.h>
27 #include <windows.h>
28 
29 static GMainLoop *loop = NULL;
30 static gboolean visible = FALSE;
31 static HWND hwnd = NULL;
32 static gboolean set_handle_on_request = FALSE;
33 static gboolean test_reuse = FALSE;
34 
35 static LRESULT CALLBACK
window_proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)36 window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
37 {
38   switch (message) {
39     case WM_DESTROY:
40       hwnd = NULL;
41 
42       if (loop) {
43         g_main_loop_quit (loop);
44       }
45       return 0;
46     default:
47       break;
48   }
49 
50   return DefWindowProc (hWnd, message, wParam, lParam);
51 }
52 
53 static gboolean
msg_cb(GIOChannel * source,GIOCondition condition,gpointer data)54 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
55 {
56   MSG msg;
57 
58   if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
59     return G_SOURCE_CONTINUE;
60 
61   TranslateMessage (&msg);
62   DispatchMessage (&msg);
63 
64   return G_SOURCE_CONTINUE;
65 }
66 
67 static gboolean
bus_msg(GstBus * bus,GstMessage * msg,gpointer user_data)68 bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
69 {
70   GstElement *playbin = GST_ELEMENT (user_data);
71 
72   switch (GST_MESSAGE_TYPE (msg)) {
73     case GST_MESSAGE_ASYNC_DONE:
74       /* make window visible when we have something to show */
75       if (!visible && hwnd) {
76         ShowWindow (hwnd, SW_SHOW);
77         visible = TRUE;
78       }
79 
80       gst_element_set_state (playbin, GST_STATE_PLAYING);
81       break;
82     case GST_MESSAGE_EOS:
83       gst_println ("End of stream");
84       g_main_loop_quit (loop);
85       break;
86     default:
87       break;
88   }
89 
90   return TRUE;
91 }
92 
93 static GstBusSyncReply
bus_sync_handler(GstBus * bus,GstMessage * msg,gpointer user_data)94 bus_sync_handler (GstBus * bus, GstMessage * msg, gpointer user_data)
95 {
96   if (set_handle_on_request &&
97       gst_is_video_overlay_prepare_window_handle_message (msg)) {
98     GstVideoOverlay *overlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (msg));
99 
100     gst_println ("Pipeline needs window handle");
101     gst_video_overlay_set_window_handle (overlay, (guintptr) hwnd);
102     gst_message_unref (msg);
103 
104     return GST_BUS_DROP;
105   }
106 
107   switch (GST_MESSAGE_TYPE (msg)) {
108     case GST_MESSAGE_ERROR:{
109       GError *err;
110       gchar *dbg;
111 
112       gst_message_parse_error (msg, &err, &dbg);
113       gst_printerrln ("ERROR %s ", err->message);
114       if (dbg != NULL)
115         gst_printerrln ("ERROR debug information: %s", dbg);
116       g_clear_error (&err);
117       g_free (dbg);
118 
119       test_reuse = FALSE;
120 
121       g_main_loop_quit (loop);
122       break;
123     }
124     default:
125       break;
126   }
127 
128   return GST_BUS_PASS;
129 }
130 
131 gint
main(gint argc,gchar ** argv)132 main (gint argc, gchar ** argv)
133 {
134   GstElement *playbin;
135   WNDCLASSEX wc = { 0, };
136   HINSTANCE hinstance = GetModuleHandle (NULL);
137   GIOChannel *msg_io_channel;
138   GOptionContext *option_ctx;
139   GError *error = NULL;
140   gchar *uri = NULL;
141   RECT wr = { 0, 0, 320, 240 };
142   gint exitcode = 0;
143   gboolean ret;
144   GOptionEntry options[] = {
145     {"uri", 0, 0, G_OPTION_ARG_STRING, &uri,
146         "URI to test playback with Win32 overlay", NULL}
147     ,
148     {"set-handle-on-request", 0, 0, G_OPTION_ARG_NONE, &set_handle_on_request,
149         "Set window handle on \"prepare-window-handle\" message", NULL}
150     ,
151     {"repeat", 0, 0, G_OPTION_ARG_NONE, &test_reuse,
152         "Repeat and reuse pipeline per EOS", NULL}
153     ,
154     {NULL}
155   };
156 
157   option_ctx =
158       g_option_context_new ("WIN32 video overlay with playbin example");
159   g_option_context_add_main_entries (option_ctx, options, NULL);
160   g_option_context_add_group (option_ctx, gst_init_get_option_group ());
161   ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
162   g_option_context_free (option_ctx);
163 
164   if (!ret) {
165     gst_printerrln ("option parsing failed: %s", error->message);
166     g_clear_error (&error);
167     exit (1);
168   }
169 
170   if (!uri) {
171     gst_printerrln ("--uri is a required argument");
172     g_clear_error (&error);
173     exit (1);
174   }
175 
176   /* prepare window */
177   wc.cbSize = sizeof (WNDCLASSEX);
178   wc.style = CS_HREDRAW | CS_VREDRAW;
179   wc.lpfnWndProc = (WNDPROC) window_proc;
180   wc.hInstance = hinstance;
181   wc.hCursor = LoadCursor (NULL, IDC_ARROW);
182   wc.lpszClassName = "GstWin32VideoOverlayPlaybin";
183   RegisterClassEx (&wc);
184 
185   AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
186   hwnd = CreateWindowEx (0, wc.lpszClassName,
187       "GstWin32VideoOverlayPlaybin",
188       WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
189       CW_USEDEFAULT, CW_USEDEFAULT,
190       wr.right - wr.left, wr.bottom - wr.top, (HWND) NULL, (HMENU) NULL,
191       hinstance, NULL);
192 
193   loop = g_main_loop_new (NULL, FALSE);
194   msg_io_channel = g_io_channel_win32_new_messages (0);
195   g_io_add_watch (msg_io_channel, G_IO_IN, msg_cb, NULL);
196 
197   /* prepare the pipeline */
198   playbin = gst_element_factory_make ("playbin", NULL);
199 
200   if (!playbin) {
201     gst_printerrln ("playbin is not available");
202 
203     exitcode = 1;
204     goto terminate;
205   }
206 
207   /* User can set window handle on playbin before starting
208    * pipeline without watching "prepare-window-handle" message,
209    * because playbin/playsink will pass the given handle to selected
210    * video sink element later once video sink is prepared.
211    *
212    * But in case that an application wants to delay setting window handle
213    * as much as possible for some reason, the application needs to check
214    * "prepare-window-handle" message
215    * (use gst_is_video_overlay_prepare_window_handle_message() API for check)
216    * via a *sync* message handler and should set window handle on
217    * the *sync* message handler immediately */
218   if (!set_handle_on_request) {
219     GstVideoOverlay *overlay = GST_VIDEO_OVERLAY (playbin);
220 
221     gst_println ("Setting window handle now");
222     gst_video_overlay_set_window_handle (overlay, (guintptr) hwnd);
223   } else {
224     gst_println ("Will set window handle on \"prepare-window-handle\" message");
225   }
226 
227   gst_bus_add_watch (GST_ELEMENT_BUS (playbin), bus_msg, playbin);
228   gst_bus_set_sync_handler (GST_ELEMENT_BUS (playbin),
229       bus_sync_handler, NULL, NULL);
230   g_object_set (playbin, "uri", uri, NULL);
231 
232   do {
233     if (gst_element_set_state (playbin,
234             GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
235       gst_printerrln ("Pipeline doesn't want to pause");
236       gst_bus_remove_watch (GST_ELEMENT_BUS (playbin));
237 
238       exitcode = 1;
239       goto terminate;
240     }
241 
242     g_main_loop_run (loop);
243     gst_element_set_state (playbin, GST_STATE_NULL);
244   } while (test_reuse);
245 
246   gst_bus_remove_watch (GST_ELEMENT_BUS (playbin));
247 
248 terminate:
249   if (hwnd)
250     DestroyWindow (hwnd);
251 
252   gst_object_unref (playbin);
253   g_io_channel_unref (msg_io_channel);
254   g_main_loop_unref (loop);
255   g_free (uri);
256 
257   return exitcode;
258 }
259