seek: Add UI for setting the colorbalance interface properties on playbin2
[platform/upstream/gstreamer.git] / tests / examples / seek / jsseek.c
1 /* GStreamer
2  *
3  * seek.c: seeking sample application
4  *
5  * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
6  *               2006 Stefan Kost <ensonic@users.sf.net>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
28  * with newer GTK versions (>= 3.3.0) */
29 #define GDK_DISABLE_DEPRECATION_WARNINGS
30
31 #include <glib.h>
32 #include <glib/gstdio.h>
33
34 #include <stdlib.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38
39 #include <math.h>
40
41 #include <gtk/gtk.h>
42 #include <gst/gst.h>
43 #include <string.h>
44
45 #include <linux/input.h>
46 #include <linux/joystick.h>
47
48 #ifdef HAVE_X
49 #include <gdk/gdkx.h>
50 #endif
51 #include <gst/interfaces/xoverlay.h>
52
53 GST_DEBUG_CATEGORY_STATIC (seek_debug);
54 #define GST_CAT_DEFAULT (seek_debug)
55
56 /* configuration */
57
58 //#define SOURCE "filesrc"
59 #define SOURCE "gnomevfssrc"
60
61 #define ASINK "alsasink"
62 //#define ASINK "osssink"
63
64 #define VSINK "xvimagesink"
65 //#define VSINK "sdlvideosink"
66 //#define VSINK "ximagesink"
67 //#define VSINK "aasink"
68 //#define VSINK "cacasink"
69
70 #define FILL_INTERVAL 100
71 //#define UPDATE_INTERVAL 500
72 //#define UPDATE_INTERVAL 100
73 //#define UPDATE_INTERVAL 10
74 #define UPDATE_INTERVAL 40
75
76 /* number of milliseconds to play for after a seek */
77 #define SCRUB_TIME 100
78
79 /* timeout for gst_element_get_state() after a seek */
80 #define SEEK_TIMEOUT 40 * GST_MSECOND
81
82 #define DEFAULT_VIDEO_HEIGHT 300
83
84 /* the state to go to when stop is pressed */
85 #define STOP_STATE      GST_STATE_READY
86
87
88 static GList *seekable_pads = NULL;
89 static GList *rate_pads = NULL;
90 static GList *seekable_elements = NULL;
91
92 static gboolean accurate_seek = FALSE;
93 static gboolean keyframe_seek = FALSE;
94 static gboolean loop_seek = FALSE;
95 static gboolean flush_seek = TRUE;
96 static gboolean scrub = TRUE;
97 static gboolean play_scrub = FALSE;
98 static gboolean skip_seek = FALSE;
99 static gdouble rate = 1.0;
100
101 static GstElement *pipeline;
102 static gint pipeline_type;
103 static const gchar *pipeline_spec;
104 static gint64 position = -1;
105 static gint64 duration = -1;
106 static GtkAdjustment *adjustment;
107 static GtkWidget *hscale, *statusbar;
108 static guint status_id = 0;
109 static gboolean stats = FALSE;
110 static gboolean elem_seek = FALSE;
111 static gboolean verbose = FALSE;
112 static gchar *js_device = NULL;
113
114 static gboolean is_live = FALSE;
115 static gboolean buffering = FALSE;
116 static GstBufferingMode mode;
117 static gint64 buffering_left;
118 static GstState state = GST_STATE_NULL;
119 static guint update_id = 0;
120 static guint seek_timeout_id = 0;
121 static gulong changed_id;
122 static guint fill_id = 0;
123
124 static gint n_video = 0, n_audio = 0, n_text = 0;
125 static gboolean need_streams = TRUE;
126 static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
127 static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
128 static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
129 static GtkWidget *skip_checkbox, *video_window, *download_checkbox;
130 static GtkWidget *buffer_checkbox, *rate_spinbutton;
131
132 static GStaticMutex state_mutex = G_STATIC_MUTEX_INIT;
133
134 static GtkWidget *format_combo, *step_amount_spinbutton, *step_rate_spinbutton;
135 static GtkWidget *shuttle_checkbox, *step_button;
136 static GtkWidget *shuttle_hscale;
137 static GtkAdjustment *shuttle_adjustment;
138
139 static GList *paths = NULL, *l = NULL;
140
141 gint js_fd;
142
143 /* we keep an array of the visualisation entries so that we can easily switch
144  * with the combo box index. */
145 typedef struct
146 {
147   GstElementFactory *factory;
148 } VisEntry;
149
150 static GArray *vis_entries;
151
152 static void clear_streams (GstElement * pipeline);
153 static void volume_notify_cb (GstElement * pipeline, GParamSpec * arg,
154     gpointer user_dat);
155
156 /* pipeline construction */
157
158 typedef struct
159 {
160   const gchar *padname;
161   GstPad *target;
162   GstElement *bin;
163 }
164 dyn_link;
165
166 static GstElement *
167 gst_element_factory_make_or_warn (const gchar * type, const gchar * name)
168 {
169   GstElement *element = gst_element_factory_make (type, name);
170
171   if (!element) {
172     g_warning ("Failed to create element %s of type %s", name, type);
173   }
174
175   return element;
176 }
177
178 static void
179 dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
180 {
181   gchar *padname;
182   dyn_link *connect = (dyn_link *) data;
183
184   padname = gst_pad_get_name (newpad);
185
186   if (connect->padname == NULL || !strcmp (padname, connect->padname)) {
187     if (connect->bin)
188       gst_bin_add (GST_BIN (pipeline), connect->bin);
189     gst_pad_link (newpad, connect->target);
190
191     //seekable_pads = g_list_prepend (seekable_pads, newpad);
192     rate_pads = g_list_prepend (rate_pads, newpad);
193   }
194   g_free (padname);
195 }
196
197 static void
198 setup_dynamic_link (GstElement * element, const gchar * padname,
199     GstPad * target, GstElement * bin)
200 {
201   dyn_link *connect;
202
203   connect = g_new0 (dyn_link, 1);
204   connect->padname = g_strdup (padname);
205   connect->target = target;
206   connect->bin = bin;
207
208   g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
209       connect);
210 }
211
212 static GstElement *
213 make_mod_pipeline (const gchar * location)
214 {
215   GstElement *pipeline;
216   GstElement *src, *decoder, *audiosink;
217   GstPad *seekable;
218
219   pipeline = gst_pipeline_new ("app");
220
221   src = gst_element_factory_make_or_warn (SOURCE, "src");
222   decoder = gst_element_factory_make_or_warn ("modplug", "decoder");
223   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
224   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
225
226   g_object_set (G_OBJECT (src), "location", location, NULL);
227
228   gst_bin_add (GST_BIN (pipeline), src);
229   gst_bin_add (GST_BIN (pipeline), decoder);
230   gst_bin_add (GST_BIN (pipeline), audiosink);
231
232   gst_element_link (src, decoder);
233   gst_element_link (decoder, audiosink);
234
235   seekable = gst_element_get_static_pad (decoder, "src");
236   seekable_pads = g_list_prepend (seekable_pads, seekable);
237   rate_pads = g_list_prepend (rate_pads, seekable);
238   rate_pads =
239       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
240
241   return pipeline;
242 }
243
244 static GstElement *
245 make_dv_pipeline (const gchar * location)
246 {
247   GstElement *pipeline;
248   GstElement *src, *demux, *decoder, *audiosink, *videosink;
249   GstElement *a_queue, *v_queue;
250   GstPad *seekable;
251
252   pipeline = gst_pipeline_new ("app");
253
254   src = gst_element_factory_make_or_warn (SOURCE, "src");
255   demux = gst_element_factory_make_or_warn ("dvdemux", "demuxer");
256   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
257   decoder = gst_element_factory_make_or_warn ("ffdec_dvvideo", "decoder");
258   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
259   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
260   audiosink = gst_element_factory_make_or_warn ("alsasink", "a_sink");
261
262   g_object_set (G_OBJECT (src), "location", location, NULL);
263
264   gst_bin_add (GST_BIN (pipeline), src);
265   gst_bin_add (GST_BIN (pipeline), demux);
266   gst_bin_add (GST_BIN (pipeline), a_queue);
267   gst_bin_add (GST_BIN (pipeline), audiosink);
268   gst_bin_add (GST_BIN (pipeline), v_queue);
269   gst_bin_add (GST_BIN (pipeline), decoder);
270   gst_bin_add (GST_BIN (pipeline), videosink);
271
272   gst_element_link (src, demux);
273   gst_element_link (a_queue, audiosink);
274   gst_element_link (v_queue, decoder);
275   gst_element_link (decoder, videosink);
276
277   setup_dynamic_link (demux, "video", gst_element_get_static_pad (v_queue,
278           "sink"), NULL);
279   setup_dynamic_link (demux, "audio", gst_element_get_static_pad (a_queue,
280           "sink"), NULL);
281
282   seekable = gst_element_get_static_pad (decoder, "src");
283   seekable_pads = g_list_prepend (seekable_pads, seekable);
284   rate_pads = g_list_prepend (rate_pads, seekable);
285
286   return pipeline;
287 }
288
289 static GstElement *
290 make_wav_pipeline (const gchar * location)
291 {
292   GstElement *pipeline;
293   GstElement *src, *decoder, *audiosink;
294
295   pipeline = gst_pipeline_new ("app");
296
297   src = gst_element_factory_make_or_warn (SOURCE, "src");
298   decoder = gst_element_factory_make_or_warn ("wavparse", "decoder");
299   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
300
301   g_object_set (G_OBJECT (src), "location", location, NULL);
302
303   gst_bin_add (GST_BIN (pipeline), src);
304   gst_bin_add (GST_BIN (pipeline), decoder);
305   gst_bin_add (GST_BIN (pipeline), audiosink);
306
307   gst_element_link (src, decoder);
308
309   setup_dynamic_link (decoder, "src", gst_element_get_static_pad (audiosink,
310           "sink"), NULL);
311
312   seekable_elements = g_list_prepend (seekable_elements, audiosink);
313
314   /* force element seeking on this pipeline */
315   elem_seek = TRUE;
316
317   return pipeline;
318 }
319
320 static GstElement *
321 make_flac_pipeline (const gchar * location)
322 {
323   GstElement *pipeline;
324   GstElement *src, *decoder, *audiosink;
325   GstPad *seekable;
326
327   pipeline = gst_pipeline_new ("app");
328
329   src = gst_element_factory_make_or_warn (SOURCE, "src");
330   decoder = gst_element_factory_make_or_warn ("flacdec", "decoder");
331   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
332   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
333
334   g_object_set (G_OBJECT (src), "location", location, NULL);
335
336   gst_bin_add (GST_BIN (pipeline), src);
337   gst_bin_add (GST_BIN (pipeline), decoder);
338   gst_bin_add (GST_BIN (pipeline), audiosink);
339
340   gst_element_link (src, decoder);
341   gst_element_link (decoder, audiosink);
342
343   seekable = gst_element_get_static_pad (decoder, "src");
344   seekable_pads = g_list_prepend (seekable_pads, seekable);
345   rate_pads = g_list_prepend (rate_pads, seekable);
346   rate_pads =
347       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
348
349   return pipeline;
350 }
351
352 static GstElement *
353 make_sid_pipeline (const gchar * location)
354 {
355   GstElement *pipeline;
356   GstElement *src, *decoder, *audiosink;
357   GstPad *seekable;
358
359   pipeline = gst_pipeline_new ("app");
360
361   src = gst_element_factory_make_or_warn (SOURCE, "src");
362   decoder = gst_element_factory_make_or_warn ("siddec", "decoder");
363   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
364   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
365
366   g_object_set (G_OBJECT (src), "location", location, NULL);
367
368   gst_bin_add (GST_BIN (pipeline), src);
369   gst_bin_add (GST_BIN (pipeline), decoder);
370   gst_bin_add (GST_BIN (pipeline), audiosink);
371
372   gst_element_link (src, decoder);
373   gst_element_link (decoder, audiosink);
374
375   seekable = gst_element_get_static_pad (decoder, "src");
376   seekable_pads = g_list_prepend (seekable_pads, seekable);
377   rate_pads = g_list_prepend (rate_pads, seekable);
378   rate_pads =
379       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
380
381   return pipeline;
382 }
383
384 static GstElement *
385 make_parse_pipeline (const gchar * location)
386 {
387   GstElement *pipeline;
388   GstElement *src, *parser, *fakesink;
389   GstPad *seekable;
390
391   pipeline = gst_pipeline_new ("app");
392
393   src = gst_element_factory_make_or_warn (SOURCE, "src");
394   parser = gst_element_factory_make_or_warn ("mpegparse", "parse");
395   fakesink = gst_element_factory_make_or_warn ("fakesink", "sink");
396   g_object_set (G_OBJECT (fakesink), "silent", TRUE, NULL);
397   g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
398
399   g_object_set (G_OBJECT (src), "location", location, NULL);
400
401   gst_bin_add (GST_BIN (pipeline), src);
402   gst_bin_add (GST_BIN (pipeline), parser);
403   gst_bin_add (GST_BIN (pipeline), fakesink);
404
405   gst_element_link (src, parser);
406   gst_element_link (parser, fakesink);
407
408   seekable = gst_element_get_static_pad (parser, "src");
409   seekable_pads = g_list_prepend (seekable_pads, seekable);
410   rate_pads = g_list_prepend (rate_pads, seekable);
411   rate_pads =
412       g_list_prepend (rate_pads, gst_element_get_static_pad (parser, "sink"));
413
414   return pipeline;
415 }
416
417 static GstElement *
418 make_vorbis_pipeline (const gchar * location)
419 {
420   GstElement *pipeline, *audio_bin;
421   GstElement *src, *demux, *decoder, *convert, *audiosink;
422   GstPad *pad, *seekable;
423
424   pipeline = gst_pipeline_new ("app");
425
426   src = gst_element_factory_make_or_warn (SOURCE, "src");
427   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
428   decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder");
429   convert = gst_element_factory_make_or_warn ("audioconvert", "convert");
430   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
431   g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL);
432
433   g_object_set (G_OBJECT (src), "location", location, NULL);
434
435   audio_bin = gst_bin_new ("a_decoder_bin");
436
437   gst_bin_add (GST_BIN (pipeline), src);
438   gst_bin_add (GST_BIN (pipeline), demux);
439   gst_bin_add (GST_BIN (audio_bin), decoder);
440   gst_bin_add (GST_BIN (audio_bin), convert);
441   gst_bin_add (GST_BIN (audio_bin), audiosink);
442   gst_bin_add (GST_BIN (pipeline), audio_bin);
443
444   gst_element_link (src, demux);
445   gst_element_link (decoder, convert);
446   gst_element_link (convert, audiosink);
447
448   pad = gst_element_get_static_pad (decoder, "sink");
449   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
450   gst_object_unref (pad);
451
452   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
453           "sink"), NULL);
454
455   seekable = gst_element_get_static_pad (decoder, "src");
456   seekable_pads = g_list_prepend (seekable_pads, seekable);
457   rate_pads = g_list_prepend (rate_pads, seekable);
458   rate_pads =
459       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
460
461   return pipeline;
462 }
463
464 static GstElement *
465 make_theora_pipeline (const gchar * location)
466 {
467   GstElement *pipeline, *video_bin;
468   GstElement *src, *demux, *decoder, *convert, *videosink;
469   GstPad *pad, *seekable;
470
471   pipeline = gst_pipeline_new ("app");
472
473   src = gst_element_factory_make_or_warn (SOURCE, "src");
474   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
475   decoder = gst_element_factory_make_or_warn ("theoradec", "decoder");
476   convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert");
477   videosink = gst_element_factory_make_or_warn (VSINK, "sink");
478
479   g_object_set (G_OBJECT (src), "location", location, NULL);
480
481   video_bin = gst_bin_new ("v_decoder_bin");
482
483   gst_bin_add (GST_BIN (pipeline), src);
484   gst_bin_add (GST_BIN (pipeline), demux);
485   gst_bin_add (GST_BIN (video_bin), decoder);
486   gst_bin_add (GST_BIN (video_bin), convert);
487   gst_bin_add (GST_BIN (video_bin), videosink);
488   gst_bin_add (GST_BIN (pipeline), video_bin);
489
490   gst_element_link (src, demux);
491   gst_element_link (decoder, convert);
492   gst_element_link (convert, videosink);
493
494   pad = gst_element_get_static_pad (decoder, "sink");
495   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
496   gst_object_unref (pad);
497
498   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
499           "sink"), NULL);
500
501   seekable = gst_element_get_static_pad (decoder, "src");
502   seekable_pads = g_list_prepend (seekable_pads, seekable);
503   rate_pads = g_list_prepend (rate_pads, seekable);
504   rate_pads =
505       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
506
507   return pipeline;
508 }
509
510 static GstElement *
511 make_vorbis_theora_pipeline (const gchar * location)
512 {
513   GstElement *pipeline, *audio_bin, *video_bin;
514   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
515   GstElement *audiosink, *videosink;
516   GstElement *a_queue, *v_queue, *v_scale;
517   GstPad *seekable;
518   GstPad *pad;
519
520   pipeline = gst_pipeline_new ("app");
521
522   src = gst_element_factory_make_or_warn (SOURCE, "src");
523   g_object_set (G_OBJECT (src), "location", location, NULL);
524
525   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
526
527   gst_bin_add (GST_BIN (pipeline), src);
528   gst_bin_add (GST_BIN (pipeline), demux);
529   gst_element_link (src, demux);
530
531   audio_bin = gst_bin_new ("a_decoder_bin");
532   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
533   a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec");
534   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
535   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
536
537   gst_bin_add (GST_BIN (pipeline), audio_bin);
538
539   gst_bin_add (GST_BIN (audio_bin), a_queue);
540   gst_bin_add (GST_BIN (audio_bin), a_decoder);
541   gst_bin_add (GST_BIN (audio_bin), a_convert);
542   gst_bin_add (GST_BIN (audio_bin), audiosink);
543
544   gst_element_link (a_queue, a_decoder);
545   gst_element_link (a_decoder, a_convert);
546   gst_element_link (a_convert, audiosink);
547
548   pad = gst_element_get_static_pad (a_queue, "sink");
549   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
550   gst_object_unref (pad);
551
552   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
553           "sink"), NULL);
554
555   video_bin = gst_bin_new ("v_decoder_bin");
556   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
557   v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec");
558   v_convert =
559       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
560   v_scale = gst_element_factory_make_or_warn ("videoscale", "v_scale");
561   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
562
563   gst_bin_add (GST_BIN (pipeline), video_bin);
564
565   gst_bin_add (GST_BIN (video_bin), v_queue);
566   gst_bin_add (GST_BIN (video_bin), v_decoder);
567   gst_bin_add (GST_BIN (video_bin), v_convert);
568   gst_bin_add (GST_BIN (video_bin), v_scale);
569   gst_bin_add (GST_BIN (video_bin), videosink);
570
571   gst_element_link_many (v_queue, v_decoder, v_convert, v_scale, videosink,
572       NULL);
573
574   pad = gst_element_get_static_pad (v_queue, "sink");
575   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
576   gst_object_unref (pad);
577
578   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
579           "sink"), NULL);
580
581   seekable = gst_element_get_static_pad (a_decoder, "src");
582   seekable_pads = g_list_prepend (seekable_pads, seekable);
583   rate_pads = g_list_prepend (rate_pads, seekable);
584   rate_pads =
585       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
586           "sink"));
587
588   return pipeline;
589 }
590
591 static GstElement *
592 make_avi_msmpeg4v3_mp3_pipeline (const gchar * location)
593 {
594   GstElement *pipeline, *audio_bin, *video_bin;
595   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
596   GstElement *audiosink, *videosink;
597   GstElement *a_queue, *v_queue;
598   GstPad *seekable, *pad;
599
600   pipeline = gst_pipeline_new ("app");
601
602   src = gst_element_factory_make_or_warn (SOURCE, "src");
603   g_object_set (G_OBJECT (src), "location", location, NULL);
604
605   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
606
607   gst_bin_add (GST_BIN (pipeline), src);
608   gst_bin_add (GST_BIN (pipeline), demux);
609   gst_element_link (src, demux);
610
611   audio_bin = gst_bin_new ("a_decoder_bin");
612   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
613   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
614   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
615   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
616
617   gst_bin_add (GST_BIN (audio_bin), a_queue);
618   gst_bin_add (GST_BIN (audio_bin), a_decoder);
619   gst_bin_add (GST_BIN (audio_bin), a_convert);
620   gst_bin_add (GST_BIN (audio_bin), audiosink);
621
622   gst_element_link (a_queue, a_decoder);
623   gst_element_link (a_decoder, a_convert);
624   gst_element_link (a_convert, audiosink);
625
626   gst_bin_add (GST_BIN (pipeline), audio_bin);
627
628   pad = gst_element_get_static_pad (a_queue, "sink");
629   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
630   gst_object_unref (pad);
631
632   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
633           "sink"), NULL);
634
635   video_bin = gst_bin_new ("v_decoder_bin");
636   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
637   v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec");
638   v_convert =
639       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
640   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
641
642   gst_bin_add (GST_BIN (video_bin), v_queue);
643   gst_bin_add (GST_BIN (video_bin), v_decoder);
644   gst_bin_add (GST_BIN (video_bin), v_convert);
645   gst_bin_add (GST_BIN (video_bin), videosink);
646
647   gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL);
648
649   gst_bin_add (GST_BIN (pipeline), video_bin);
650
651   pad = gst_element_get_static_pad (v_queue, "sink");
652   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
653   gst_object_unref (pad);
654
655   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
656           "sink"), NULL);
657
658   seekable = gst_element_get_static_pad (a_decoder, "src");
659   seekable_pads = g_list_prepend (seekable_pads, seekable);
660   rate_pads = g_list_prepend (rate_pads, seekable);
661   rate_pads =
662       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
663           "sink"));
664
665   return pipeline;
666 }
667
668 static GstElement *
669 make_mp3_pipeline (const gchar * location)
670 {
671   GstElement *pipeline;
672   GstElement *src, *parser, *decoder, *audiosink, *queue;
673   GstPad *seekable;
674
675   pipeline = gst_pipeline_new ("app");
676
677   src = gst_element_factory_make_or_warn (SOURCE, "src");
678   parser = gst_element_factory_make_or_warn ("mp3parse", "parse");
679   decoder = gst_element_factory_make_or_warn ("mad", "dec");
680   queue = gst_element_factory_make_or_warn ("queue", "queue");
681   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
682
683   seekable_elements = g_list_prepend (seekable_elements, audiosink);
684
685   g_object_set (G_OBJECT (src), "location", location, NULL);
686   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
687
688   gst_bin_add (GST_BIN (pipeline), src);
689   gst_bin_add (GST_BIN (pipeline), parser);
690   gst_bin_add (GST_BIN (pipeline), decoder);
691   gst_bin_add (GST_BIN (pipeline), queue);
692   gst_bin_add (GST_BIN (pipeline), audiosink);
693
694   gst_element_link (src, parser);
695   gst_element_link (parser, decoder);
696   gst_element_link (decoder, queue);
697   gst_element_link (queue, audiosink);
698
699   seekable = gst_element_get_static_pad (queue, "src");
700   seekable_pads = g_list_prepend (seekable_pads, seekable);
701   rate_pads = g_list_prepend (rate_pads, seekable);
702   rate_pads =
703       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
704
705   return pipeline;
706 }
707
708 static GstElement *
709 make_avi_pipeline (const gchar * location)
710 {
711   GstElement *pipeline, *audio_bin, *video_bin;
712   GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
713   GstElement *a_queue = NULL, *v_queue = NULL;
714   GstPad *seekable;
715
716   pipeline = gst_pipeline_new ("app");
717
718   src = gst_element_factory_make_or_warn (SOURCE, "src");
719   g_object_set (G_OBJECT (src), "location", location, NULL);
720
721   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
722   seekable_elements = g_list_prepend (seekable_elements, demux);
723
724   gst_bin_add (GST_BIN (pipeline), src);
725   gst_bin_add (GST_BIN (pipeline), demux);
726   gst_element_link (src, demux);
727
728   audio_bin = gst_bin_new ("a_decoder_bin");
729   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
730   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
731   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
732   gst_element_link (a_decoder, a_queue);
733   gst_element_link (a_queue, audiosink);
734   gst_bin_add (GST_BIN (audio_bin), a_decoder);
735   gst_bin_add (GST_BIN (audio_bin), a_queue);
736   gst_bin_add (GST_BIN (audio_bin), audiosink);
737   gst_element_set_state (audio_bin, GST_STATE_PAUSED);
738
739   setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
740           "sink"), audio_bin);
741
742   seekable = gst_element_get_static_pad (a_queue, "src");
743   seekable_pads = g_list_prepend (seekable_pads, seekable);
744   rate_pads = g_list_prepend (rate_pads, seekable);
745   rate_pads =
746       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
747           "sink"));
748
749   video_bin = gst_bin_new ("v_decoder_bin");
750   v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec");
751   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
752   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
753   gst_element_link (v_decoder, v_queue);
754   gst_element_link (v_queue, videosink);
755   gst_bin_add (GST_BIN (video_bin), v_decoder);
756   gst_bin_add (GST_BIN (video_bin), v_queue);
757   gst_bin_add (GST_BIN (video_bin), videosink);
758
759   gst_element_set_state (video_bin, GST_STATE_PAUSED);
760
761   setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
762           "sink"), video_bin);
763
764   seekable = gst_element_get_static_pad (v_queue, "src");
765   seekable_pads = g_list_prepend (seekable_pads, seekable);
766   rate_pads = g_list_prepend (rate_pads, seekable);
767   rate_pads =
768       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
769           "sink"));
770
771   return pipeline;
772 }
773
774 static GstElement *
775 make_mpeg_pipeline (const gchar * location)
776 {
777   GstElement *pipeline, *audio_bin, *video_bin;
778   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
779   GstElement *audiosink, *videosink;
780   GstElement *a_queue, *v_queue;
781   GstPad *seekable;
782   GstPad *pad;
783
784   pipeline = gst_pipeline_new ("app");
785
786   src = gst_element_factory_make_or_warn (SOURCE, "src");
787   g_object_set (G_OBJECT (src), "location", location, NULL);
788
789   //demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
790   demux = gst_element_factory_make_or_warn ("flupsdemux", "demux");
791
792   gst_bin_add (GST_BIN (pipeline), src);
793   gst_bin_add (GST_BIN (pipeline), demux);
794   gst_element_link (src, demux);
795
796   audio_bin = gst_bin_new ("a_decoder_bin");
797   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
798   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
799   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
800   gst_bin_add (GST_BIN (audio_bin), a_decoder);
801   gst_bin_add (GST_BIN (audio_bin), a_queue);
802   gst_bin_add (GST_BIN (audio_bin), audiosink);
803
804   gst_element_link (a_decoder, a_queue);
805   gst_element_link (a_queue, audiosink);
806
807   gst_bin_add (GST_BIN (pipeline), audio_bin);
808
809   pad = gst_element_get_static_pad (a_decoder, "sink");
810   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
811   gst_object_unref (pad);
812
813   setup_dynamic_link (demux, "audio_c0", gst_element_get_static_pad (audio_bin,
814           "sink"), NULL);
815
816   video_bin = gst_bin_new ("v_decoder_bin");
817   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
818   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
819   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
820   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
821
822   gst_bin_add (GST_BIN (video_bin), v_decoder);
823   gst_bin_add (GST_BIN (video_bin), v_queue);
824   gst_bin_add (GST_BIN (video_bin), v_filter);
825   gst_bin_add (GST_BIN (video_bin), videosink);
826
827   gst_element_link (v_decoder, v_queue);
828   gst_element_link (v_queue, v_filter);
829   gst_element_link (v_filter, videosink);
830
831   gst_bin_add (GST_BIN (pipeline), video_bin);
832
833   pad = gst_element_get_static_pad (v_decoder, "sink");
834   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
835   gst_object_unref (pad);
836
837   setup_dynamic_link (demux, "video_e0", gst_element_get_static_pad (video_bin,
838           "sink"), NULL);
839
840   seekable = gst_element_get_static_pad (v_filter, "src");
841   seekable_pads = g_list_prepend (seekable_pads, seekable);
842   rate_pads = g_list_prepend (rate_pads, seekable);
843   rate_pads =
844       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
845           "sink"));
846
847   return pipeline;
848 }
849
850 static GstElement *
851 make_mpegnt_pipeline (const gchar * location)
852 {
853   GstElement *pipeline, *audio_bin, *video_bin;
854   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
855   GstElement *audiosink, *videosink;
856   GstElement *a_queue;
857   GstPad *seekable;
858
859   pipeline = gst_pipeline_new ("app");
860
861   src = gst_element_factory_make_or_warn (SOURCE, "src");
862   g_object_set (G_OBJECT (src), "location", location, NULL);
863
864   demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
865   //g_object_set (G_OBJECT (demux), "sync", TRUE, NULL);
866
867   seekable_elements = g_list_prepend (seekable_elements, demux);
868
869   gst_bin_add (GST_BIN (pipeline), src);
870   gst_bin_add (GST_BIN (pipeline), demux);
871   gst_element_link (src, demux);
872
873   audio_bin = gst_bin_new ("a_decoder_bin");
874   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
875   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
876   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
877   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
878   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
879   gst_element_link (a_decoder, a_queue);
880   gst_element_link (a_queue, audiosink);
881   gst_bin_add (GST_BIN (audio_bin), a_decoder);
882   gst_bin_add (GST_BIN (audio_bin), a_queue);
883   gst_bin_add (GST_BIN (audio_bin), audiosink);
884
885   setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
886           "sink"), audio_bin);
887
888   seekable = gst_element_get_static_pad (a_queue, "src");
889   seekable_pads = g_list_prepend (seekable_pads, seekable);
890   rate_pads = g_list_prepend (rate_pads, seekable);
891   rate_pads =
892       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
893           "sink"));
894
895   video_bin = gst_bin_new ("v_decoder_bin");
896   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
897   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
898   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
899   gst_element_link_many (v_decoder, v_filter, videosink, NULL);
900
901   gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL);
902
903   setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
904           "sink"), video_bin);
905
906   seekable = gst_element_get_static_pad (v_decoder, "src");
907   seekable_pads = g_list_prepend (seekable_pads, seekable);
908   rate_pads = g_list_prepend (rate_pads, seekable);
909   rate_pads =
910       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
911           "sink"));
912
913   return pipeline;
914 }
915
916 static void
917 playerbin_set_uri (GstElement * player, const gchar * location)
918 {
919   gchar *uri;
920
921   /* Add "file://" prefix for convenience */
922   if (g_str_has_prefix (location, "/")) {
923     uri = g_strconcat ("file://", location, NULL);
924     g_object_set (G_OBJECT (player), "uri", uri, NULL);
925     g_free (uri);
926   } else {
927     g_object_set (G_OBJECT (player), "uri", location, NULL);
928   }
929 }
930
931 static GstElement *
932 construct_playerbin (const gchar * name, const gchar * location)
933 {
934   GstElement *player;
935
936   player = gst_element_factory_make (name, "player");
937   g_assert (player);
938
939   playerbin_set_uri (player, location);
940
941   seekable_elements = g_list_prepend (seekable_elements, player);
942
943   /* force element seeking on this pipeline */
944   elem_seek = TRUE;
945
946   return player;
947 }
948
949 static GstElement *
950 make_playerbin_pipeline (const gchar * location)
951 {
952   return construct_playerbin ("playbin", location);
953 }
954
955 static GstElement *
956 make_playerbin2_pipeline (const gchar * location)
957 {
958   GstElement *pipeline = construct_playerbin ("playbin2", location);
959
960   /* FIXME: this is not triggered, playbin2 is not forwarding it from the sink */
961   g_signal_connect (pipeline, "notify::volume", G_CALLBACK (volume_notify_cb),
962       NULL);
963   return pipeline;
964 }
965
966 #ifndef GST_DISABLE_PARSE
967 static GstElement *
968 make_parselaunch_pipeline (const gchar * description)
969 {
970   GstElement *pipeline;
971   GError *error = NULL;
972
973   pipeline = gst_parse_launch (description, &error);
974
975   seekable_elements = g_list_prepend (seekable_elements, pipeline);
976
977   elem_seek = TRUE;
978
979   return pipeline;
980 }
981 #endif
982
983 typedef struct
984 {
985   const gchar *name;
986   GstElement *(*func) (const gchar * location);
987 }
988 Pipeline;
989
990 static Pipeline pipelines[] = {
991   {"mp3", make_mp3_pipeline},
992   {"avi", make_avi_pipeline},
993   {"mpeg1", make_mpeg_pipeline},
994   {"mpegparse", make_parse_pipeline},
995   {"vorbis", make_vorbis_pipeline},
996   {"theora", make_theora_pipeline},
997   {"ogg/v/t", make_vorbis_theora_pipeline},
998   {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline},
999   {"sid", make_sid_pipeline},
1000   {"flac", make_flac_pipeline},
1001   {"wav", make_wav_pipeline},
1002   {"mod", make_mod_pipeline},
1003   {"dv", make_dv_pipeline},
1004   {"mpeg1nothreads", make_mpegnt_pipeline},
1005   {"playerbin", make_playerbin_pipeline},
1006 #ifndef GST_DISABLE_PARSE
1007   {"parse-launch", make_parselaunch_pipeline},
1008 #endif
1009   {"playerbin2", make_playerbin2_pipeline},
1010   {NULL, NULL},
1011 };
1012
1013 #define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
1014
1015 /* ui callbacks and helpers */
1016
1017 static gchar *
1018 format_value (GtkScale * scale, gdouble value)
1019 {
1020   gint64 real;
1021   gint64 seconds;
1022   gint64 subseconds;
1023
1024   real = value * duration / 100;
1025   seconds = (gint64) real / GST_SECOND;
1026   subseconds = (gint64) real / (GST_SECOND / 100);
1027
1028   return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
1029       G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
1030 }
1031
1032
1033 static gchar *
1034 shuttle_format_value (GtkScale * scale, gdouble value)
1035 {
1036   return g_strdup_printf ("%0.*g", gtk_scale_get_digits (scale), value);
1037 }
1038
1039 typedef struct
1040 {
1041   const gchar *name;
1042   const GstFormat format;
1043 }
1044 seek_format;
1045
1046 static seek_format seek_formats[] = {
1047   {"tim", GST_FORMAT_TIME},
1048   {"byt", GST_FORMAT_BYTES},
1049   {"buf", GST_FORMAT_BUFFERS},
1050   {"def", GST_FORMAT_DEFAULT},
1051   {NULL, 0},
1052 };
1053
1054 G_GNUC_UNUSED static void
1055 query_rates (void)
1056 {
1057   GList *walk = rate_pads;
1058
1059   while (walk) {
1060     GstPad *pad = GST_PAD (walk->data);
1061     gint i = 0;
1062
1063     g_print ("rate/sec  %8.8s: ", GST_PAD_NAME (pad));
1064     while (seek_formats[i].name) {
1065       gint64 value;
1066       GstFormat format;
1067
1068       format = seek_formats[i].format;
1069
1070       if (gst_pad_query_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format,
1071               &value)) {
1072         g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value);
1073       } else {
1074         g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
1075       }
1076
1077       i++;
1078     }
1079     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1080
1081     walk = g_list_next (walk);
1082   }
1083 }
1084
1085 G_GNUC_UNUSED static void
1086 query_positions_elems (void)
1087 {
1088   GList *walk = seekable_elements;
1089
1090   while (walk) {
1091     GstElement *element = GST_ELEMENT (walk->data);
1092     gint i = 0;
1093
1094     g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
1095     while (seek_formats[i].name) {
1096       gint64 position, total;
1097       GstFormat format;
1098
1099       format = seek_formats[i].format;
1100
1101       if (gst_element_query_position (element, &format, &position) &&
1102           gst_element_query_duration (element, &format, &total)) {
1103         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1104             seek_formats[i].name, position, total);
1105       } else {
1106         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1107             "*NA*");
1108       }
1109       i++;
1110     }
1111     g_print (" %s\n", GST_ELEMENT_NAME (element));
1112
1113     walk = g_list_next (walk);
1114   }
1115 }
1116
1117 G_GNUC_UNUSED static void
1118 query_positions_pads (void)
1119 {
1120   GList *walk = seekable_pads;
1121
1122   while (walk) {
1123     GstPad *pad = GST_PAD (walk->data);
1124     gint i = 0;
1125
1126     g_print ("positions %8.8s: ", GST_PAD_NAME (pad));
1127     while (seek_formats[i].name) {
1128       GstFormat format;
1129       gint64 position, total;
1130
1131       format = seek_formats[i].format;
1132
1133       if (gst_pad_query_position (pad, &format, &position) &&
1134           gst_pad_query_duration (pad, &format, &total)) {
1135         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1136             seek_formats[i].name, position, total);
1137       } else {
1138         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1139             "*NA*");
1140       }
1141
1142       i++;
1143     }
1144     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1145
1146     walk = g_list_next (walk);
1147   }
1148 }
1149
1150 static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
1151     gpointer user_data);
1152 static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
1153     gpointer user_data);
1154 static void seek_cb (GtkWidget * widget);
1155
1156 static void
1157 set_scale (gdouble value)
1158 {
1159   g_signal_handlers_block_by_func (hscale, (void *) start_seek,
1160       (void *) pipeline);
1161   g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
1162       (void *) pipeline);
1163   g_signal_handlers_block_by_func (hscale, (void *) seek_cb, (void *) pipeline);
1164   gtk_adjustment_set_value (adjustment, value);
1165   g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
1166       (void *) pipeline);
1167   g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
1168       (void *) pipeline);
1169   g_signal_handlers_unblock_by_func (hscale, (void *) seek_cb,
1170       (void *) pipeline);
1171   gtk_widget_queue_draw (hscale);
1172 }
1173
1174 static gboolean
1175 update_fill (gpointer data)
1176 {
1177   if (elem_seek) {
1178     if (seekable_elements) {
1179       GstElement *element = GST_ELEMENT (seekable_elements->data);
1180       GstQuery *query;
1181
1182       query = gst_query_new_buffering (GST_FORMAT_PERCENT);
1183       if (gst_element_query (element, query)) {
1184         gint64 start, stop, buffering_total;
1185         GstFormat format;
1186         gdouble fill;
1187         gboolean busy;
1188         gint percent;
1189         GstBufferingMode mode;
1190         gint avg_in, avg_out;
1191         gint64 buffering_left;
1192
1193         gst_query_parse_buffering_percent (query, &busy, &percent);
1194         gst_query_parse_buffering_range (query, &format, &start, &stop,
1195             &buffering_total);
1196         gst_query_parse_buffering_stats (query, &mode, &avg_in, &avg_out,
1197             &buffering_left);
1198
1199         /* note that we could start the playback when buffering_left < remaining
1200          * playback time */
1201         GST_DEBUG ("buffering total %" G_GINT64_FORMAT " ms, left %"
1202             G_GINT64_FORMAT " ms", buffering_total, buffering_left);
1203         GST_DEBUG ("start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT,
1204             start, stop);
1205
1206         if (stop != -1)
1207           fill = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1208         else
1209           fill = 100.0;
1210
1211         gtk_range_set_fill_level (GTK_RANGE (hscale), fill);
1212       }
1213       gst_query_unref (query);
1214     }
1215   }
1216   return TRUE;
1217 }
1218
1219 static gboolean
1220 update_scale (gpointer data)
1221 {
1222   GstFormat format = GST_FORMAT_TIME;
1223
1224   //position = 0;
1225   //duration = 0;
1226
1227   if (elem_seek) {
1228     if (seekable_elements) {
1229       GstElement *element = GST_ELEMENT (seekable_elements->data);
1230
1231       gst_element_query_position (element, &format, &position);
1232       gst_element_query_duration (element, &format, &duration);
1233     }
1234   } else {
1235     if (seekable_pads) {
1236       GstPad *pad = GST_PAD (seekable_pads->data);
1237
1238       gst_pad_query_position (pad, &format, &position);
1239       gst_pad_query_duration (pad, &format, &duration);
1240     }
1241   }
1242
1243   if (stats) {
1244     if (elem_seek) {
1245       query_positions_elems ();
1246     } else {
1247       query_positions_pads ();
1248     }
1249     query_rates ();
1250   }
1251
1252   if (position >= duration)
1253     duration = position;
1254
1255   if (duration > 0) {
1256     set_scale (position * 100.0 / duration);
1257   }
1258
1259   /* FIXME: see make_playerbin2_pipeline() and volume_notify_cb() */
1260   if (pipeline_type == 16) {
1261     g_object_notify (G_OBJECT (pipeline), "volume");
1262   }
1263
1264   return TRUE;
1265 }
1266
1267 static void do_seek (GtkWidget * widget);
1268 static void connect_bus_signals (GstElement * pipeline);
1269 static void set_update_scale (gboolean active);
1270 static void set_update_fill (gboolean active);
1271
1272 static gboolean
1273 end_scrub (GtkWidget * widget)
1274 {
1275   GST_DEBUG ("end scrub, PAUSE");
1276   gst_element_set_state (pipeline, GST_STATE_PAUSED);
1277   seek_timeout_id = 0;
1278
1279   return FALSE;
1280 }
1281
1282 static gboolean
1283 send_event (GstEvent * event)
1284 {
1285   gboolean res = FALSE;
1286
1287   if (!elem_seek) {
1288     GList *walk = seekable_pads;
1289
1290     while (walk) {
1291       GstPad *seekable = GST_PAD (walk->data);
1292
1293       GST_DEBUG ("send event on pad %s:%s", GST_DEBUG_PAD_NAME (seekable));
1294
1295       gst_event_ref (event);
1296       res = gst_pad_send_event (seekable, event);
1297
1298       walk = g_list_next (walk);
1299     }
1300   } else {
1301     GList *walk = seekable_elements;
1302
1303     while (walk) {
1304       GstElement *seekable = GST_ELEMENT (walk->data);
1305
1306       GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
1307
1308       gst_event_ref (event);
1309       res = gst_element_send_event (seekable, event);
1310
1311       walk = g_list_next (walk);
1312     }
1313   }
1314   gst_event_unref (event);
1315   return res;
1316 }
1317
1318 static void
1319 do_seek (GtkWidget * widget)
1320 {
1321   gint64 real;
1322   gboolean res = FALSE;
1323   GstEvent *s_event;
1324   GstSeekFlags flags;
1325
1326   real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100;
1327
1328   flags = 0;
1329   if (flush_seek)
1330     flags |= GST_SEEK_FLAG_FLUSH;
1331   if (accurate_seek)
1332     flags |= GST_SEEK_FLAG_ACCURATE;
1333   if (keyframe_seek)
1334     flags |= GST_SEEK_FLAG_KEY_UNIT;
1335   if (loop_seek)
1336     flags |= GST_SEEK_FLAG_SEGMENT;
1337   if (skip_seek)
1338     flags |= GST_SEEK_FLAG_SKIP;
1339
1340   if (rate >= 0) {
1341     s_event = gst_event_new_seek (rate,
1342         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET,
1343         GST_CLOCK_TIME_NONE);
1344     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1345         rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
1346   } else {
1347     s_event = gst_event_new_seek (rate,
1348         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1349         GST_SEEK_TYPE_SET, real);
1350     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1351         rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
1352   }
1353
1354   res = send_event (s_event);
1355
1356   if (res) {
1357     if (flush_seek) {
1358       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1359     } else {
1360       set_update_scale (TRUE);
1361     }
1362   } else {
1363     g_print ("seek failed\n");
1364     set_update_scale (TRUE);
1365   }
1366 }
1367
1368 static void
1369 seek_cb (GtkWidget * widget)
1370 {
1371   /* If the timer hasn't expired yet, then the pipeline is running */
1372   if (play_scrub && seek_timeout_id != 0) {
1373     GST_DEBUG ("do scrub seek, PAUSED");
1374     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1375   }
1376
1377   GST_DEBUG ("do seek");
1378   do_seek (widget);
1379
1380   if (play_scrub) {
1381     GST_DEBUG ("do scrub seek, PLAYING");
1382     gst_element_set_state (pipeline, GST_STATE_PLAYING);
1383
1384     if (seek_timeout_id == 0) {
1385       seek_timeout_id =
1386           g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
1387     }
1388   }
1389 }
1390
1391 static void
1392 set_update_fill (gboolean active)
1393 {
1394   GST_DEBUG ("fill scale is %d", active);
1395
1396   if (active) {
1397     if (fill_id == 0) {
1398       fill_id =
1399           g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline);
1400     }
1401   } else {
1402     if (fill_id) {
1403       g_source_remove (fill_id);
1404       fill_id = 0;
1405     }
1406   }
1407 }
1408
1409 static void
1410 set_update_scale (gboolean active)
1411 {
1412
1413   GST_DEBUG ("update scale is %d", active);
1414
1415   if (active) {
1416     if (update_id == 0) {
1417       update_id =
1418           g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline);
1419     }
1420   } else {
1421     if (update_id) {
1422       g_source_remove (update_id);
1423       update_id = 0;
1424     }
1425   }
1426 }
1427
1428 static gboolean
1429 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1430 {
1431   if (event->type != GDK_BUTTON_PRESS)
1432     return FALSE;
1433
1434   set_update_scale (FALSE);
1435
1436   if (state == GST_STATE_PLAYING && flush_seek && scrub) {
1437     GST_DEBUG ("start scrub seek, PAUSE");
1438     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1439   }
1440
1441   if (changed_id == 0 && flush_seek && scrub) {
1442     changed_id =
1443         g_signal_connect (hscale, "value_changed", G_CALLBACK (seek_cb),
1444         pipeline);
1445   }
1446
1447   return FALSE;
1448 }
1449
1450 static gboolean
1451 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1452 {
1453   if (changed_id) {
1454     g_signal_handler_disconnect (hscale, changed_id);
1455     changed_id = 0;
1456   }
1457
1458   if (!flush_seek || !scrub) {
1459     GST_DEBUG ("do final seek");
1460     do_seek (widget);
1461   }
1462
1463   if (seek_timeout_id != 0) {
1464     g_source_remove (seek_timeout_id);
1465     seek_timeout_id = 0;
1466     /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
1467      * instead. */
1468     if (state == GST_STATE_PAUSED) {
1469       GST_DEBUG ("stop scrub seek, PAUSED");
1470       gst_element_set_state (pipeline, GST_STATE_PAUSED);
1471     }
1472   } else {
1473     if (state == GST_STATE_PLAYING) {
1474       GST_DEBUG ("stop scrub seek, PLAYING");
1475       gst_element_set_state (pipeline, GST_STATE_PLAYING);
1476     }
1477   }
1478
1479   return FALSE;
1480 }
1481
1482 static void
1483 play_cb (GtkButton * button, gpointer data)
1484 {
1485   GstStateChangeReturn ret;
1486
1487   if (state != GST_STATE_PLAYING) {
1488     g_print ("PLAY pipeline\n");
1489     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1490
1491     ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
1492     switch (ret) {
1493       case GST_STATE_CHANGE_FAILURE:
1494         goto failed;
1495       case GST_STATE_CHANGE_NO_PREROLL:
1496         is_live = TRUE;
1497         break;
1498       default:
1499         break;
1500     }
1501     state = GST_STATE_PLAYING;
1502     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
1503   }
1504
1505   return;
1506
1507 failed:
1508   {
1509     g_print ("PLAY failed\n");
1510     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
1511   }
1512 }
1513
1514 static void
1515 pause_cb (GtkButton * button, gpointer data)
1516 {
1517   g_static_mutex_lock (&state_mutex);
1518   if (state != GST_STATE_PAUSED) {
1519     GstStateChangeReturn ret;
1520
1521     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1522     g_print ("PAUSE pipeline\n");
1523     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1524     switch (ret) {
1525       case GST_STATE_CHANGE_FAILURE:
1526         goto failed;
1527       case GST_STATE_CHANGE_NO_PREROLL:
1528         is_live = TRUE;
1529         break;
1530       default:
1531         break;
1532     }
1533
1534     state = GST_STATE_PAUSED;
1535     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
1536   }
1537   g_static_mutex_unlock (&state_mutex);
1538
1539   return;
1540
1541 failed:
1542   {
1543     g_static_mutex_unlock (&state_mutex);
1544     g_print ("PAUSE failed\n");
1545     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
1546   }
1547 }
1548
1549 static void
1550 stop_cb (GtkButton * button, gpointer data)
1551 {
1552   if (state != STOP_STATE) {
1553     GstStateChangeReturn ret;
1554
1555     g_print ("READY pipeline\n");
1556     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1557
1558     g_static_mutex_lock (&state_mutex);
1559     ret = gst_element_set_state (pipeline, STOP_STATE);
1560     if (ret == GST_STATE_CHANGE_FAILURE)
1561       goto failed;
1562
1563     state = STOP_STATE;
1564     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
1565     gtk_widget_queue_draw (video_window);
1566
1567     is_live = FALSE;
1568     buffering = FALSE;
1569     set_update_scale (FALSE);
1570     set_scale (0.0);
1571     set_update_fill (FALSE);
1572
1573     if (pipeline_type == 16)
1574       clear_streams (pipeline);
1575     g_static_mutex_unlock (&state_mutex);
1576
1577 #if 0
1578     /* if one uses parse_launch, play, stop and play again it fails as all the
1579      * pads after the demuxer can't be reconnected
1580      */
1581     if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
1582       gst_element_set_state (pipeline, GST_STATE_NULL);
1583       gst_object_unref (pipeline);
1584
1585       g_list_free (seekable_elements);
1586       seekable_elements = NULL;
1587       g_list_free (seekable_pads);
1588       seekable_pads = NULL;
1589       g_list_free (rate_pads);
1590       rate_pads = NULL;
1591
1592       pipeline = pipelines[pipeline_type].func (pipeline_spec);
1593       g_assert (pipeline);
1594       gst_element_set_state (pipeline, STOP_STATE);
1595       connect_bus_signals (pipeline);
1596     }
1597 #endif
1598   }
1599   return;
1600
1601 failed:
1602   {
1603     g_static_mutex_unlock (&state_mutex);
1604     g_print ("STOP failed\n");
1605     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
1606   }
1607 }
1608
1609 static void
1610 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1611 {
1612   accurate_seek = gtk_toggle_button_get_active (button);
1613 }
1614
1615 static void
1616 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1617 {
1618   keyframe_seek = gtk_toggle_button_get_active (button);
1619 }
1620
1621 static void
1622 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1623 {
1624   loop_seek = gtk_toggle_button_get_active (button);
1625   if (state == GST_STATE_PLAYING) {
1626     do_seek (hscale);
1627   }
1628 }
1629
1630 static void
1631 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1632 {
1633   flush_seek = gtk_toggle_button_get_active (button);
1634 }
1635
1636 static void
1637 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1638 {
1639   scrub = gtk_toggle_button_get_active (button);
1640 }
1641
1642 static void
1643 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1644 {
1645   play_scrub = gtk_toggle_button_get_active (button);
1646 }
1647
1648 static void
1649 skip_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1650 {
1651   skip_seek = gtk_toggle_button_get_active (button);
1652   if (state == GST_STATE_PLAYING) {
1653     do_seek (hscale);
1654   }
1655 }
1656
1657 static void
1658 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1659 {
1660   gboolean res = FALSE;
1661   GstEvent *s_event;
1662   GstSeekFlags flags;
1663
1664   rate = gtk_spin_button_get_value (button);
1665
1666   GST_DEBUG ("rate changed to %lf", rate);
1667
1668   flags = 0;
1669   if (flush_seek)
1670     flags |= GST_SEEK_FLAG_FLUSH;
1671   if (loop_seek)
1672     flags |= GST_SEEK_FLAG_SEGMENT;
1673   if (accurate_seek)
1674     flags |= GST_SEEK_FLAG_ACCURATE;
1675   if (keyframe_seek)
1676     flags |= GST_SEEK_FLAG_KEY_UNIT;
1677   if (skip_seek)
1678     flags |= GST_SEEK_FLAG_SKIP;
1679
1680   if (rate >= 0.0) {
1681     s_event = gst_event_new_seek (rate,
1682         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1683         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1684   } else {
1685     s_event = gst_event_new_seek (rate,
1686         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1687         GST_SEEK_TYPE_SET, position);
1688   }
1689
1690   res = send_event (s_event);
1691
1692   if (res) {
1693     if (flush_seek) {
1694       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1695     }
1696   } else
1697     g_print ("seek failed\n");
1698 }
1699
1700 static void
1701 update_flag (GstPipeline * pipeline, gint num, gboolean state)
1702 {
1703   gint flags;
1704
1705   g_object_get (pipeline, "flags", &flags, NULL);
1706   if (state)
1707     flags |= (1 << num);
1708   else
1709     flags &= ~(1 << num);
1710   g_object_set (pipeline, "flags", flags, NULL);
1711 }
1712
1713 static void
1714 vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1715 {
1716   gboolean state;
1717
1718   state = gtk_toggle_button_get_active (button);
1719   update_flag (pipeline, 3, state);
1720   gtk_widget_set_sensitive (vis_combo, state);
1721 }
1722
1723 static void
1724 audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1725 {
1726   gboolean state;
1727
1728   state = gtk_toggle_button_get_active (button);
1729   update_flag (pipeline, 1, state);
1730   gtk_widget_set_sensitive (audio_combo, state);
1731 }
1732
1733 static void
1734 video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1735 {
1736   gboolean state;
1737
1738   state = gtk_toggle_button_get_active (button);
1739   update_flag (pipeline, 0, state);
1740   gtk_widget_set_sensitive (video_combo, state);
1741 }
1742
1743 static void
1744 text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1745 {
1746   gboolean state;
1747
1748   state = gtk_toggle_button_get_active (button);
1749   update_flag (pipeline, 2, state);
1750   gtk_widget_set_sensitive (text_combo, state);
1751 }
1752
1753 static void
1754 mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1755 {
1756   gboolean mute;
1757
1758   mute = gtk_toggle_button_get_active (button);
1759   g_object_set (pipeline, "mute", mute, NULL);
1760 }
1761
1762 static void
1763 download_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1764 {
1765   gboolean state;
1766
1767   state = gtk_toggle_button_get_active (button);
1768   update_flag (pipeline, 7, state);
1769 }
1770
1771 static void
1772 buffer_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1773 {
1774   gboolean state;
1775
1776   state = gtk_toggle_button_get_active (button);
1777   update_flag (pipeline, 8, state);
1778 }
1779
1780 static void
1781 clear_streams (GstElement * pipeline)
1782 {
1783   gint i;
1784
1785   /* remove previous info */
1786   for (i = 0; i < n_video; i++)
1787     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (video_combo), 0);
1788   for (i = 0; i < n_audio; i++)
1789     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (audio_combo), 0);
1790   for (i = 0; i < n_text; i++)
1791     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (text_combo), 0);
1792
1793   n_audio = n_video = n_text = 0;
1794   gtk_widget_set_sensitive (video_combo, FALSE);
1795   gtk_widget_set_sensitive (audio_combo, FALSE);
1796   gtk_widget_set_sensitive (text_combo, FALSE);
1797
1798   need_streams = TRUE;
1799 }
1800
1801 static void
1802 update_streams (GstPipeline * pipeline)
1803 {
1804   gint i;
1805
1806   if (pipeline_type == 16 && need_streams) {
1807     GstTagList *tags;
1808     gchar *name, *str;
1809     gint active_idx;
1810     gboolean state;
1811
1812     /* remove previous info */
1813     clear_streams (GST_ELEMENT_CAST (pipeline));
1814
1815     /* here we get and update the different streams detected by playbin2 */
1816     g_object_get (pipeline, "n-video", &n_video, NULL);
1817     g_object_get (pipeline, "n-audio", &n_audio, NULL);
1818     g_object_get (pipeline, "n-text", &n_text, NULL);
1819
1820     g_print ("video %d, audio %d, text %d\n", n_video, n_audio, n_text);
1821
1822     active_idx = 0;
1823     for (i = 0; i < n_video; i++) {
1824       g_signal_emit_by_name (pipeline, "get-video-tags", i, &tags);
1825       if (tags) {
1826         str = gst_structure_to_string ((GstStructure *) tags);
1827         g_print ("video %d: %s\n", i, str);
1828         g_free (str);
1829       }
1830       /* find good name for the label */
1831       name = g_strdup_printf ("video %d", i + 1);
1832       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (video_combo), name);
1833       g_free (name);
1834     }
1835     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (video_checkbox));
1836     gtk_widget_set_sensitive (video_combo, state && n_video > 0);
1837     gtk_combo_box_set_active (GTK_COMBO_BOX (video_combo), active_idx);
1838
1839     active_idx = 0;
1840     for (i = 0; i < n_audio; i++) {
1841       g_signal_emit_by_name (pipeline, "get-audio-tags", i, &tags);
1842       if (tags) {
1843         str = gst_structure_to_string ((GstStructure *) tags);
1844         g_print ("audio %d: %s\n", i, str);
1845         g_free (str);
1846       }
1847       /* find good name for the label */
1848       name = g_strdup_printf ("audio %d", i + 1);
1849       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (audio_combo), name);
1850       g_free (name);
1851     }
1852     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (audio_checkbox));
1853     gtk_widget_set_sensitive (audio_combo, state && n_audio > 0);
1854     gtk_combo_box_set_active (GTK_COMBO_BOX (audio_combo), active_idx);
1855
1856     active_idx = 0;
1857     for (i = 0; i < n_text; i++) {
1858       g_signal_emit_by_name (pipeline, "get-text-tags", i, &tags);
1859
1860       name = NULL;
1861       if (tags) {
1862         const GValue *value;
1863
1864         str = gst_structure_to_string ((GstStructure *) tags);
1865         g_print ("text %d: %s\n", i, str);
1866         g_free (str);
1867
1868         /* get the language code if we can */
1869         value = gst_tag_list_get_value_index (tags, GST_TAG_LANGUAGE_CODE, 0);
1870         if (value && G_VALUE_HOLDS_STRING (value)) {
1871           name = g_strdup_printf ("text %s", g_value_get_string (value));
1872         }
1873       }
1874       /* find good name for the label if we didn't use a tag */
1875       if (name == NULL)
1876         name = g_strdup_printf ("text %d", i + 1);
1877
1878       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (text_combo), name);
1879       g_free (name);
1880     }
1881     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_checkbox));
1882     gtk_widget_set_sensitive (text_combo, state && n_text > 0);
1883     gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), active_idx);
1884
1885     need_streams = FALSE;
1886   }
1887 }
1888
1889 static void
1890 video_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1891 {
1892   gint active;
1893
1894   active = gtk_combo_box_get_active (combo);
1895
1896   g_print ("setting current video track %d\n", active);
1897   g_object_set (pipeline, "current-video", active, NULL);
1898 }
1899
1900 static void
1901 audio_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1902 {
1903   gint active;
1904
1905   active = gtk_combo_box_get_active (combo);
1906
1907   g_print ("setting current audio track %d\n", active);
1908   g_object_set (pipeline, "current-audio", active, NULL);
1909 }
1910
1911 static void
1912 text_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1913 {
1914   gint active;
1915
1916   active = gtk_combo_box_get_active (combo);
1917
1918   g_print ("setting current text track %d\n", active);
1919   g_object_set (pipeline, "current-text", active, NULL);
1920 }
1921
1922 static gboolean
1923 filter_features (GstPluginFeature * feature, gpointer data)
1924 {
1925   GstElementFactory *f;
1926
1927   if (!GST_IS_ELEMENT_FACTORY (feature))
1928     return FALSE;
1929   f = GST_ELEMENT_FACTORY (feature);
1930   if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
1931     return FALSE;
1932
1933   return TRUE;
1934 }
1935
1936 static void
1937 init_visualization_features (void)
1938 {
1939   GList *list, *walk;
1940
1941   vis_entries = g_array_new (FALSE, FALSE, sizeof (VisEntry));
1942
1943   list = gst_registry_feature_filter (gst_registry_get_default (),
1944       filter_features, FALSE, NULL);
1945
1946   for (walk = list; walk; walk = g_list_next (walk)) {
1947     VisEntry entry;
1948     const gchar *name;
1949
1950     entry.factory = GST_ELEMENT_FACTORY (walk->data);
1951     name = gst_element_factory_get_longname (entry.factory);
1952
1953     g_array_append_val (vis_entries, entry);
1954     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (vis_combo), name);
1955   }
1956   gtk_combo_box_set_active (GTK_COMBO_BOX (vis_combo), 0);
1957 }
1958
1959 static void
1960 vis_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1961 {
1962   guint index;
1963   VisEntry *entry;
1964   GstElement *element;
1965
1966   /* get the selected index and get the factory for this index */
1967   index = gtk_combo_box_get_active (GTK_COMBO_BOX (vis_combo));
1968   if (vis_entries->len > 0) {
1969     entry = &g_array_index (vis_entries, VisEntry, index);
1970
1971     /* create an instance of the element from the factory */
1972     element = gst_element_factory_create (entry->factory, NULL);
1973     if (!element)
1974       return;
1975
1976     /* set vis plugin for playbin2 */
1977     g_object_set (pipeline, "vis-plugin", element, NULL);
1978   }
1979 }
1980
1981 static void
1982 volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1983 {
1984   gdouble volume;
1985
1986   volume = gtk_spin_button_get_value (button);
1987
1988   g_object_set (pipeline, "volume", volume, NULL);
1989 }
1990
1991 static void
1992 volume_notify_cb (GstElement * pipeline, GParamSpec * arg, gpointer user_dat)
1993 {
1994   gdouble cur_volume, new_volume;
1995
1996   g_object_get (pipeline, "volume", &new_volume, NULL);
1997   cur_volume = gtk_spin_button_get_value (GTK_SPIN_BUTTON (volume_spinbutton));
1998   if (fabs (cur_volume - new_volume) > 0.001) {
1999     g_signal_handlers_block_by_func (volume_spinbutton,
2000         volume_spinbutton_changed_cb, pipeline);
2001     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), new_volume);
2002     g_signal_handlers_unblock_by_func (volume_spinbutton,
2003         volume_spinbutton_changed_cb, pipeline);
2004   }
2005 }
2006
2007 static void
2008 shot_cb (GtkButton * button, gpointer data)
2009 {
2010   GstBuffer *buffer;
2011   GstCaps *caps;
2012
2013   /* convert to our desired format (RGB24) */
2014   caps = gst_caps_new_simple ("video/x-raw-rgb",
2015       "bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
2016       /* Note: we don't ask for a specific width/height here, so that
2017        * videoscale can adjust dimensions from a non-1/1 pixel aspect
2018        * ratio to a 1/1 pixel-aspect-ratio */
2019       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
2020       "endianness", G_TYPE_INT, G_BIG_ENDIAN,
2021       "red_mask", G_TYPE_INT, 0xff0000,
2022       "green_mask", G_TYPE_INT, 0x00ff00,
2023       "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
2024
2025   /* convert the latest frame to the requested format */
2026   g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
2027   gst_caps_unref (caps);
2028
2029   if (buffer) {
2030     GstCaps *caps;
2031     GstStructure *s;
2032     gboolean res;
2033     gint width, height;
2034     GdkPixbuf *pixbuf;
2035     GError *error = NULL;
2036
2037     /* get the snapshot buffer format now. We set the caps on the appsink so
2038      * that it can only be an rgb buffer. The only thing we have not specified
2039      * on the caps is the height, which is dependant on the pixel-aspect-ratio
2040      * of the source material */
2041     caps = GST_BUFFER_CAPS (buffer);
2042     if (!caps) {
2043       g_warning ("could not get snapshot format\n");
2044       goto done;
2045     }
2046     s = gst_caps_get_structure (caps, 0);
2047
2048     /* we need to get the final caps on the buffer to get the size */
2049     res = gst_structure_get_int (s, "width", &width);
2050     res |= gst_structure_get_int (s, "height", &height);
2051     if (!res) {
2052       g_warning ("could not get snapshot dimension\n");
2053       goto done;
2054     }
2055
2056     /* create pixmap from buffer and save, gstreamer video buffers have a stride
2057      * that is rounded up to the nearest multiple of 4 */
2058     pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
2059         GDK_COLORSPACE_RGB, FALSE, 8, width, height,
2060         GST_ROUND_UP_4 (width * 3), NULL, NULL);
2061
2062     /* save the pixbuf */
2063     gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
2064
2065   done:
2066     gst_buffer_unref (buffer);
2067   }
2068 }
2069
2070 /* called when the Step button is pressed */
2071 static void
2072 step_cb (GtkButton * button, gpointer data)
2073 {
2074   GstEvent *event;
2075   GstFormat format;
2076   guint64 amount;
2077   gdouble rate;
2078   gboolean flush, res;
2079   gint active;
2080
2081   active = gtk_combo_box_get_active (GTK_COMBO_BOX (format_combo));
2082   amount =
2083       gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON
2084       (step_amount_spinbutton));
2085   rate = gtk_spin_button_get_value (GTK_SPIN_BUTTON (step_rate_spinbutton));
2086   flush = TRUE;
2087
2088   switch (active) {
2089     case 0:
2090       format = GST_FORMAT_BUFFERS;
2091       break;
2092     case 1:
2093       format = GST_FORMAT_TIME;
2094       amount *= GST_MSECOND;
2095       break;
2096     default:
2097       format = GST_FORMAT_UNDEFINED;
2098       break;
2099   }
2100
2101   event = gst_event_new_step (format, amount, rate, flush, FALSE);
2102
2103   res = send_event (event);
2104
2105   if (!res) {
2106     g_print ("Sending step event failed\n");
2107   }
2108 }
2109
2110 static void
2111 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2112 {
2113   const GstStructure *s;
2114
2115   s = gst_message_get_structure (message);
2116   g_print ("message from \"%s\" (%s): ",
2117       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
2118       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2119   if (s) {
2120     gchar *sstr;
2121
2122     sstr = gst_structure_to_string (s);
2123     g_print ("%s\n", sstr);
2124     g_free (sstr);
2125   } else {
2126     g_print ("no message details\n");
2127   }
2128 }
2129
2130 static gboolean shuttling = FALSE;
2131 static gdouble shuttle_rate = 0.0;
2132 static gdouble play_rate = 1.0;
2133
2134 static void
2135 do_shuttle (GstElement * element)
2136 {
2137   guint64 duration;
2138
2139   if (shuttling)
2140     duration = 40 * GST_MSECOND;
2141   else
2142     duration = -1;
2143
2144   gst_element_send_event (element,
2145       gst_event_new_step (GST_FORMAT_TIME, duration, shuttle_rate, FALSE,
2146           FALSE));
2147 }
2148
2149 static void
2150 msg_sync_step_done (GstBus * bus, GstMessage * message, GstElement * element)
2151 {
2152   GstFormat format;
2153   guint64 amount;
2154   gdouble rate;
2155   gboolean flush;
2156   gboolean intermediate;
2157   guint64 duration;
2158   gboolean eos;
2159
2160   gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
2161       &intermediate, &duration, &eos);
2162
2163   if (eos) {
2164     g_print ("stepped till EOS\n");
2165     return;
2166   }
2167
2168   if (g_static_mutex_trylock (&state_mutex)) {
2169     if (shuttling)
2170       do_shuttle (element);
2171     g_static_mutex_unlock (&state_mutex);
2172   } else {
2173     /* ignore step messages that come while we are doing a state change */
2174     g_print ("state change is busy\n");
2175   }
2176 }
2177
2178 static void
2179 shuttle_toggled (GtkToggleButton * button, GstElement * element)
2180 {
2181   gboolean active;
2182
2183   active = gtk_toggle_button_get_active (button);
2184
2185   if (active != shuttling) {
2186     shuttling = active;
2187     g_print ("shuttling %s\n", shuttling ? "active" : "inactive");
2188     if (active) {
2189       shuttle_rate = 0.0;
2190       play_rate = 1.0;
2191       pause_cb (NULL, NULL);
2192       gst_element_get_state (element, NULL, NULL, -1);
2193     }
2194   }
2195 }
2196
2197 static void
2198 shuttle_rate_switch (GstElement * element)
2199 {
2200   GstSeekFlags flags;
2201   GstEvent *s_event;
2202   gboolean res;
2203
2204   if (state == GST_STATE_PLAYING) {
2205     /* pause when we need to */
2206     pause_cb (NULL, NULL);
2207     gst_element_get_state (element, NULL, NULL, -1);
2208   }
2209
2210   if (play_rate == 1.0)
2211     play_rate = -1.0;
2212   else
2213     play_rate = 1.0;
2214
2215   g_print ("rate changed to %lf %" GST_TIME_FORMAT "\n", play_rate,
2216       GST_TIME_ARGS (position));
2217
2218   flags = GST_SEEK_FLAG_FLUSH;
2219   flags |= GST_SEEK_FLAG_ACCURATE;
2220
2221   if (play_rate >= 0.0) {
2222     s_event = gst_event_new_seek (play_rate,
2223         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
2224         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
2225   } else {
2226     s_event = gst_event_new_seek (play_rate,
2227         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2228         GST_SEEK_TYPE_SET, position);
2229   }
2230   res = send_event (s_event);
2231   if (res) {
2232     gst_element_get_state (element, NULL, NULL, SEEK_TIMEOUT);
2233   } else {
2234     g_print ("seek failed\n");
2235   }
2236 }
2237
2238 static void
2239 shuttle_value_changed (GtkRange * range, GstElement * element)
2240 {
2241   gdouble rate;
2242
2243   rate = gtk_adjustment_get_value (shuttle_adjustment);
2244
2245   if (rate == 0.0) {
2246     g_print ("rate 0.0, pause\n");
2247     pause_cb (NULL, NULL);
2248     gst_element_get_state (element, NULL, NULL, -1);
2249   } else {
2250     g_print ("rate changed %0.3g\n", rate);
2251
2252     if ((rate < 0.0 && play_rate > 0.0) || (rate > 0.0 && play_rate < 0.0)) {
2253       shuttle_rate_switch (element);
2254     }
2255
2256     shuttle_rate = ABS (rate);
2257     if (state != GST_STATE_PLAYING) {
2258       do_shuttle (element);
2259       play_cb (NULL, NULL);
2260     }
2261   }
2262 }
2263
2264 static void
2265 msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2266 {
2267   GST_DEBUG ("async done");
2268   /* when we get ASYNC_DONE we can query position, duration and other
2269    * properties */
2270   update_scale (pipeline);
2271
2272   /* update the available streams */
2273   update_streams (pipeline);
2274 }
2275
2276 static void
2277 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2278 {
2279   const GstStructure *s;
2280
2281   s = gst_message_get_structure (message);
2282
2283   /* We only care about state changed on the pipeline */
2284   if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
2285     GstState old, new, pending;
2286
2287     gst_message_parse_state_changed (message, &old, &new, &pending);
2288
2289     /* When state of the pipeline changes to paused or playing we start updating scale */
2290     if (new == GST_STATE_PLAYING) {
2291       set_update_scale (TRUE);
2292     } else {
2293       set_update_scale (FALSE);
2294     }
2295   }
2296 }
2297
2298 static void
2299 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2300 {
2301   GstEvent *s_event;
2302   GstSeekFlags flags;
2303   gboolean res;
2304   GstFormat format;
2305
2306   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2307   gst_message_parse_segment_done (message, &format, &position);
2308   GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2309
2310   flags = 0;
2311   /* in the segment-done callback we never flush as this would not make sense
2312    * for seamless playback. */
2313   if (loop_seek)
2314     flags |= GST_SEEK_FLAG_SEGMENT;
2315   if (skip_seek)
2316     flags |= GST_SEEK_FLAG_SKIP;
2317
2318   s_event = gst_event_new_seek (rate,
2319       GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2320       GST_SEEK_TYPE_SET, duration);
2321
2322   GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
2323       rate, GST_TIME_ARGS (duration));
2324
2325   res = send_event (s_event);
2326   if (!res)
2327     g_print ("segment seek failed\n");
2328 }
2329
2330 /* in stream buffering mode we PAUSE the pipeline until we receive a 100%
2331  * message */
2332 static void
2333 do_stream_buffering (gint percent)
2334 {
2335   gchar *bufstr;
2336
2337   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2338   bufstr = g_strdup_printf ("Buffering...%d", percent);
2339   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2340   g_free (bufstr);
2341
2342   if (percent == 100) {
2343     /* a 100% message means buffering is done */
2344     buffering = FALSE;
2345     /* if the desired state is playing, go back */
2346     if (state == GST_STATE_PLAYING) {
2347       /* no state management needed for live pipelines */
2348       if (!is_live) {
2349         fprintf (stderr, "Done buffering, setting pipeline to PLAYING ...\n");
2350         gst_element_set_state (pipeline, GST_STATE_PLAYING);
2351       }
2352       gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2353       gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
2354     }
2355   } else {
2356     /* buffering busy */
2357     if (buffering == FALSE && state == GST_STATE_PLAYING) {
2358       /* we were not buffering but PLAYING, PAUSE  the pipeline. */
2359       if (!is_live) {
2360         fprintf (stderr, "Buffering, setting pipeline to PAUSED ...\n");
2361         gst_element_set_state (pipeline, GST_STATE_PAUSED);
2362       }
2363     }
2364     buffering = TRUE;
2365   }
2366 }
2367
2368 static void
2369 do_download_buffering (gint percent)
2370 {
2371   if (!buffering && percent < 100) {
2372     gchar *bufstr;
2373
2374     buffering = TRUE;
2375
2376     bufstr = g_strdup_printf ("Downloading...");
2377     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2378     g_free (bufstr);
2379
2380     /* once we get a buffering message, we'll do the fill update */
2381     set_update_fill (TRUE);
2382
2383     if (state == GST_STATE_PLAYING && !is_live) {
2384       fprintf (stderr, "Downloading, setting pipeline to PAUSED ...\n");
2385       gst_element_set_state (pipeline, GST_STATE_PAUSED);
2386       /* user has to manually start the playback */
2387       state = GST_STATE_PAUSED;
2388     }
2389   }
2390 }
2391
2392 static void
2393 msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data)
2394 {
2395   gint percent;
2396
2397   gst_message_parse_buffering (message, &percent);
2398
2399   /* get more stats */
2400   gst_message_parse_buffering_stats (message, &mode, NULL, NULL,
2401       &buffering_left);
2402
2403   switch (mode) {
2404     case GST_BUFFERING_DOWNLOAD:
2405       do_download_buffering (percent);
2406       break;
2407     case GST_BUFFERING_LIVE:
2408     case GST_BUFFERING_TIMESHIFT:
2409     case GST_BUFFERING_STREAM:
2410       do_stream_buffering (percent);
2411       break;
2412   }
2413 }
2414
2415 static void
2416 msg_clock_lost (GstBus * bus, GstMessage * message, GstPipeline * data)
2417 {
2418   g_print ("clock lost! PAUSE and PLAY to select a new clock\n");
2419
2420   gst_element_set_state (pipeline, GST_STATE_PAUSED);
2421   gst_element_set_state (pipeline, GST_STATE_PLAYING);
2422 }
2423
2424 #ifdef HAVE_X
2425
2426 static gulong embed_xid = 0;
2427
2428 /* We set the xid here in response to the prepare-xwindow-id message via a
2429  * bus sync handler because we don't know the actual videosink used from the
2430  * start (as we don't know the pipeline, or bin elements such as autovideosink
2431  * or gconfvideosink may be used which create the actual videosink only once
2432  * the pipeline is started) */
2433 static GstBusSyncReply
2434 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
2435 {
2436   if ((GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) &&
2437       gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
2438     GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (message));
2439
2440     g_print ("got prepare-xwindow-id, setting XID %lu\n", embed_xid);
2441
2442     if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
2443             "force-aspect-ratio")) {
2444       g_object_set (element, "force-aspect-ratio", TRUE, NULL);
2445     }
2446
2447     /* Should have been initialised from main thread before (can't use
2448      * GDK_WINDOW_XID here with Gtk+ >= 2.18, because the sync handler will
2449      * be called from a streaming thread and GDK_WINDOW_XID maps to more than
2450      * a simple structure lookup with Gtk+ >= 2.18, where 'more' is stuff that
2451      * shouldn't be done from a non-GUI thread without explicit locking).  */
2452     g_assert (embed_xid != 0);
2453
2454     gst_x_overlay_set_window_handle (GST_X_OVERLAY (element), embed_xid);
2455   }
2456   return GST_BUS_PASS;
2457 }
2458 #endif
2459
2460 static gboolean
2461 draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data)
2462 {
2463   if (state < GST_STATE_PAUSED) {
2464     int width, height;
2465
2466     width = gtk_widget_get_allocated_width (widget);
2467     height = gtk_widget_get_allocated_height (widget);
2468     cairo_set_source_rgb (cr, 0, 0, 0);
2469     cairo_rectangle (cr, 0, 0, width, height);
2470     cairo_fill (cr);
2471     return TRUE;
2472   }
2473   return FALSE;
2474 }
2475
2476 static void
2477 realize_cb (GtkWidget * widget, gpointer data)
2478 {
2479   GdkWindow *window = gtk_widget_get_window (widget);
2480
2481   /* This is here just for pedagogical purposes, GDK_WINDOW_XID will call it
2482    * as well */
2483   if (!gdk_window_ensure_native (window))
2484     g_error ("Couldn't create native window needed for GstXOverlay!");
2485
2486 #ifdef HAVE_X
2487   embed_xid = GDK_WINDOW_XID (window);
2488   g_print ("Window realize: video window XID = %lu\n", embed_xid);
2489 #endif
2490 }
2491
2492 static void
2493 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * data)
2494 {
2495   message_received (bus, message, data);
2496
2497   /* Set new uri for playerbins and continue playback */
2498   if (l && (pipeline_type == 14 || pipeline_type == 16)) {
2499     stop_cb (NULL, NULL);
2500     l = g_list_next (l);
2501     if (l) {
2502       playerbin_set_uri (GST_ELEMENT (data), l->data);
2503       play_cb (NULL, NULL);
2504     }
2505   }
2506 }
2507
2508 static void
2509 msg_step_done (GstBus * bus, GstMessage * message, GstPipeline * data)
2510 {
2511   if (!shuttling)
2512     message_received (bus, message, data);
2513 }
2514
2515 static void
2516 connect_bus_signals (GstElement * pipeline)
2517 {
2518   GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
2519
2520 #ifdef HAVE_X
2521   /* handle prepare-xwindow-id element message synchronously */
2522   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler,
2523       pipeline);
2524 #endif
2525
2526   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
2527   gst_bus_enable_sync_message_emission (bus);
2528
2529   g_signal_connect (bus, "message::state-changed",
2530       (GCallback) msg_state_changed, pipeline);
2531   g_signal_connect (bus, "message::segment-done", (GCallback) msg_segment_done,
2532       pipeline);
2533   g_signal_connect (bus, "message::async-done", (GCallback) msg_async_done,
2534       pipeline);
2535
2536   g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
2537       pipeline);
2538   g_signal_connect (bus, "message::clock-lost", (GCallback) msg_clock_lost,
2539       pipeline);
2540   g_signal_connect (bus, "message::error", (GCallback) message_received,
2541       pipeline);
2542   g_signal_connect (bus, "message::warning", (GCallback) message_received,
2543       pipeline);
2544   g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
2545   g_signal_connect (bus, "message::tag", (GCallback) message_received,
2546       pipeline);
2547   g_signal_connect (bus, "message::element", (GCallback) message_received,
2548       pipeline);
2549   g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
2550       pipeline);
2551   g_signal_connect (bus, "message::buffering", (GCallback) msg_buffering,
2552       pipeline);
2553 //  g_signal_connect (bus, "message::step-done", (GCallback) msg_step_done,
2554 //      pipeline);
2555   g_signal_connect (bus, "message::step-start", (GCallback) msg_step_done,
2556       pipeline);
2557   g_signal_connect (bus, "sync-message::step-done",
2558       (GCallback) msg_sync_step_done, pipeline);
2559
2560   gst_object_unref (bus);
2561 }
2562
2563 /* Return GList of paths described in location string */
2564 static GList *
2565 handle_wildcards (const gchar * location)
2566 {
2567   GList *res = NULL;
2568   gchar *path = g_path_get_dirname (location);
2569   gchar *pattern = g_path_get_basename (location);
2570   GPatternSpec *pspec = g_pattern_spec_new (pattern);
2571   GDir *dir = g_dir_open (path, 0, NULL);
2572   const gchar *name;
2573
2574   g_print ("matching %s from %s\n", pattern, path);
2575
2576   if (!dir) {
2577     g_print ("opening directory %s failed\n", path);
2578     goto out;
2579   }
2580
2581   while ((name = g_dir_read_name (dir)) != NULL) {
2582     if (g_pattern_match_string (pspec, name)) {
2583       res = g_list_append (res, g_strjoin ("/", path, name, NULL));
2584       g_print ("  found clip %s\n", name);
2585     }
2586   }
2587
2588   g_dir_close (dir);
2589 out:
2590   g_pattern_spec_free (pspec);
2591   g_free (pattern);
2592   g_free (path);
2593
2594   return res;
2595 }
2596
2597 static void
2598 delete_event_cb (void)
2599 {
2600   stop_cb (NULL, NULL);
2601   gtk_main_quit ();
2602 }
2603
2604 static void
2605 print_usage (int argc, char **argv)
2606 {
2607   gint i;
2608
2609   g_print ("usage: %s <type> <filename>\n", argv[0]);
2610   g_print ("   possible types:\n");
2611
2612   for (i = 0; i < NUM_TYPES; i++) {
2613     g_print ("     %d = %s\n", i, pipelines[i].name);
2614   }
2615 }
2616
2617 static gboolean
2618 read_joystick (GIOChannel * source, GIOCondition condition, gpointer user_data)
2619 {
2620   gchar buf[sizeof (struct js_event)];
2621   struct js_event *js = (struct js_event *) buf;
2622   GError *err = NULL;
2623   gsize bytes_read = 0;
2624   GIOStatus result;
2625
2626   result =
2627       g_io_channel_read_chars (source, buf, sizeof (struct js_event),
2628       &bytes_read, &err);
2629   if (err) {
2630     g_print ("error reading from joystick: %s", err->message);
2631     g_error_free (err);
2632     return FALSE;
2633   } else if (bytes_read != sizeof (struct js_event)) {
2634     g_print ("error reading joystick, read %u bytes of %u\n",
2635         (guint) bytes_read, (guint) sizeof (struct js_event));
2636     return TRUE;
2637   } else if (result != G_IO_STATUS_NORMAL) {
2638     g_print ("reading from joystick returned status %d", result);
2639   }
2640
2641   switch (js->type & ~JS_EVENT_INIT) {
2642     case JS_EVENT_AXIS:
2643       if (js->number == 0) {
2644         gdouble new_rate = (gdouble) (js->value) / 3000;
2645         g_print ("Got: %d (rate %g)\n", js->value, new_rate);
2646         if (shuttling)
2647           gtk_adjustment_set_value (shuttle_adjustment, new_rate);
2648       }
2649       break;
2650   }
2651
2652   return TRUE;
2653 }
2654
2655 int
2656 main (int argc, char **argv)
2657 {
2658   GtkWidget *window, *hbox, *vbox, *panel, *expander, *pb2vbox, *boxes,
2659       *flagtable, *boxes2, *step;
2660   GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
2661   GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
2662   GtkWidget *scrub_checkbox, *play_scrub_checkbox;
2663   GtkWidget *rate_label, *volume_label;
2664   GOptionEntry options[] = {
2665     {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
2666         "Show pad stats", NULL},
2667     {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek,
2668         "Seek on elements instead of pads", NULL},
2669     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
2670         "Verbose properties", NULL},
2671     {"joystick", 'j', 0, G_OPTION_ARG_STRING, &js_device,
2672         "Joystick device to use", NULL},
2673     {NULL}
2674   };
2675   GOptionContext *ctx;
2676   GError *err = NULL;
2677
2678 #if !GLIB_CHECK_VERSION (2, 31, 0)
2679   if (!g_thread_supported ())
2680     g_thread_init (NULL);
2681 #endif
2682
2683   ctx = g_option_context_new ("- test seeking in gsteamer");
2684   g_option_context_add_main_entries (ctx, options, NULL);
2685   g_option_context_add_group (ctx, gst_init_get_option_group ());
2686   g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
2687
2688   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
2689     g_print ("Error initializing: %s\n", err->message);
2690     exit (1);
2691   }
2692
2693   GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
2694
2695   if (argc != 3) {
2696     print_usage (argc, argv);
2697     exit (-1);
2698   }
2699
2700   pipeline_type = atoi (argv[1]);
2701
2702   if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
2703     print_usage (argc, argv);
2704     exit (-1);
2705   }
2706
2707   pipeline_spec = argv[2];
2708
2709   if (js_device == NULL)
2710     js_device = g_strdup ("/dev/input/js0");
2711
2712   js_fd = g_open (js_device, O_RDONLY, 0);
2713   if (js_fd < 0) {
2714     g_print ("Failed to open joystick device %s\n", js_device);
2715     exit (-1);
2716   }
2717
2718   if (g_strrstr (pipeline_spec, "*") != NULL ||
2719       g_strrstr (pipeline_spec, "?") != NULL) {
2720     paths = handle_wildcards (pipeline_spec);
2721   } else {
2722     paths = g_list_prepend (paths, g_strdup (pipeline_spec));
2723   }
2724
2725   if (!paths) {
2726     g_print ("opening %s failed\n", pipeline_spec);
2727     exit (-1);
2728   }
2729
2730   l = paths;
2731
2732   pipeline = pipelines[pipeline_type].func ((gchar *) l->data);
2733   g_assert (pipeline);
2734
2735   /* initialize gui elements ... */
2736   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2737   video_window = gtk_drawing_area_new ();
2738   g_signal_connect (video_window, "draw", G_CALLBACK (draw_cb), NULL);
2739   g_signal_connect (video_window, "realize", G_CALLBACK (realize_cb), NULL);
2740   gtk_widget_set_double_buffered (video_window, FALSE);
2741
2742   statusbar = gtk_statusbar_new ();
2743   status_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "seek");
2744   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
2745   hbox = gtk_hbox_new (FALSE, 0);
2746   vbox = gtk_vbox_new (FALSE, 0);
2747   flagtable = gtk_table_new (4, 2, FALSE);
2748   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
2749
2750   /* media controls */
2751   play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
2752   pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
2753   stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
2754
2755   /* seek flags */
2756   accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
2757   key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
2758   loop_checkbox = gtk_check_button_new_with_label ("Loop");
2759   flush_checkbox = gtk_check_button_new_with_label ("Flush");
2760   scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
2761   play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
2762   skip_checkbox = gtk_check_button_new_with_label ("Play Skip");
2763   rate_spinbutton = gtk_spin_button_new_with_range (-100, 100, 0.1);
2764   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (rate_spinbutton), 3);
2765   rate_label = gtk_label_new ("Rate");
2766
2767   gtk_widget_set_tooltip_text (accurate_checkbox,
2768       "accurate position is requested, this might be considerably slower for some formats");
2769   gtk_widget_set_tooltip_text (key_checkbox,
2770       "seek to the nearest keyframe. This might be faster but less accurate");
2771   gtk_widget_set_tooltip_text (loop_checkbox, "loop playback");
2772   gtk_widget_set_tooltip_text (flush_checkbox, "flush pipeline after seeking");
2773   gtk_widget_set_tooltip_text (rate_spinbutton, "define the playback rate, "
2774       "negative value trigger reverse playback");
2775   gtk_widget_set_tooltip_text (scrub_checkbox, "show images while seeking");
2776   gtk_widget_set_tooltip_text (play_scrub_checkbox, "play video while seeking");
2777   gtk_widget_set_tooltip_text (skip_checkbox,
2778       "Skip frames while playing at high frame rates");
2779
2780   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
2781   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
2782
2783   gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
2784
2785   /* step expander */
2786   {
2787     GtkWidget *hbox;
2788
2789     step = gtk_expander_new ("step options");
2790     hbox = gtk_hbox_new (FALSE, 0);
2791
2792     format_combo = gtk_combo_box_text_new ();
2793     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
2794         "frames");
2795     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
2796         "time (ms)");
2797     gtk_combo_box_set_active (GTK_COMBO_BOX (format_combo), 0);
2798     gtk_box_pack_start (GTK_BOX (hbox), format_combo, FALSE, FALSE, 2);
2799
2800     step_amount_spinbutton = gtk_spin_button_new_with_range (1, 1000, 1);
2801     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_amount_spinbutton), 0);
2802     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_amount_spinbutton), 1.0);
2803     gtk_box_pack_start (GTK_BOX (hbox), step_amount_spinbutton, FALSE, FALSE,
2804         2);
2805
2806     step_rate_spinbutton = gtk_spin_button_new_with_range (0.0, 100, 0.1);
2807     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_rate_spinbutton), 3);
2808     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_rate_spinbutton), 1.0);
2809     gtk_box_pack_start (GTK_BOX (hbox), step_rate_spinbutton, FALSE, FALSE, 2);
2810
2811     step_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_FORWARD);
2812     gtk_button_set_label (GTK_BUTTON (step_button), "Step");
2813     gtk_box_pack_start (GTK_BOX (hbox), step_button, FALSE, FALSE, 2);
2814
2815     g_signal_connect (G_OBJECT (step_button), "clicked", G_CALLBACK (step_cb),
2816         pipeline);
2817
2818     /* shuttle scale */
2819     shuttle_checkbox = gtk_check_button_new_with_label ("Shuttle");
2820     gtk_box_pack_start (GTK_BOX (hbox), shuttle_checkbox, FALSE, FALSE, 2);
2821     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shuttle_checkbox), FALSE);
2822     g_signal_connect (shuttle_checkbox, "toggled", G_CALLBACK (shuttle_toggled),
2823         pipeline);
2824
2825     shuttle_adjustment =
2826         GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -3.00, 4.0, 0.1, 1.0, 1.0));
2827     shuttle_hscale = gtk_hscale_new (shuttle_adjustment);
2828     gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2);
2829     gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP);
2830     g_signal_connect (shuttle_hscale, "value_changed",
2831         G_CALLBACK (shuttle_value_changed), pipeline);
2832     g_signal_connect (shuttle_hscale, "format_value",
2833         G_CALLBACK (shuttle_format_value), pipeline);
2834
2835     gtk_box_pack_start (GTK_BOX (hbox), shuttle_hscale, TRUE, TRUE, 2);
2836
2837     gtk_container_add (GTK_CONTAINER (step), hbox);
2838   }
2839
2840   /* seek bar */
2841   adjustment =
2842       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0));
2843   hscale = gtk_hscale_new (adjustment);
2844   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
2845   gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
2846   gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
2847   gtk_range_set_fill_level (GTK_RANGE (hscale), 100.0);
2848
2849   g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek),
2850       pipeline);
2851   g_signal_connect (hscale, "button_release_event", G_CALLBACK (stop_seek),
2852       pipeline);
2853   g_signal_connect (hscale, "format_value", G_CALLBACK (format_value),
2854       pipeline);
2855
2856   if (pipeline_type == 16) {
2857     /* the playbin2 panel controls for the video/audio/subtitle tracks */
2858     panel = gtk_hbox_new (FALSE, 0);
2859     video_combo = gtk_combo_box_text_new ();
2860     audio_combo = gtk_combo_box_text_new ();
2861     text_combo = gtk_combo_box_text_new ();
2862     gtk_widget_set_sensitive (video_combo, FALSE);
2863     gtk_widget_set_sensitive (audio_combo, FALSE);
2864     gtk_widget_set_sensitive (text_combo, FALSE);
2865     gtk_box_pack_start (GTK_BOX (panel), video_combo, TRUE, TRUE, 2);
2866     gtk_box_pack_start (GTK_BOX (panel), audio_combo, TRUE, TRUE, 2);
2867     gtk_box_pack_start (GTK_BOX (panel), text_combo, TRUE, TRUE, 2);
2868     g_signal_connect (G_OBJECT (video_combo), "changed",
2869         G_CALLBACK (video_combo_cb), pipeline);
2870     g_signal_connect (G_OBJECT (audio_combo), "changed",
2871         G_CALLBACK (audio_combo_cb), pipeline);
2872     g_signal_connect (G_OBJECT (text_combo), "changed",
2873         G_CALLBACK (text_combo_cb), pipeline);
2874     /* playbin2 panel for flag checkboxes and volume/mute */
2875     boxes = gtk_hbox_new (FALSE, 0);
2876     vis_checkbox = gtk_check_button_new_with_label ("Vis");
2877     video_checkbox = gtk_check_button_new_with_label ("Video");
2878     audio_checkbox = gtk_check_button_new_with_label ("Audio");
2879     text_checkbox = gtk_check_button_new_with_label ("Text");
2880     mute_checkbox = gtk_check_button_new_with_label ("Mute");
2881     download_checkbox = gtk_check_button_new_with_label ("Download");
2882     buffer_checkbox = gtk_check_button_new_with_label ("Buffer");
2883     volume_label = gtk_label_new ("Volume");
2884     volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
2885     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
2886     gtk_box_pack_start (GTK_BOX (boxes), video_checkbox, TRUE, TRUE, 2);
2887     gtk_box_pack_start (GTK_BOX (boxes), audio_checkbox, TRUE, TRUE, 2);
2888     gtk_box_pack_start (GTK_BOX (boxes), text_checkbox, TRUE, TRUE, 2);
2889     gtk_box_pack_start (GTK_BOX (boxes), vis_checkbox, TRUE, TRUE, 2);
2890     gtk_box_pack_start (GTK_BOX (boxes), mute_checkbox, TRUE, TRUE, 2);
2891     gtk_box_pack_start (GTK_BOX (boxes), download_checkbox, TRUE, TRUE, 2);
2892     gtk_box_pack_start (GTK_BOX (boxes), buffer_checkbox, TRUE, TRUE, 2);
2893     gtk_box_pack_start (GTK_BOX (boxes), volume_label, TRUE, TRUE, 2);
2894     gtk_box_pack_start (GTK_BOX (boxes), volume_spinbutton, TRUE, TRUE, 2);
2895     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vis_checkbox), FALSE);
2896     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audio_checkbox), TRUE);
2897     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (video_checkbox), TRUE);
2898     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_checkbox), TRUE);
2899     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mute_checkbox), FALSE);
2900     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (download_checkbox), FALSE);
2901     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (buffer_checkbox), FALSE);
2902     g_signal_connect (G_OBJECT (vis_checkbox), "toggled",
2903         G_CALLBACK (vis_toggle_cb), pipeline);
2904     g_signal_connect (G_OBJECT (audio_checkbox), "toggled",
2905         G_CALLBACK (audio_toggle_cb), pipeline);
2906     g_signal_connect (G_OBJECT (video_checkbox), "toggled",
2907         G_CALLBACK (video_toggle_cb), pipeline);
2908     g_signal_connect (G_OBJECT (text_checkbox), "toggled",
2909         G_CALLBACK (text_toggle_cb), pipeline);
2910     g_signal_connect (G_OBJECT (mute_checkbox), "toggled",
2911         G_CALLBACK (mute_toggle_cb), pipeline);
2912     g_signal_connect (G_OBJECT (download_checkbox), "toggled",
2913         G_CALLBACK (download_toggle_cb), pipeline);
2914     g_signal_connect (G_OBJECT (buffer_checkbox), "toggled",
2915         G_CALLBACK (buffer_toggle_cb), pipeline);
2916     g_signal_connect (G_OBJECT (volume_spinbutton), "value_changed",
2917         G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
2918     /* playbin2 panel for snapshot */
2919     boxes2 = gtk_hbox_new (FALSE, 0);
2920     shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
2921     gtk_widget_set_tooltip_text (shot_button,
2922         "save a screenshot .png in the current directory");
2923     g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
2924         pipeline);
2925     vis_combo = gtk_combo_box_text_new ();
2926     g_signal_connect (G_OBJECT (vis_combo), "changed",
2927         G_CALLBACK (vis_combo_cb), pipeline);
2928     gtk_widget_set_sensitive (vis_combo, FALSE);
2929     gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
2930     gtk_box_pack_start (GTK_BOX (boxes2), vis_combo, TRUE, TRUE, 2);
2931
2932     /* fill the vis combo box and the array of factories */
2933     init_visualization_features ();
2934   } else {
2935     panel = boxes = boxes2 = NULL;
2936   }
2937
2938   /* do the packing stuff ... */
2939   gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
2940   /* FIXME: can we avoid this for audio only? */
2941   gtk_widget_set_size_request (GTK_WIDGET (video_window), -1,
2942       DEFAULT_VIDEO_HEIGHT);
2943   gtk_container_add (GTK_CONTAINER (window), vbox);
2944   gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 2);
2945   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
2946   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
2947   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
2948   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
2949   gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
2950   gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
2951       1);
2952   gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
2953   gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
2954   gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
2955   gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
2956   gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
2957       1, 2);
2958   gtk_table_attach_defaults (GTK_TABLE (flagtable), skip_checkbox, 3, 4, 0, 1);
2959   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 4, 5, 0, 1);
2960   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 4, 5, 1,
2961       2);
2962   if (panel && boxes && boxes2) {
2963     expander = gtk_expander_new ("playbin2 options");
2964     pb2vbox = gtk_vbox_new (FALSE, 0);
2965     gtk_box_pack_start (GTK_BOX (pb2vbox), panel, FALSE, FALSE, 2);
2966     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes, FALSE, FALSE, 2);
2967     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes2, FALSE, FALSE, 2);
2968     gtk_container_add (GTK_CONTAINER (expander), pb2vbox);
2969     gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 2);
2970   }
2971   gtk_box_pack_start (GTK_BOX (vbox), step, FALSE, FALSE, 2);
2972   gtk_box_pack_start (GTK_BOX (vbox), hscale, FALSE, FALSE, 2);
2973   gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 2);
2974
2975   /* connect things ... */
2976   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
2977       pipeline);
2978   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
2979       pipeline);
2980   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
2981       pipeline);
2982   g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
2983       G_CALLBACK (accurate_toggle_cb), pipeline);
2984   g_signal_connect (G_OBJECT (key_checkbox), "toggled",
2985       G_CALLBACK (key_toggle_cb), pipeline);
2986   g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
2987       G_CALLBACK (loop_toggle_cb), pipeline);
2988   g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
2989       G_CALLBACK (flush_toggle_cb), pipeline);
2990   g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
2991       G_CALLBACK (scrub_toggle_cb), pipeline);
2992   g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
2993       G_CALLBACK (play_scrub_toggle_cb), pipeline);
2994   g_signal_connect (G_OBJECT (skip_checkbox), "toggled",
2995       G_CALLBACK (skip_toggle_cb), pipeline);
2996   g_signal_connect (G_OBJECT (rate_spinbutton), "value_changed",
2997       G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
2998
2999   g_signal_connect (G_OBJECT (window), "delete-event", delete_event_cb, NULL);
3000
3001   /* show the gui. */
3002   gtk_widget_show_all (window);
3003
3004   /* realize window now so that the video window gets created and we can
3005    * obtain its XID before the pipeline is started up and the videosink
3006    * asks for the XID of the window to render onto */
3007   gtk_widget_realize (window);
3008
3009   /* we should have the XID now */
3010   g_assert (embed_xid != 0);
3011
3012   if (verbose) {
3013     g_signal_connect (pipeline, "deep_notify",
3014         G_CALLBACK (gst_object_default_deep_notify), NULL);
3015   }
3016
3017   {
3018     GIOChannel *js_watch = g_io_channel_unix_new (js_fd);
3019     g_io_channel_set_encoding (js_watch, NULL, NULL);
3020     g_io_add_watch (js_watch, G_IO_IN, read_joystick, NULL);
3021   }
3022
3023   connect_bus_signals (pipeline);
3024   gtk_main ();
3025
3026   g_print ("NULL pipeline\n");
3027   gst_element_set_state (pipeline, GST_STATE_NULL);
3028
3029   g_print ("free pipeline\n");
3030   gst_object_unref (pipeline);
3031
3032   g_list_foreach (paths, (GFunc) g_free, NULL);
3033   g_list_free (paths);
3034
3035   return 0;
3036 }