1 /* GStreamer interactive test for the gdkpixbufsink element
2 * Copyright (C) 2008 Tim-Philipp Müller <tim centricular net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
36 GtkWidget *accurate_cb;
38 gboolean accurate; /* whether to try accurate seeks */
44 static void seek_to (AppInfo * info, gdouble percent);
47 create_element (const gchar * factory_name)
51 element = gst_element_factory_make (factory_name, NULL);
54 g_error ("Failed to create '%s' element", factory_name);
60 new_decoded_pad (GstElement * dec, GstPad * new_pad, gboolean last,
64 GstElement *csp, *scale, *filter;
69 /* already found a video stream? */
73 /* FIXME: is this racy or does decodebin2 make sure caps are always
74 * negotiated at this point? */
75 caps = gst_pad_get_caps (new_pad);
76 g_return_if_fail (caps != NULL);
78 s = gst_caps_get_structure (caps, 0);
79 sname = gst_structure_get_name (s);
80 if (!g_str_has_prefix (sname, "video/x-raw-"))
83 csp = create_element ("ffmpegcolorspace");
84 scale = create_element ("videoscale");
85 filter = create_element ("capsfilter");
86 info->sink = create_element ("gdkpixbufsink");
87 g_object_set (info->sink, "qos", FALSE, "max-lateness", (gint64) - 1, NULL);
89 gst_bin_add_many (GST_BIN (info->pipe), csp, scale, filter, info->sink, NULL);
91 sinkpad = gst_element_get_static_pad (csp, "sink");
92 if (GST_PAD_LINK_FAILED (gst_pad_link (new_pad, sinkpad)))
93 g_error ("Can't link new decoded pad to ffmpegcolorspace's sink pad");
94 gst_object_unref (sinkpad);
96 if (!gst_element_link (csp, scale))
97 g_error ("Can't link ffmpegcolorspace to videoscale");
98 if (!gst_element_link (scale, filter))
99 g_error ("Can't link videoscale to capsfilter");
100 if (!gst_element_link (filter, info->sink))
101 g_error ("Can't link capsfilter to gdkpixbufsink");
103 gst_element_set_state (info->sink, GST_STATE_PAUSED);
104 gst_element_set_state (filter, GST_STATE_PAUSED);
105 gst_element_set_state (scale, GST_STATE_PAUSED);
106 gst_element_set_state (csp, GST_STATE_PAUSED);
108 info->got_video = TRUE;
114 g_error ("This file does not contain a video track, or you do not have "
115 "the necessary decoder(s) installed");
121 bus_message_cb (GstBus * bus, GstMessage * msg, AppInfo * info)
123 switch (GST_MESSAGE_TYPE (msg)) {
124 case GST_MESSAGE_ASYNC_DONE:{
125 GstFormat fmt = GST_FORMAT_TIME;
127 /* only interested in async-done messages from the top-level pipeline */
128 if (msg->src != GST_OBJECT_CAST (info->pipe))
131 if (!info->prerolled) {
132 /* make slider visible if it's not visible already */
133 gtk_widget_show (info->slider);
135 /* initial frame is often black, so seek to beginning plus a bit */
136 seek_to (info, 0.001);
137 info->prerolled = TRUE;
140 /* update position */
141 if (!gst_element_query_position (info->pipe, &fmt, &info->cur_pos))
145 case GST_MESSAGE_ELEMENT:{
147 GdkPixbuf *pixbuf = NULL;
149 /* only interested in element messages from our gdkpixbufsink */
150 if (msg->src != GST_OBJECT_CAST (info->sink))
153 /* only interested in these two messages */
154 if (!gst_structure_has_name (msg->structure, "preroll-pixbuf") &&
155 !gst_structure_has_name (msg->structure, "pixbuf")) {
159 g_print ("pixbuf\n");
160 val = gst_structure_get_value (msg->structure, "pixbuf");
161 g_return_if_fail (val != NULL);
163 pixbuf = g_value_dup_object (val);
164 gtk_image_set_from_pixbuf (GTK_IMAGE (info->img), pixbuf);
165 g_object_unref (pixbuf);
168 case GST_MESSAGE_ERROR:{
172 gst_message_parse_error (msg, &err, &dbg);
173 g_error ("Error: %s\n%s\n", err->message, (dbg) ? dbg : "");
184 create_pipeline (AppInfo * info, const gchar * filename)
186 GstElement *src, *dec;
189 info->pipe = gst_pipeline_new ("pipeline");
190 src = create_element ("filesrc");
191 g_object_set (src, "location", filename, NULL);
193 dec = create_element ("decodebin2");
195 gst_bin_add_many (GST_BIN (info->pipe), src, dec, NULL);
196 if (!gst_element_link (src, dec))
197 g_error ("Can't link filesrc to decodebin2");
199 g_signal_connect (dec, "new-decoded-pad", G_CALLBACK (new_decoded_pad), info);
202 bus = gst_element_get_bus (info->pipe);
203 gst_bus_add_signal_watch (bus);
204 g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), info);
205 gst_object_unref (bus);
211 seek_to (AppInfo * info, gdouble percent)
213 GstSeekFlags seek_flags;
214 GstFormat fmt = GST_FORMAT_TIME;
215 gint64 seek_pos, dur = -1;
217 if (!gst_element_query_duration (info->pipe, &fmt, &dur) || dur <= 0) {
218 g_printerr ("Could not query duration\n");
222 seek_pos = gst_gdouble_to_guint64 (gst_guint64_to_gdouble (dur) * percent);
223 g_print ("Seeking to %" GST_TIME_FORMAT ", accurate: %d\n",
224 GST_TIME_ARGS (seek_pos), info->accurate);
226 seek_flags = GST_SEEK_FLAG_FLUSH;
229 seek_flags |= GST_SEEK_FLAG_ACCURATE;
231 seek_flags |= GST_SEEK_FLAG_KEY_UNIT;
233 if (!gst_element_seek_simple (info->pipe, GST_FORMAT_TIME, seek_flags,
235 g_printerr ("Seek failed.\n");
241 slider_cb (GtkRange * range, AppInfo * info)
245 val = gtk_range_get_value (range);
250 slider_format_value_cb (GtkScale * scale, gdouble value, AppInfo * info)
254 if (info->cur_pos < 0)
255 return g_strdup_printf ("%0.1g%%", value * 100.0);
257 g_snprintf (s, 64, "%" GST_TIME_FORMAT, GST_TIME_ARGS (info->cur_pos));
263 accurate_toggled_cb (GtkToggleButton * toggle, AppInfo * info)
265 info->accurate = gtk_toggle_button_get_active (toggle);
269 run_gui (const gchar * filename)
271 GtkWidget *vbox, *hbox;
274 info = g_new0 (AppInfo, 1);
276 /* create pipeline */
277 if (!create_pipeline (info, filename))
281 info->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
282 g_signal_connect (info->win, "delete-event", G_CALLBACK (gtk_main_quit),
285 vbox = gtk_vbox_new (FALSE, 6);
286 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
287 gtk_container_add (GTK_CONTAINER (info->win), vbox);
289 info->img = gtk_image_new ();
290 gtk_box_pack_start (GTK_BOX (vbox), info->img, FALSE, FALSE, 6);
292 hbox = gtk_hbox_new (FALSE, 6);
293 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
295 info->accurate_cb = gtk_check_button_new_with_label ("accurate seek "
296 "(might not work reliably with all demuxers)");
297 gtk_box_pack_start (GTK_BOX (hbox), info->accurate_cb, FALSE, FALSE, 6);
298 g_signal_connect (info->accurate_cb, "toggled",
299 G_CALLBACK (accurate_toggled_cb), info);
301 info->slider = gtk_hscale_new_with_range (0.0, 1.0, 0.001);
302 gtk_box_pack_start (GTK_BOX (vbox), info->slider, FALSE, FALSE, 6);
303 g_signal_connect (info->slider, "value-changed",
304 G_CALLBACK (slider_cb), info);
305 g_signal_connect (info->slider, "format-value",
306 G_CALLBACK (slider_format_value_cb), info);
309 gst_element_set_state (info->pipe, GST_STATE_PAUSED);
311 gtk_widget_show_all (info->win);
312 gtk_widget_hide (info->slider); /* hide until we're prerolled */
320 static gchar **filenames = NULL;
323 main (int argc, char **argv)
325 static const GOptionEntry test_goptions[] = {
326 {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL},
327 {NULL, '\0', 0, 0, NULL, NULL, NULL}
330 GError *opt_err = NULL;
332 if (!g_thread_supported ())
333 g_thread_init (NULL);
335 gtk_init (&argc, &argv);
337 /* command line option parsing */
338 ctx = g_option_context_new (" VIDEOFILE");
339 g_option_context_add_group (ctx, gst_init_get_option_group ());
340 g_option_context_add_main_entries (ctx, test_goptions, NULL);
342 if (!g_option_context_parse (ctx, &argc, &argv, &opt_err)) {
343 g_error ("Error parsing command line options: %s", opt_err->message);
347 if (filenames == NULL || filenames[0] == NULL || filenames[0][0] == '\0') {
348 g_printerr ("Please specify a path to a video file\n\n");
352 run_gui (filenames[0]);
355 g_option_context_free (ctx);