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