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