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