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