• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2008-2009 Julien Isorce <julien.isorce@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 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/gst.h>
26 #include <gtk/gtk.h>
27 #include <gdk/gdk.h>
28 
29 #include "../gstgtk.h"
30 
31 #ifdef HAVE_X11
32 #include <X11/Xlib.h>
33 #endif
34 
create_window(GstBus * bus,GstMessage * message,GtkWidget * widget)35 static GstBusSyncReply create_window (GstBus* bus, GstMessage* message, GtkWidget* widget)
36 {
37     GtkAllocation allocation;
38 
39     if (gst_gtk_handle_need_context (bus, message, NULL))
40         return GST_BUS_DROP;
41 
42     // ignore anything but 'prepare-window-handle' element messages
43     if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
44         return GST_BUS_PASS;
45 
46     if (!gst_is_video_overlay_prepare_window_handle_message (message))
47         return GST_BUS_PASS;
48 
49     g_print ("setting window handle %p\n", widget);
50 
51     gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), widget);
52 
53     gtk_widget_get_allocation (widget, &allocation);
54     gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)), allocation.x, allocation.y, allocation.width, allocation.height);
55 
56     gst_message_unref (message);
57 
58     return GST_BUS_DROP;
59 }
60 
61 static gboolean
resize_cb(GtkWidget * widget,GdkEvent * event,gpointer sink)62 resize_cb (GtkWidget * widget, GdkEvent * event, gpointer sink)
63 {
64     GtkAllocation allocation;
65 
66     gtk_widget_get_allocation (widget, &allocation);
67     gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (sink), allocation.x, allocation.y, allocation.width, allocation.height);
68 
69     return G_SOURCE_CONTINUE;
70 }
71 
end_stream_cb(GstBus * bus,GstMessage * message,GstElement * pipeline)72 static void end_stream_cb(GstBus* bus, GstMessage* message, GstElement* pipeline)
73 {
74     GError *error = NULL;
75     gchar *details;
76 
77     switch (GST_MESSAGE_TYPE (message)) {
78         case GST_MESSAGE_ERROR:
79             gst_message_parse_error (message, &error, &details);
80 
81             g_print("Error %s\n", error->message);
82             g_print("Details %s\n", details);
83         /* fallthrough */
84         case GST_MESSAGE_EOS:
85             g_print("End of stream\n");
86 
87             gst_element_set_state (pipeline, GST_STATE_NULL);
88             gst_object_unref(pipeline);
89 
90             gtk_main_quit();
91             break;
92         case GST_MESSAGE_WARNING:
93             gst_message_parse_warning (message, &error, &details);
94 
95             g_print("Warning %s\n", error->message);
96             g_print("Details %s\n", details);
97             break;
98         default:
99             break;
100     }
101 }
102 
expose_cb(GtkWidget * widget,cairo_t * cr,GstElement * videosink)103 static gboolean expose_cb(GtkWidget* widget, cairo_t *cr, GstElement* videosink)
104 {
105     gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink));
106     return FALSE;
107 }
108 
109 
destroy_cb(GtkWidget * widget,GdkEvent * event,GstElement * pipeline)110 static void destroy_cb(GtkWidget* widget, GdkEvent* event, GstElement* pipeline)
111 {
112     g_print("Close\n");
113 
114     gst_element_set_state (pipeline, GST_STATE_NULL);
115     gst_object_unref(pipeline);
116 
117     gtk_main_quit();
118 }
119 
120 
button_state_null_cb(GtkWidget * widget,GstElement * pipeline)121 static void button_state_null_cb(GtkWidget* widget, GstElement* pipeline)
122 {
123     gst_element_set_state (pipeline, GST_STATE_NULL);
124     g_print ("GST_STATE_NULL\n");
125 }
126 
127 
button_state_ready_cb(GtkWidget * widget,GstElement * pipeline)128 static void button_state_ready_cb(GtkWidget* widget, GstElement* pipeline)
129 {
130     gst_element_set_state (pipeline, GST_STATE_READY);
131     g_print ("GST_STATE_READY\n");
132 }
133 
134 
button_state_paused_cb(GtkWidget * widget,GstElement * pipeline)135 static void button_state_paused_cb(GtkWidget* widget, GstElement* pipeline)
136 {
137     gst_element_set_state (pipeline, GST_STATE_PAUSED);
138     g_print ("GST_STATE_PAUSED\n");
139 }
140 
141 
button_state_playing_cb(GtkWidget * widget,GstElement * pipeline)142 static void button_state_playing_cb(GtkWidget* widget, GstElement* pipeline)
143 {
144     gst_element_set_state (pipeline, GST_STATE_PLAYING);
145     g_print ("GST_STATE_PLAYING\n");
146 }
147 
148 
slider_fps_cb(GtkScale * scale,gdouble value,GstElement * pipeline)149 static gchar* slider_fps_cb (GtkScale* scale, gdouble value, GstElement* pipeline)
150 {
151     //change the video frame rate dynamically
152     return g_strdup_printf ("video framerate: %0.*g", gtk_scale_get_digits (scale), value);
153 }
154 
155 
156 
main(gint argc,gchar * argv[])157 gint main (gint argc, gchar *argv[])
158 {
159 #ifdef HAVE_X11
160     XInitThreads ();
161 #endif
162 
163     gtk_init (&argc, &argv);
164     gst_init (&argc, &argv);
165 
166     GstElement* pipeline = gst_pipeline_new ("pipeline");
167 
168     //window that contains an area where the video is drawn
169     GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
170     gtk_widget_set_size_request (window, 640, 480);
171     gtk_window_move (GTK_WINDOW (window), 300, 10);
172     gtk_window_set_title (GTK_WINDOW (window), "glimagesink implement the gstvideooverlay interface");
173     GdkGeometry geometry;
174     geometry.min_width = 1;
175     geometry.min_height = 1;
176     geometry.max_width = -1;
177     geometry.max_height = -1;
178     gtk_window_set_geometry_hints (GTK_WINDOW (window), window, &geometry, GDK_HINT_MIN_SIZE);
179 
180     //window to control the states
181     GtkWidget* window_control = gtk_window_new (GTK_WINDOW_TOPLEVEL);
182     geometry.min_width = 1;
183     geometry.min_height = 1;
184     geometry.max_width = -1;
185     geometry.max_height = -1;
186     gtk_window_set_geometry_hints (GTK_WINDOW (window_control), window_control, &geometry, GDK_HINT_MIN_SIZE);
187     gtk_window_set_resizable (GTK_WINDOW (window_control), FALSE);
188     gtk_window_move (GTK_WINDOW (window_control), 10, 10);
189     GtkWidget* grid = gtk_grid_new ();
190     gtk_container_add (GTK_CONTAINER (window_control), grid);
191 
192     //control state null
193     GtkWidget* button_state_null = gtk_button_new_with_label ("GST_STATE_NULL");
194     g_signal_connect (G_OBJECT (button_state_null), "clicked",
195         G_CALLBACK (button_state_null_cb), pipeline);
196     gtk_grid_attach (GTK_GRID (grid), button_state_null, 0, 1, 1, 1);
197     gtk_widget_show (button_state_null);
198 
199     //control state ready
200     GtkWidget* button_state_ready = gtk_button_new_with_label ("GST_STATE_READY");
201     g_signal_connect (G_OBJECT (button_state_ready), "clicked",
202         G_CALLBACK (button_state_ready_cb), pipeline);
203     gtk_grid_attach (GTK_GRID (grid), button_state_ready, 0, 2, 1, 1);
204     gtk_widget_show (button_state_ready);
205 
206     //control state paused
207     GtkWidget* button_state_paused = gtk_button_new_with_label ("GST_STATE_PAUSED");
208     g_signal_connect (G_OBJECT (button_state_paused), "clicked",
209         G_CALLBACK (button_state_paused_cb), pipeline);
210     gtk_grid_attach (GTK_GRID (grid), button_state_paused, 0, 3, 1, 1);
211     gtk_widget_show (button_state_paused);
212 
213     //control state playing
214     GtkWidget* button_state_playing = gtk_button_new_with_label ("GST_STATE_PLAYING");
215     g_signal_connect (G_OBJECT (button_state_playing), "clicked",
216         G_CALLBACK (button_state_playing_cb), pipeline);
217     gtk_grid_attach (GTK_GRID (grid), button_state_playing, 0, 4, 1, 1);
218     gtk_widget_show (button_state_playing);
219 
220     //change framerate
221     GtkWidget* slider_fps = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, 1, 30, 2);
222     g_signal_connect (G_OBJECT (slider_fps), "format-value",
223         G_CALLBACK (slider_fps_cb), pipeline);
224     gtk_grid_attach (GTK_GRID (grid), slider_fps, 1, 0, 1, 5);
225     gtk_widget_show (slider_fps);
226 
227     gtk_widget_show (grid);
228     gtk_widget_show (window_control);
229 
230     //configure the pipeline
231     g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(destroy_cb), pipeline);
232 
233     GstElement* videosrc = gst_element_factory_make ("videotestsrc", "videotestsrc");
234     GstElement* upload = gst_element_factory_make ("glupload", "glupload");
235     GstElement* glfiltercube = gst_element_factory_make ("glfiltercube", "glfiltercube");
236     GstElement* videosink = gst_element_factory_make ("glimagesink", "glimagesink");
237 
238     GstCaps *caps = gst_caps_new_simple("video/x-raw",
239                                         "width", G_TYPE_INT, 640,
240                                         "height", G_TYPE_INT, 480,
241                                         "framerate", GST_TYPE_FRACTION, 25, 1,
242                                         "format", G_TYPE_STRING, "RGBA",
243                                         NULL) ;
244 
245     gst_bin_add_many (GST_BIN (pipeline), videosrc, upload, glfiltercube, videosink, NULL);
246 
247     gboolean link_ok = gst_element_link_filtered(videosrc, upload, caps) ;
248     gst_caps_unref(caps) ;
249     if(!link_ok)
250     {
251         g_warning("Failed to link videosrc to glfiltercube!\n") ;
252         return -1;
253     }
254 
255     if(!gst_element_link_many(upload, glfiltercube, videosink, NULL))
256     {
257         g_warning("Failed to link glfiltercube to videosink!\n") ;
258         return -1;
259     }
260 
261     //area where the video is drawn
262     GtkWidget* area = gtk_drawing_area_new();
263     gtk_widget_set_redraw_on_allocate (area, TRUE);
264     gtk_container_add (GTK_CONTAINER (window), area);
265 
266     gtk_widget_realize(area);
267 
268     //set window id on this event
269     GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
270     gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, area, NULL);
271     gst_bus_add_signal_watch (bus);
272     g_signal_connect(bus, "message::error", G_CALLBACK(end_stream_cb), pipeline);
273     g_signal_connect(bus, "message::warning", G_CALLBACK(end_stream_cb), pipeline);
274     g_signal_connect(bus, "message::eos", G_CALLBACK(end_stream_cb), pipeline);
275     gst_object_unref (bus);
276 
277     //needed when being in GST_STATE_READY, GST_STATE_PAUSED
278     //or resizing/obscuring the window
279     g_signal_connect(area, "draw", G_CALLBACK(expose_cb), videosink);
280     g_signal_connect(area, "configure-event", G_CALLBACK(resize_cb), videosink);
281 
282     //start
283     GstStateChangeReturn ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
284     if (ret == GST_STATE_CHANGE_FAILURE)
285     {
286         g_print ("Failed to start up pipeline!\n");
287         return -1;
288     }
289 
290     gtk_widget_show_all (window);
291 
292     gtk_main();
293 
294     return 0;
295 }
296 
297