1 /*
2  * GStreamer
3  * Copyright (C) 2008-2009 Filippo Argiolas <filippo.argiolas@gmail.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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #define GLIB_DISABLE_DEPRECATION_WARNINGS
25 
26 #include <gst/gst.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdk.h>
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 
31 #include "../gstgtk.h"
32 
33 #include <gst/video/videooverlay.h>
34 
35 #ifdef HAVE_X11
36 #include <X11/Xlib.h>
37 #endif
38 
39 
40 static GstBusSyncReply
create_window(GstBus * bus,GstMessage * message,GtkWidget * widget)41 create_window (GstBus * bus, GstMessage * message, GtkWidget * widget)
42 {
43   if (gst_gtk_handle_need_context (bus, message, NULL))
44     return GST_BUS_DROP;
45 
46   /* ignore anything but 'prepare-window-handle' element messages */
47   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
48     return GST_BUS_PASS;
49 
50   if (!gst_is_video_overlay_prepare_window_handle_message (message))
51     return GST_BUS_PASS;
52 
53   g_print ("setting window handle\n");
54 
55   /* do not call gdk_window_ensure_native for the first time here because
56    * we are in a different thread than the main thread
57    * (and the main thread the one) */
58   gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC
59           (message)), widget);
60 
61   gst_message_unref (message);
62 
63   return GST_BUS_DROP;
64 }
65 
66 
67 static void
end_stream_cb(GstBus * bus,GstMessage * message,GstElement * pipeline)68 end_stream_cb (GstBus * bus, GstMessage * message, GstElement * pipeline)
69 {
70   switch (GST_MESSAGE_TYPE (message)) {
71     case GST_MESSAGE_EOS:
72       g_print ("End of stream\n");
73 
74       gst_element_set_state (pipeline, GST_STATE_NULL);
75       gst_object_unref (pipeline);
76       gtk_main_quit ();
77       break;
78     case GST_MESSAGE_ERROR:
79     {
80       gchar *debug = NULL;
81       GError *err = NULL;
82 
83       gst_message_parse_error (message, &err, &debug);
84 
85       g_print ("Error: %s\n", err->message);
86       g_clear_error (&err);
87 
88       if (debug) {
89         g_print ("Debug details: %s\n", debug);
90         g_free (debug);
91       }
92 
93       gst_element_set_state (pipeline, GST_STATE_NULL);
94       gst_object_unref (pipeline);
95       gtk_main_quit ();
96       break;
97     }
98     default:
99       break;
100   }
101 }
102 
103 static gboolean
resize_cb(GtkWidget * widget,GdkEvent * event,gpointer data)104 resize_cb (GtkWidget * widget, GdkEvent * event, gpointer data)
105 {
106   GtkAllocation allocation;
107   GstVideoOverlay *overlay =
108       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (data),
109           GST_TYPE_VIDEO_OVERLAY));
110 
111   gtk_widget_get_allocation (widget, &allocation);
112   gst_video_overlay_set_render_rectangle (overlay, allocation.x, allocation.y,
113       allocation.width, allocation.height);
114   gst_object_unref (overlay);
115 
116   return G_SOURCE_CONTINUE;
117 }
118 
119 static gboolean
expose_cb(GtkWidget * widget,gpointer unused,gpointer data)120 expose_cb (GtkWidget * widget, gpointer unused, gpointer data)
121 {
122   GstVideoOverlay *overlay =
123       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (data),
124           GST_TYPE_VIDEO_OVERLAY));
125 
126   gst_video_overlay_expose (overlay);
127   gst_object_unref (overlay);
128 
129   return FALSE;
130 }
131 
132 static void
destroy_cb(GtkWidget * widget,GdkEvent * event,GstElement * pipeline)133 destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline)
134 {
135   g_message ("destroy callback");
136 
137   gst_element_set_state (pipeline, GST_STATE_NULL);
138   gst_object_unref (pipeline);
139 
140   gtk_main_quit ();
141 }
142 
143 static gboolean
apply_fx(GtkWidget * widget,gpointer data)144 apply_fx (GtkWidget * widget, gpointer data)
145 {
146   gchar *fx;
147   GEnumClass *p_class;
148 
149 /* heeeellppppp!! */
150   p_class =
151       G_PARAM_SPEC_ENUM (g_object_class_find_property (G_OBJECT_GET_CLASS
152           (G_OBJECT (data)), "effect")
153       )->enum_class;
154 
155   fx = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (widget));
156   g_print ("setting: %s - %s\n", fx, g_enum_get_value_by_nick (p_class,
157           fx)->value_name);
158   g_object_set (G_OBJECT (data), "effect", g_enum_get_value_by_nick (p_class,
159           fx)->value, NULL);
160   return FALSE;
161 }
162 
163 static gboolean
play_cb(GtkWidget * widget,gpointer data)164 play_cb (GtkWidget * widget, gpointer data)
165 {
166   g_message ("playing");
167   gst_element_set_state (GST_ELEMENT (data), GST_STATE_PLAYING);
168   return FALSE;
169 }
170 
171 static gboolean
null_cb(GtkWidget * widget,gpointer data)172 null_cb (GtkWidget * widget, gpointer data)
173 {
174   g_message ("nulling");
175   gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL);
176   return FALSE;
177 }
178 
179 static gboolean
ready_cb(GtkWidget * widget,gpointer data)180 ready_cb (GtkWidget * widget, gpointer data)
181 {
182   g_message ("readying");
183   gst_element_set_state (GST_ELEMENT (data), GST_STATE_READY);
184   return FALSE;
185 }
186 
187 static gboolean
pause_cb(GtkWidget * widget,gpointer data)188 pause_cb (GtkWidget * widget, gpointer data)
189 {
190   g_message ("pausing");
191   gst_element_set_state (GST_ELEMENT (data), GST_STATE_PAUSED);
192   return FALSE;
193 }
194 
195 gint
main(gint argc,gchar * argv[])196 main (gint argc, gchar * argv[])
197 {
198   GstStateChangeReturn ret;
199   GstElement *pipeline;
200   GstElement *upload, *filter, *sink;
201   GstElement *sourcebin;
202   GstBus *bus;
203   GError *error = NULL;
204 
205   GtkWidget *window;
206   GtkWidget *screen;
207   GtkWidget *vbox, *combo;
208   GtkWidget *hbox;
209   GtkWidget *play, *pause, *null, *ready;
210 
211   gchar **source_desc_array = NULL;
212   gchar *source_desc = NULL;
213 
214   GOptionContext *context;
215   GOptionEntry options[] = {
216     {"source-bin", 's', 0, G_OPTION_ARG_STRING_ARRAY, &source_desc_array,
217         "Use a custom source bin description (gst-launch style)", NULL}
218     ,
219     {NULL}
220   };
221 
222 #ifdef HAVE_X11
223   XInitThreads ();
224 #endif
225 
226   context = g_option_context_new (NULL);
227   g_option_context_add_main_entries (context, options, NULL);
228   g_option_context_add_group (context, gst_init_get_option_group ());
229   g_option_context_add_group (context, gtk_get_option_group (TRUE));
230   if (!g_option_context_parse (context, &argc, &argv, &error)) {
231     g_print ("Inizialization error: %s\n", GST_STR_NULL (error->message));
232     g_option_context_free (context);
233     g_clear_error (&error);
234     return -1;
235   }
236   g_option_context_free (context);
237 
238   if (source_desc_array != NULL) {
239     source_desc = g_strjoinv (" ", source_desc_array);
240     g_strfreev (source_desc_array);
241   }
242   if (source_desc == NULL) {
243     source_desc =
244         g_strdup
245         ("videotestsrc ! video/x-raw, width=352, height=288 ! identity");
246   }
247 
248   sourcebin =
249       gst_parse_bin_from_description (g_strdup (source_desc), TRUE, &error);
250   g_free (source_desc);
251   if (error) {
252     g_print ("Error while parsing source bin description: %s\n",
253         GST_STR_NULL (error->message));
254     return -1;
255   }
256 
257   g_set_application_name ("gst-gl-effects test app");
258 
259   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
260   gtk_container_set_border_width (GTK_CONTAINER (window), 3);
261 
262   pipeline = gst_pipeline_new ("pipeline");
263 
264   upload = gst_element_factory_make ("glupload", NULL);
265   filter = gst_element_factory_make ("gleffects", "flt");
266   sink = gst_element_factory_make ("glimagesink", "glsink");
267 
268   gst_bin_add_many (GST_BIN (pipeline), sourcebin, upload, filter, sink, NULL);
269 
270   if (!gst_element_link_many (sourcebin, upload, filter, sink, NULL)) {
271     g_print ("Failed to link one or more elements!\n");
272     return -1;
273   }
274 
275   g_signal_connect (G_OBJECT (window), "delete-event",
276       G_CALLBACK (destroy_cb), pipeline);
277   g_signal_connect (G_OBJECT (window), "destroy-event",
278       G_CALLBACK (destroy_cb), pipeline);
279 
280   screen = gtk_drawing_area_new ();
281   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
282   gst_bus_add_signal_watch (bus);
283   g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb),
284       pipeline);
285   g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb),
286       pipeline);
287   g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), pipeline);
288 
289   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, screen,
290       NULL);
291   gst_object_unref (bus);
292 
293   gtk_widget_set_size_request (screen, 640, 480);       // 500 x 376
294 
295   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
296 
297   gtk_box_pack_start (GTK_BOX (vbox), screen, TRUE, TRUE, 0);
298 
299   combo = gtk_combo_box_text_new ();
300 
301   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "identity");
302   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "mirror");
303   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "squeeze");
304   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "stretch");
305   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "fisheye");
306   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "twirl");
307   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "bulge");
308   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "tunnel");
309   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "square");
310   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "heat");
311   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "xpro");
312   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "lumaxpro");
313   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "sepia");
314   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "xray");
315   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "sin");
316   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "glow");
317 
318   g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (apply_fx), filter);
319 
320   gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
321 
322   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
323 
324   play = gtk_button_new_with_label ("PLAY");
325 
326   g_signal_connect (G_OBJECT (play), "clicked", G_CALLBACK (play_cb), pipeline);
327 
328   pause = gtk_button_new_with_label ("PAUSE");
329 
330   g_signal_connect (G_OBJECT (pause), "clicked",
331       G_CALLBACK (pause_cb), pipeline);
332 
333   null = gtk_button_new_with_label ("NULL");
334 
335   g_signal_connect (G_OBJECT (null), "clicked", G_CALLBACK (null_cb), pipeline);
336 
337   ready = gtk_button_new_with_label ("READY");
338 
339   g_signal_connect (G_OBJECT (ready), "clicked",
340       G_CALLBACK (ready_cb), pipeline);
341 
342   gtk_box_pack_start (GTK_BOX (hbox), null, TRUE, TRUE, 0);
343   gtk_box_pack_start (GTK_BOX (hbox), ready, TRUE, TRUE, 0);
344   gtk_box_pack_start (GTK_BOX (hbox), play, TRUE, TRUE, 0);
345   gtk_box_pack_start (GTK_BOX (hbox), pause, TRUE, TRUE, 0);
346 
347   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
348 
349   gtk_container_add (GTK_CONTAINER (window), vbox);
350 
351   g_signal_connect (screen, "draw", G_CALLBACK (expose_cb), pipeline);
352   g_signal_connect (screen, "configure-event", G_CALLBACK (resize_cb),
353       pipeline);
354   gtk_widget_realize (screen);
355 
356   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
357   if (ret == GST_STATE_CHANGE_FAILURE) {
358     g_print ("Failed to start up pipeline!\n");
359     return -1;
360   }
361 
362   gtk_widget_show_all (GTK_WIDGET (window));
363 
364   gtk_main ();
365 
366   return 0;
367 }
368