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