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 static gint delay = 0;
40 static gint saveddelay = 0;
41 static gint method = 1;
42 
43 struct _SourceData
44 {
45   gpointer data;
46   gpointer nick;
47   gpointer value;
48 };
49 typedef struct _SourceData SourceData;
50 
51 static GstBusSyncReply
create_window(GstBus * bus,GstMessage * message,GtkWidget * widget)52 create_window (GstBus * bus, GstMessage * message, GtkWidget * widget)
53 {
54   // ignore anything but 'prepare-window-handle' element messages
55   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
56     return GST_BUS_PASS;
57 
58   if (!gst_is_video_overlay_prepare_window_handle_message (message))
59     return GST_BUS_PASS;
60 
61   gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC
62           (message)), widget);
63 
64   gst_message_unref (message);
65 
66   return GST_BUS_DROP;
67 }
68 
69 static void
message_cb(GstBus * bus,GstMessage * message,GstElement * pipeline)70 message_cb (GstBus * bus, GstMessage * message, GstElement * pipeline)
71 {
72   gst_element_set_state (pipeline, GST_STATE_NULL);
73   gst_object_unref (pipeline);
74 
75   gtk_main_quit ();
76 }
77 
78 static gboolean
expose_cb(GtkWidget * widget,cairo_t * cr,GstElement * videosink)79 expose_cb (GtkWidget * widget, cairo_t * cr, GstElement * videosink)
80 {
81   gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink));
82   return FALSE;
83 }
84 
85 static void
destroy_cb(GtkWidget * widget,GdkEvent * event,GstElement * pipeline)86 destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline)
87 {
88   g_message ("destroy callback");
89 
90   gst_element_set_state (pipeline, GST_STATE_NULL);
91   gst_object_unref (pipeline);
92 
93   gtk_main_quit ();
94 }
95 
96 static gboolean
play_cb(GtkWidget * widget,gpointer data)97 play_cb (GtkWidget * widget, gpointer data)
98 {
99   g_message ("playing");
100   gst_element_set_state (GST_ELEMENT (data), GST_STATE_PLAYING);
101   return FALSE;
102 }
103 
104 static gboolean
null_cb(GtkWidget * widget,gpointer data)105 null_cb (GtkWidget * widget, gpointer data)
106 {
107   g_message ("nulling");
108   gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL);
109   return FALSE;
110 }
111 
112 static gboolean
ready_cb(GtkWidget * widget,gpointer data)113 ready_cb (GtkWidget * widget, gpointer data)
114 {
115   g_message ("readying");
116   gst_element_set_state (GST_ELEMENT (data), GST_STATE_READY);
117   return FALSE;
118 }
119 
120 static gboolean
pause_cb(GtkWidget * widget,gpointer data)121 pause_cb (GtkWidget * widget, gpointer data)
122 {
123   g_message ("pausing");
124   gst_element_set_state (GST_ELEMENT (data), GST_STATE_PAUSED);
125   return FALSE;
126 }
127 
128 static gboolean
set_location_delayed(gpointer data)129 set_location_delayed (gpointer data)
130 {
131   SourceData *sdata = (SourceData *) data;
132   delay--;
133   g_print ("%d\n", delay);
134   if (delay > 0) {
135     return TRUE;
136   }
137   g_object_set (G_OBJECT (sdata->data), sdata->nick, sdata->value, NULL);
138   delay = saveddelay;
139   return FALSE;
140 }
141 
142 static void
on_drag_data_received(GtkWidget * widget,GdkDragContext * context,int x,int y,GtkSelectionData * seldata,guint inf,guint time,gpointer data)143 on_drag_data_received (GtkWidget * widget,
144     GdkDragContext * context, int x, int y,
145     GtkSelectionData * seldata, guint inf, guint time, gpointer data)
146 {
147   SourceData *userdata = g_new0 (SourceData, 1);
148   GdkPixbufFormat *format;
149   gchar **uris = gtk_selection_data_get_uris (seldata);
150   gchar *filename = NULL;
151 
152   g_return_if_fail (uris != NULL);
153   filename = g_filename_from_uri (uris[0], NULL, NULL);
154   g_return_if_fail (filename != NULL);
155   format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
156   g_return_if_fail (format);
157   g_print ("received %s image: %s\n", filename,
158       gdk_pixbuf_format_get_name (format));
159 
160   userdata->nick = (gchar *) "location";
161   userdata->value = g_strdup (filename);
162   userdata->data = data;
163   saveddelay = delay;
164   if (delay > 0) {
165     g_print ("%d\n", delay);
166     g_timeout_add_seconds (1, set_location_delayed, userdata);
167   } else
168     g_object_set (G_OBJECT (userdata->data), userdata->nick, userdata->value,
169         NULL);
170   g_free (filename);
171 }
172 
173 
174 gint
main(gint argc,gchar * argv[])175 main (gint argc, gchar * argv[])
176 {
177   GstElement *pipeline;
178   GstElement *filter, *sink;
179   GstElement *sourcebin;
180   GstBus *bus;
181   GError *error = NULL;
182 
183   GtkWidget *window;
184   GtkWidget *screen;
185   GtkWidget *vbox;
186   GtkWidget *hbox;
187   GtkWidget *play, *pause, *null, *ready;
188 
189   gchar **source_desc_array = NULL;
190   gchar *source_desc = NULL;
191 
192   GOptionContext *context;
193   GOptionEntry options[] = {
194     {"source-bin", 's', 0, G_OPTION_ARG_STRING_ARRAY, &source_desc_array,
195         "Use a custom source bin description (gst-launch style)", NULL}
196     ,
197     {"method", 'm', 0, G_OPTION_ARG_INT, &method,
198         "1 for gstdifferencematte, 2 for gloverlay", "M"}
199     ,
200     {"delay", 'd', 0, G_OPTION_ARG_INT, &delay,
201           "Wait N seconds before to send the image to gstreamer (useful with differencematte)",
202         "N"}
203     ,
204     {NULL}
205   };
206 
207 #ifdef HAVE_X11
208   XInitThreads ();
209 #endif
210 
211   context = g_option_context_new (NULL);
212   g_option_context_add_main_entries (context, options, NULL);
213   g_option_context_add_group (context, gst_init_get_option_group ());
214   g_option_context_add_group (context, gtk_get_option_group (TRUE));
215   if (!g_option_context_parse (context, &argc, &argv, &error)) {
216     g_print ("Inizialization error: %s\n", GST_STR_NULL (error->message));
217     g_option_context_free (context);
218     g_clear_error (&error);
219     return -1;
220   }
221   g_option_context_free (context);
222 
223   if (source_desc_array != NULL) {
224     source_desc = g_strjoinv (" ", source_desc_array);
225     g_strfreev (source_desc_array);
226   }
227   if (source_desc == NULL) {
228     source_desc =
229         g_strdup
230         ("videotestsrc ! video/x-raw, width=352, height=288 ! identity ! glupload");
231   }
232 
233   sourcebin =
234       gst_parse_bin_from_description (g_strdup (source_desc), TRUE, &error);
235   g_free (source_desc);
236   if (error) {
237     g_print ("Error while parsing source bin description: %s\n",
238         GST_STR_NULL (error->message));
239     return -1;
240   }
241 
242   g_set_application_name ("gst-gl-effects test app");
243 
244   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
245   gtk_container_set_border_width (GTK_CONTAINER (window), 3);
246 
247   pipeline = gst_pipeline_new ("pipeline");
248 
249   if (method == 2) {
250     filter = gst_element_factory_make ("gloverlay", "flt");
251   } else {
252     filter = gst_element_factory_make ("gldifferencematte", "flt");
253   }
254   sink = gst_element_factory_make ("glimagesink", "glsink");
255 
256   gst_bin_add_many (GST_BIN (pipeline), sourcebin, filter, sink, NULL);
257 
258   if (!gst_element_link_many (sourcebin, filter, sink, NULL)) {
259     g_print ("Failed to link one or more elements!\n");
260     return -1;
261   }
262 
263   g_signal_connect (G_OBJECT (window), "delete-event",
264       G_CALLBACK (destroy_cb), pipeline);
265   g_signal_connect (G_OBJECT (window), "destroy-event",
266       G_CALLBACK (destroy_cb), pipeline);
267 
268   screen = gtk_drawing_area_new ();
269 
270   gtk_widget_set_size_request (screen, 640, 480);       // 500 x 376
271 
272   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
273 
274   gtk_box_pack_start (GTK_BOX (vbox), screen, TRUE, TRUE, 0);
275 
276   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
277 
278   play = gtk_button_new_with_label ("PLAY");
279 
280   g_signal_connect (G_OBJECT (play), "clicked", G_CALLBACK (play_cb), pipeline);
281 
282   pause = gtk_button_new_with_label ("PAUSE");
283 
284   g_signal_connect (G_OBJECT (pause), "clicked",
285       G_CALLBACK (pause_cb), pipeline);
286 
287   null = gtk_button_new_with_label ("NULL");
288 
289   g_signal_connect (G_OBJECT (null), "clicked", G_CALLBACK (null_cb), pipeline);
290 
291   ready = gtk_button_new_with_label ("READY");
292 
293   g_signal_connect (G_OBJECT (ready), "clicked",
294       G_CALLBACK (ready_cb), pipeline);
295 
296   gtk_box_pack_start (GTK_BOX (hbox), null, TRUE, TRUE, 0);
297   gtk_box_pack_start (GTK_BOX (hbox), ready, TRUE, TRUE, 0);
298   gtk_box_pack_start (GTK_BOX (hbox), play, TRUE, TRUE, 0);
299   gtk_box_pack_start (GTK_BOX (hbox), pause, TRUE, TRUE, 0);
300 
301   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
302 
303   gtk_container_add (GTK_CONTAINER (window), vbox);
304 
305   gtk_widget_realize (screen);
306 
307   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
308   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, screen,
309       NULL);
310   gst_bus_add_signal_watch (bus);
311   g_signal_connect (bus, "message::error", G_CALLBACK (message_cb), pipeline);
312   g_signal_connect (bus, "message::warning", G_CALLBACK (message_cb), pipeline);
313   g_signal_connect (bus, "message::eos", G_CALLBACK (message_cb), pipeline);
314   gst_object_unref (bus);
315   g_signal_connect (screen, "draw", G_CALLBACK (expose_cb), sink);
316 
317   gtk_drag_dest_set (screen, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
318   gtk_drag_dest_add_uri_targets (screen);
319 
320   g_signal_connect (screen, "drag-data-received",
321       G_CALLBACK (on_drag_data_received), filter);
322 
323   gtk_widget_show_all (GTK_WIDGET (window));
324 
325   gst_element_set_state (pipeline, GST_STATE_PLAYING);
326 
327   gtk_main ();
328 
329   return 0;
330 }
331