1 /*
2  * Copyright (C) 2018 LG Electronics
3  *   @author Wonchul Lee <w.lee@lge.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 #include <gst/gst.h>
22 
23 #include <gst/video/videooverlay.h>
24 #include <gst/wayland/wayland.h>
25 
26 static gint retry = 100;
27 
28 typedef struct
29 {
30   struct wl_display *display;
31   struct wl_display *display_wrapper;
32   struct wl_registry *registry;
33   struct wl_compositor *compositor;
34   struct wl_event_queue *queue;
35 
36   GThread *thread;
37 
38   GstElement *pipeline1;
39   GstElement *pipeline2;
40   GstVideoOverlay *overlay;
41   GMainLoop *loop;
42 } App;
43 
44 static gboolean
message_cb(GstBus * bus,GstMessage * message,gpointer user_data)45 message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
46 {
47   App *app = user_data;
48   switch (GST_MESSAGE_TYPE (message)) {
49     case GST_MESSAGE_ERROR:{
50       GError *err = NULL;
51       gchar *debug = NULL;
52 
53       gst_message_parse_error (message, &err, &debug);
54       gst_printerrln ("Error message received: %s", err->message);
55       gst_printerrln ("Debug info: %s", debug);
56       g_error_free (err);
57       g_free (debug);
58     }
59     case GST_MESSAGE_EOS:
60       if (retry <= 0)
61         g_main_loop_quit (app->loop);
62       gst_element_set_state (GST_ELEMENT (GST_MESSAGE_SRC (message)),
63           GST_STATE_NULL);
64       gst_element_set_state (GST_ELEMENT (GST_MESSAGE_SRC (message)),
65           GST_STATE_PLAYING);
66       retry--;
67       break;
68     default:
69       break;
70   }
71   return TRUE;
72 }
73 
74 static GstBusSyncReply
bus_sync_handler(GstBus * bus,GstMessage * message,gpointer user_data)75 bus_sync_handler (GstBus * bus, GstMessage * message, gpointer user_data)
76 {
77   App *app = user_data;
78 
79   if (gst_is_wayland_display_handle_need_context_message (message)) {
80     GstContext *context;
81     context = gst_wayland_display_handle_context_new (app->display);
82     gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (message)), context);
83 
84     goto drop;
85   }
86   return GST_BUS_PASS;
87 
88 drop:
89   gst_message_unref (message);
90   return GST_BUS_DROP;
91 }
92 
93 static void
registry_handle(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)94 registry_handle (void *data, struct wl_registry *registry,
95     uint32_t id, const char *interface, uint32_t version)
96 {
97   App *app = data;
98 
99   if (g_strcmp0 (interface, "wl_compositor") == 0) {
100     app->compositor =
101         wl_registry_bind (app->registry, id, &wl_compositor_interface,
102         MIN (version, 3));
103   }
104 }
105 
106 static const struct wl_registry_listener registry_listener = {
107   registry_handle
108 };
109 
110 static gpointer
wl_main_thread_run(gpointer data)111 wl_main_thread_run (gpointer data)
112 {
113   App *app = data;
114   while (wl_display_dispatch_queue (app->display, app->queue) != -1)
115     return NULL;
116 
117   return NULL;
118 }
119 
120 static GstElement *
build_pipeline(App * app,gint num_buffers)121 build_pipeline (App * app, gint num_buffers)
122 {
123   GstElement *pipeline;
124   GstBus *bus;
125   gchar *str;
126 
127   str =
128       g_strdup_printf ("videotestsrc num-buffers=%d ! waylandsink",
129       num_buffers);
130 
131   pipeline = gst_parse_launch (str, NULL);
132   g_free (str);
133   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
134   gst_bus_add_signal_watch (bus);
135   g_signal_connect (bus, "message", G_CALLBACK (message_cb), app);
136   gst_bus_set_sync_handler (bus, bus_sync_handler, app, NULL);
137   gst_object_unref (bus);
138 
139   return pipeline;
140 }
141 
142 int
main(int argc,char ** argv)143 main (int argc, char **argv)
144 {
145   App *app;
146   GError *error = NULL;
147 
148   gst_init (&argc, &argv);
149 
150   app = g_slice_new0 (App);
151 
152   app->loop = g_main_loop_new (NULL, FALSE);
153 
154   app->display = wl_display_connect (NULL);
155   if (!app->display)
156     goto done;
157   app->display_wrapper = wl_proxy_create_wrapper (app->display);
158   app->queue = wl_display_create_queue (app->display);
159   wl_proxy_set_queue ((struct wl_proxy *) app->display_wrapper, app->queue);
160   app->registry = wl_display_get_registry (app->display_wrapper);
161   wl_registry_add_listener (app->registry, &registry_listener, app);
162 
163   wl_display_roundtrip_queue (app->display, app->queue);
164   wl_display_roundtrip_queue (app->display, app->queue);
165 
166   if (!app->compositor) {
167     g_set_error (&error, g_quark_from_static_string ("waylandMultiThreads"), 0,
168         "Could not bind to wl_compositor interface");
169     goto done;
170   }
171 
172   app->thread =
173       g_thread_try_new ("WlMainThread", wl_main_thread_run, app, &error);
174   if (error) {
175     gst_printerrln ("error: %s", error->message);
176     g_error_free (error);
177     goto done;
178   }
179 
180   app->pipeline1 = build_pipeline (app, 30);
181   app->pipeline2 = build_pipeline (app, 40);
182 
183   gst_element_set_state (app->pipeline1, GST_STATE_PLAYING);
184   gst_element_set_state (app->pipeline2, GST_STATE_PLAYING);
185 
186   g_main_loop_run (app->loop);
187 
188   gst_element_set_state (app->pipeline1, GST_STATE_NULL);
189   gst_element_set_state (app->pipeline2, GST_STATE_NULL);
190 
191   gst_object_unref (app->pipeline1);
192   gst_object_unref (app->pipeline2);
193 
194 done:
195   if (app->thread)
196     g_thread_join (app->thread);
197 
198   if (app->compositor)
199     wl_compositor_destroy (app->compositor);
200   if (app->registry)
201     wl_registry_destroy (app->registry);
202   if (app->queue)
203     wl_event_queue_destroy (app->queue);
204   if (app->display_wrapper)
205     wl_proxy_wrapper_destroy (app->display_wrapper);
206   if (app->display) {
207     wl_display_flush (app->display);
208     wl_display_disconnect (app->display);
209   }
210   g_slice_free (App, app);
211   return 0;
212 }
213