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