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