tests/icles/gdkpixbufsink-test.c: Add cast to placate gcc 4.1.2.
[platform/upstream/gst-plugins-good.git] / tests / icles / gdkpixbufsink-test.c
1 /* GStreamer interactive test for the gdkpixbufsink element
2  * Copyright (C) 2008 Tim-Philipp Müller <tim centricular net>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <gst/gst.h>
25 #include <gtk/gtk.h>
26
27 typedef struct
28 {
29   GstElement *pipe;
30   GstElement *sink;
31   gboolean got_video;
32
33   GtkWidget *win;
34   GtkWidget *img;
35   GtkWidget *slider;
36   GtkWidget *accurate_cb;
37
38   gboolean accurate;            /* whether to try accurate seeks */
39
40   gint64 cur_pos;
41   gboolean prerolled;
42 } AppInfo;
43
44 static void seek_to (AppInfo * info, gdouble percent);
45
46 static GstElement *
47 create_element (const gchar * factory_name)
48 {
49   GstElement *element;
50
51   element = gst_element_factory_make (factory_name, NULL);
52
53   if (element == NULL)
54     g_error ("Failed to create '%s' element", factory_name);
55
56   return element;
57 }
58
59 static void
60 new_decoded_pad (GstElement * dec, GstPad * new_pad, gboolean last,
61     AppInfo * info)
62 {
63   const gchar *sname;
64   GstElement *csp, *scale, *filter;
65   GstStructure *s;
66   GstCaps *caps;
67   GstPad *sinkpad;
68
69   /* already found a video stream? */
70   if (info->got_video)
71     return;
72
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);
77
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-"))
81     goto not_video;
82
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);
88
89   gst_bin_add_many (GST_BIN (info->pipe), csp, scale, filter, info->sink, NULL);
90
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);
95
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");
102
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);
107
108   info->got_video = TRUE;
109   return;
110
111 not_video:
112   {
113     if (last) {
114       g_error ("This file does not contain a video track, or you do not have "
115           "the necessary decoder(s) installed");
116     }
117   }
118 }
119
120 static void
121 bus_message_cb (GstBus * bus, GstMessage * msg, AppInfo * info)
122 {
123   switch (GST_MESSAGE_TYPE (msg)) {
124     case GST_MESSAGE_ASYNC_DONE:{
125       GstFormat fmt = GST_FORMAT_TIME;
126
127       /* only interested in async-done messages from the top-level pipeline */
128       if (msg->src != GST_OBJECT_CAST (info->pipe))
129         break;
130
131       if (!info->prerolled) {
132         /* make slider visible if it's not visible already */
133         gtk_widget_show (info->slider);
134
135         /* initial frame is often black, so seek to beginning plus a bit */
136         seek_to (info, 0.001);
137         info->prerolled = TRUE;
138       }
139
140       /* update position */
141       if (!gst_element_query_position (info->pipe, &fmt, &info->cur_pos))
142         info->cur_pos = -1;
143       break;
144     }
145     case GST_MESSAGE_ELEMENT:{
146       const GValue *val;
147       GdkPixbuf *pixbuf = NULL;
148
149       /* only interested in element messages from our gdkpixbufsink */
150       if (msg->src != GST_OBJECT_CAST (info->sink))
151         break;
152
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")) {
156         break;
157       }
158
159       g_print ("pixbuf\n");
160       val = gst_structure_get_value (msg->structure, "pixbuf");
161       g_return_if_fail (val != NULL);
162
163       pixbuf = GDK_PIXBUF (g_value_dup_object (val));
164       gtk_image_set_from_pixbuf (GTK_IMAGE (info->img), pixbuf);
165       g_object_unref (pixbuf);
166       break;
167     }
168     case GST_MESSAGE_ERROR:{
169       GError *err = NULL;
170       gchar *dbg = NULL;
171
172       gst_message_parse_error (msg, &err, &dbg);
173       g_error ("Error: %s\n%s\n", err->message, (dbg) ? dbg : "");
174       g_error_free (err);
175       g_free (dbg);
176       break;
177     }
178     default:
179       break;
180   }
181 }
182
183 static gboolean
184 create_pipeline (AppInfo * info, const gchar * filename)
185 {
186   GstElement *src, *dec;
187   GstBus *bus;
188
189   info->pipe = gst_pipeline_new ("pipeline");
190   src = create_element ("filesrc");
191   g_object_set (src, "location", filename, NULL);
192
193   dec = create_element ("decodebin2");
194
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");
198
199   g_signal_connect (dec, "new-decoded-pad", G_CALLBACK (new_decoded_pad), info);
200
201   /* set up bus */
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);
206
207   return TRUE;
208 }
209
210 static void
211 seek_to (AppInfo * info, gdouble percent)
212 {
213   GstSeekFlags seek_flags;
214   GstFormat fmt = GST_FORMAT_TIME;
215   gint64 seek_pos, dur = -1;
216
217   if (!gst_element_query_duration (info->pipe, &fmt, &dur) || dur <= 0) {
218     g_printerr ("Could not query duration\n");
219     return;
220   }
221
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);
225
226   seek_flags = GST_SEEK_FLAG_FLUSH;
227
228   if (info->accurate)
229     seek_flags |= GST_SEEK_FLAG_ACCURATE;
230   else
231     seek_flags |= GST_SEEK_FLAG_KEY_UNIT;
232
233   if (!gst_element_seek_simple (info->pipe, GST_FORMAT_TIME, seek_flags,
234           seek_pos)) {
235     g_printerr ("Seek failed.\n");
236     return;
237   }
238 }
239
240 static void
241 slider_cb (GtkRange * range, AppInfo * info)
242 {
243   gdouble val;
244
245   val = gtk_range_get_value (range);
246   seek_to (info, val);
247 }
248
249 static gchar *
250 slider_format_value_cb (GtkScale * scale, gdouble value, AppInfo * info)
251 {
252   gchar s[64];
253
254   if (info->cur_pos < 0)
255     return g_strdup_printf ("%0.1g%%", value * 100.0);
256
257   g_snprintf (s, 64, "%" GST_TIME_FORMAT, GST_TIME_ARGS (info->cur_pos));
258   s[10] = '\0';
259   return g_strdup (s);
260 }
261
262 static void
263 accurate_toggled_cb (GtkToggleButton * toggle, AppInfo * info)
264 {
265   info->accurate = gtk_toggle_button_get_active (toggle);
266 }
267
268 static void
269 run_gui (const gchar * filename)
270 {
271   GtkWidget *vbox, *hbox;
272   AppInfo *info;
273
274   info = g_new0 (AppInfo, 1);
275
276   /* create pipeline */
277   if (!create_pipeline (info, filename))
278     goto done;
279
280   /* create window */
281   info->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
282   g_signal_connect (info->win, "delete-event", G_CALLBACK (gtk_main_quit),
283       NULL);
284
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);
288
289   info->img = gtk_image_new ();
290   gtk_box_pack_start (GTK_BOX (vbox), info->img, FALSE, FALSE, 6);
291
292   hbox = gtk_hbox_new (FALSE, 6);
293   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
294
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);
300
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);
307
308   /* and go! */
309   gst_element_set_state (info->pipe, GST_STATE_PAUSED);
310
311   gtk_widget_show_all (info->win);
312   gtk_widget_hide (info->slider);       /* hide until we're prerolled */
313   gtk_main ();
314
315 done:
316
317   g_free (info);
318 }
319
320 static gchar **filenames = NULL;
321
322 int
323 main (int argc, char **argv)
324 {
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}
328   };
329   GOptionContext *ctx;
330   GError *opt_err = NULL;
331
332   if (!g_thread_supported ())
333     g_thread_init (NULL);
334
335   gtk_init (&argc, &argv);
336
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);
341
342   if (!g_option_context_parse (ctx, &argc, &argv, &opt_err)) {
343     g_error ("Error parsing command line options: %s", opt_err->message);
344     return -1;
345   }
346
347   if (filenames == NULL || filenames[0] == NULL || filenames[0][0] == '\0') {
348     g_printerr ("Please specify a path to a video file\n\n");
349     return -1;
350   }
351
352   run_gui (filenames[0]);
353
354   g_free (filenames);
355   g_option_context_free (ctx);
356
357   return 0;
358 }