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