tests/examples/seek/seek.c: Include config.h and check if we have X. Fixes: #540334.
[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 gdouble rate = 1.0;
81
82 static GstElement *pipeline;
83 static gint pipeline_type;
84 static const gchar *pipeline_spec;
85 static gint64 position = -1;
86 static gint64 duration = -1;
87 static GtkAdjustment *adjustment;
88 static GtkWidget *hscale, *statusbar;
89 static guint status_id = 0;
90 static gboolean stats = FALSE;
91 static gboolean elem_seek = FALSE;
92 static gboolean verbose = FALSE;
93
94 static gboolean is_live = FALSE;
95 static gboolean buffering = FALSE;
96 static GstBufferingMode mode;
97 static gint64 buffering_left;
98 static GstState state = GST_STATE_NULL;
99 static guint update_id = 0;
100 static guint seek_timeout_id = 0;
101 static gulong changed_id;
102 static guint fill_id = 0;
103
104 static gint n_video = 0, n_audio = 0, n_text = 0;
105 static gboolean need_streams = TRUE;
106 static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
107 static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
108 static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
109 static GtkWidget *video_window;
110 static guint embed_xid = 0;
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
1290   if (rate >= 0) {
1291     s_event = gst_event_new_seek (rate,
1292         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET, -1);
1293     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1294         rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
1295   } else {
1296     s_event = gst_event_new_seek (rate,
1297         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1298         GST_SEEK_TYPE_SET, real);
1299     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1300         rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
1301   }
1302
1303   res = send_event (s_event);
1304
1305   if (res) {
1306     if (flush_seek) {
1307       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1308     } else {
1309       set_update_scale (TRUE);
1310     }
1311   } else {
1312     g_print ("seek failed\n");
1313     set_update_scale (TRUE);
1314   }
1315 }
1316
1317 static void
1318 seek_cb (GtkWidget * widget)
1319 {
1320   /* If the timer hasn't expired yet, then the pipeline is running */
1321   if (play_scrub && seek_timeout_id != 0) {
1322     GST_DEBUG ("do scrub seek, PAUSED");
1323     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1324   }
1325
1326   GST_DEBUG ("do seek");
1327   do_seek (widget);
1328
1329   if (play_scrub) {
1330     GST_DEBUG ("do scrub seek, PLAYING");
1331     gst_element_set_state (pipeline, GST_STATE_PLAYING);
1332
1333     if (seek_timeout_id == 0) {
1334       seek_timeout_id =
1335           g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
1336     }
1337   }
1338 }
1339
1340 static void
1341 set_update_fill (gboolean active)
1342 {
1343   GST_DEBUG ("fill scale is %d", active);
1344
1345   if (active) {
1346     if (fill_id == 0) {
1347       fill_id =
1348           g_timeout_add (FILL_INTERVAL, (GtkFunction) update_fill, pipeline);
1349     }
1350   } else {
1351     if (fill_id) {
1352       g_source_remove (fill_id);
1353       fill_id = 0;
1354     }
1355   }
1356 }
1357
1358 static void
1359 set_update_scale (gboolean active)
1360 {
1361
1362   GST_DEBUG ("update scale is %d", active);
1363
1364   if (active) {
1365     if (update_id == 0) {
1366       update_id =
1367           g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
1368     }
1369   } else {
1370     if (update_id) {
1371       g_source_remove (update_id);
1372       update_id = 0;
1373     }
1374   }
1375 }
1376
1377 static gboolean
1378 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1379 {
1380   if (event->type != GDK_BUTTON_PRESS)
1381     return FALSE;
1382
1383   set_update_scale (FALSE);
1384
1385   if (state == GST_STATE_PLAYING && flush_seek && scrub) {
1386     GST_DEBUG ("start scrub seek, PAUSE");
1387     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1388   }
1389
1390   if (changed_id == 0 && flush_seek && scrub) {
1391     changed_id = gtk_signal_connect (GTK_OBJECT (hscale),
1392         "value_changed", G_CALLBACK (seek_cb), pipeline);
1393   }
1394
1395   return FALSE;
1396 }
1397
1398 static gboolean
1399 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1400 {
1401   if (changed_id) {
1402     g_signal_handler_disconnect (GTK_OBJECT (hscale), changed_id);
1403     changed_id = 0;
1404   }
1405
1406   if (!flush_seek || !scrub) {
1407     GST_DEBUG ("do final seek");
1408     do_seek (widget);
1409   }
1410
1411   if (seek_timeout_id != 0) {
1412     g_source_remove (seek_timeout_id);
1413     seek_timeout_id = 0;
1414     /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
1415      * instead. */
1416     if (state == GST_STATE_PAUSED) {
1417       GST_DEBUG ("stop scrub seek, PAUSED");
1418       gst_element_set_state (pipeline, GST_STATE_PAUSED);
1419     }
1420   } else {
1421     if (state == GST_STATE_PLAYING) {
1422       GST_DEBUG ("stop scrub seek, PLAYING");
1423       gst_element_set_state (pipeline, GST_STATE_PLAYING);
1424     }
1425   }
1426
1427   return FALSE;
1428 }
1429
1430 static void
1431 play_cb (GtkButton * button, gpointer data)
1432 {
1433   GstStateChangeReturn ret;
1434
1435   if (state != GST_STATE_PLAYING) {
1436     g_print ("PLAY pipeline\n");
1437     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1438
1439     ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
1440     switch (ret) {
1441       case GST_STATE_CHANGE_FAILURE:
1442         goto failed;
1443       case GST_STATE_CHANGE_NO_PREROLL:
1444         is_live = TRUE;
1445         break;
1446       default:
1447         break;
1448     }
1449     state = GST_STATE_PLAYING;
1450     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
1451   }
1452   return;
1453
1454 failed:
1455   {
1456     g_print ("PLAY failed\n");
1457     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
1458   }
1459 }
1460
1461 static void
1462 pause_cb (GtkButton * button, gpointer data)
1463 {
1464   if (state != GST_STATE_PAUSED) {
1465     GstStateChangeReturn ret;
1466
1467     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1468     g_print ("PAUSE pipeline\n");
1469     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1470     switch (ret) {
1471       case GST_STATE_CHANGE_FAILURE:
1472         goto failed;
1473       case GST_STATE_CHANGE_NO_PREROLL:
1474         is_live = TRUE;
1475         break;
1476       default:
1477         break;
1478     }
1479
1480     state = GST_STATE_PAUSED;
1481     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
1482   }
1483   return;
1484
1485 failed:
1486   {
1487     g_print ("PAUSE failed\n");
1488     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
1489   }
1490 }
1491
1492 static void
1493 stop_cb (GtkButton * button, gpointer data)
1494 {
1495   if (state != GST_STATE_READY) {
1496     GstStateChangeReturn ret;
1497
1498     g_print ("READY pipeline\n");
1499     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1500
1501     ret = gst_element_set_state (pipeline, GST_STATE_READY);
1502     if (ret == GST_STATE_CHANGE_FAILURE)
1503       goto failed;
1504
1505     state = GST_STATE_READY;
1506     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
1507
1508     is_live = FALSE;
1509     buffering = FALSE;
1510     set_update_scale (FALSE);
1511     set_scale (0.0);
1512     set_update_fill (FALSE);
1513
1514     if (pipeline_type == 16)
1515       clear_streams (pipeline);
1516
1517     /* if one uses parse_launch, play, stop and play again it fails as all the
1518      * pads after the demuxer can't be reconnected
1519      */
1520     if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
1521       gst_element_set_state (pipeline, GST_STATE_NULL);
1522       gst_object_unref (pipeline);
1523
1524       g_list_free (seekable_elements);
1525       seekable_elements = NULL;
1526       g_list_free (seekable_pads);
1527       seekable_pads = NULL;
1528       g_list_free (rate_pads);
1529       rate_pads = NULL;
1530
1531       pipeline = pipelines[pipeline_type].func (pipeline_spec);
1532       g_assert (pipeline);
1533       gst_element_set_state (pipeline, GST_STATE_READY);
1534       connect_bus_signals (pipeline);
1535     }
1536   }
1537   return;
1538
1539 failed:
1540   {
1541     g_print ("STOP failed\n");
1542     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
1543   }
1544 }
1545
1546 static void
1547 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1548 {
1549   accurate_seek = gtk_toggle_button_get_active (button);
1550 }
1551
1552 static void
1553 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1554 {
1555   keyframe_seek = gtk_toggle_button_get_active (button);
1556 }
1557
1558 static void
1559 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1560 {
1561   loop_seek = gtk_toggle_button_get_active (button);
1562   if (state == GST_STATE_PLAYING) {
1563     do_seek (hscale);
1564   }
1565 }
1566
1567 static void
1568 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1569 {
1570   flush_seek = gtk_toggle_button_get_active (button);
1571 }
1572
1573 static void
1574 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1575 {
1576   scrub = gtk_toggle_button_get_active (button);
1577 }
1578
1579 static void
1580 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1581 {
1582   play_scrub = gtk_toggle_button_get_active (button);
1583 }
1584
1585 static void
1586 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1587 {
1588   gboolean res = FALSE;
1589   GstEvent *s_event;
1590   GstSeekFlags flags;
1591
1592   rate = gtk_spin_button_get_value (button);
1593
1594   flags = 0;
1595   if (flush_seek)
1596     flags |= GST_SEEK_FLAG_FLUSH;
1597   if (loop_seek)
1598     flags |= GST_SEEK_FLAG_SEGMENT;
1599   if (accurate_seek)
1600     flags |= GST_SEEK_FLAG_ACCURATE;
1601   if (keyframe_seek)
1602     flags |= GST_SEEK_FLAG_KEY_UNIT;
1603
1604   if (rate >= 0) {
1605     s_event = gst_event_new_seek (rate,
1606         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1607         GST_SEEK_TYPE_SET, -1);
1608   } else {
1609     s_event = gst_event_new_seek (rate,
1610         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1611         GST_SEEK_TYPE_SET, position);
1612   }
1613
1614   GST_DEBUG ("rate changed to %lf", rate);
1615
1616   res = send_event (s_event);
1617
1618   if (res) {
1619     if (flush_seek) {
1620       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1621     }
1622   } else
1623     g_print ("seek failed\n");
1624 }
1625
1626 static void
1627 update_flag (GstPipeline * pipeline, gint num, gboolean state)
1628 {
1629   gint flags;
1630
1631   g_object_get (pipeline, "flags", &flags, NULL);
1632   if (state)
1633     flags |= (1 << num);
1634   else
1635     flags &= ~(1 << num);
1636   g_object_set (pipeline, "flags", flags, NULL);
1637 }
1638
1639 static void
1640 vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1641 {
1642   gboolean state;
1643
1644   state = gtk_toggle_button_get_active (button);
1645   update_flag (pipeline, 3, state);
1646   gtk_widget_set_sensitive (vis_combo, state);
1647 }
1648
1649 static void
1650 audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1651 {
1652   gboolean state;
1653
1654   state = gtk_toggle_button_get_active (button);
1655   update_flag (pipeline, 1, state);
1656   gtk_widget_set_sensitive (audio_combo, state);
1657 }
1658
1659 static void
1660 video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1661 {
1662   gboolean state;
1663
1664   state = gtk_toggle_button_get_active (button);
1665   update_flag (pipeline, 0, state);
1666   gtk_widget_set_sensitive (video_combo, state);
1667 }
1668
1669 static void
1670 text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1671 {
1672   gboolean state;
1673
1674   state = gtk_toggle_button_get_active (button);
1675   update_flag (pipeline, 2, state);
1676   gtk_widget_set_sensitive (text_combo, state);
1677 }
1678
1679 static void
1680 mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1681 {
1682   gboolean mute;
1683
1684   mute = gtk_toggle_button_get_active (button);
1685   g_object_set (pipeline, "mute", mute, NULL);
1686 }
1687
1688 static void
1689 clear_streams (GstElement * pipeline)
1690 {
1691   gint i;
1692
1693   /* remove previous info */
1694   for (i = 0; i < n_video; i++)
1695     gtk_combo_box_remove_text (GTK_COMBO_BOX (video_combo), 0);
1696   for (i = 0; i < n_audio; i++)
1697     gtk_combo_box_remove_text (GTK_COMBO_BOX (audio_combo), 0);
1698   for (i = 0; i < n_text; i++)
1699     gtk_combo_box_remove_text (GTK_COMBO_BOX (text_combo), 0);
1700
1701   n_audio = n_video = n_text = 0;
1702   gtk_widget_set_sensitive (video_combo, FALSE);
1703   gtk_widget_set_sensitive (audio_combo, FALSE);
1704   gtk_widget_set_sensitive (text_combo, FALSE);
1705
1706   need_streams = TRUE;
1707 }
1708
1709 static void
1710 update_streams (GstPipeline * pipeline)
1711 {
1712   gint i;
1713
1714   if (pipeline_type == 16 && need_streams) {
1715     GstTagList *tags;
1716     gchar *name;
1717     gint active_idx;
1718     gboolean state;
1719
1720     /* remove previous info */
1721     clear_streams (GST_ELEMENT_CAST (pipeline));
1722
1723     /* here we get and update the different streams detected by playbin2 */
1724     g_object_get (pipeline, "n-video", &n_video, NULL);
1725     g_object_get (pipeline, "n-audio", &n_audio, NULL);
1726     g_object_get (pipeline, "n-text", &n_text, NULL);
1727
1728     g_print ("video %d, audio %d, text %d\n", n_video, n_audio, n_text);
1729
1730     active_idx = 0;
1731     for (i = 0; i < n_video; i++) {
1732       g_signal_emit_by_name (pipeline, "get-video-tags", i, &tags);
1733       /* find good name for the label */
1734       name = g_strdup_printf ("video %d", i + 1);
1735       gtk_combo_box_append_text (GTK_COMBO_BOX (video_combo), name);
1736       g_free (name);
1737     }
1738     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (video_checkbox));
1739     gtk_widget_set_sensitive (video_combo, state && n_video > 0);
1740     gtk_combo_box_set_active (GTK_COMBO_BOX (video_combo), active_idx);
1741
1742     active_idx = 0;
1743     for (i = 0; i < n_audio; i++) {
1744       g_signal_emit_by_name (pipeline, "get-audio-tags", i, &tags);
1745       /* find good name for the label */
1746       name = g_strdup_printf ("audio %d", i + 1);
1747       gtk_combo_box_append_text (GTK_COMBO_BOX (audio_combo), name);
1748       g_free (name);
1749     }
1750     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (audio_checkbox));
1751     gtk_widget_set_sensitive (audio_combo, state && n_audio > 0);
1752     gtk_combo_box_set_active (GTK_COMBO_BOX (audio_combo), active_idx);
1753
1754     active_idx = 0;
1755     for (i = 0; i < n_text; i++) {
1756       g_signal_emit_by_name (pipeline, "get-text-tags", i, &tags);
1757       name = NULL;
1758       if (tags) {
1759         const GValue *value;
1760
1761         /* get the language code if we can */
1762         value = gst_tag_list_get_value_index (tags, GST_TAG_LANGUAGE_CODE, 0);
1763         if (value && G_VALUE_HOLDS_STRING (value)) {
1764           name = g_strdup_printf ("text %s", g_value_get_string (value));
1765         }
1766       }
1767       /* find good name for the label if we didn't use a tag */
1768       if (name == NULL)
1769         name = g_strdup_printf ("text %d", i + 1);
1770
1771       gtk_combo_box_append_text (GTK_COMBO_BOX (text_combo), name);
1772       g_free (name);
1773     }
1774     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_checkbox));
1775     gtk_widget_set_sensitive (text_combo, state && n_text > 0);
1776     gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), active_idx);
1777
1778     need_streams = FALSE;
1779   }
1780 }
1781
1782 static void
1783 video_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1784 {
1785   g_object_set (pipeline, "current-video", gtk_combo_box_get_active (combo),
1786       NULL);
1787 }
1788
1789 static void
1790 audio_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1791 {
1792   g_object_set (pipeline, "current-audio", gtk_combo_box_get_active (combo),
1793       NULL);
1794 }
1795
1796 static void
1797 text_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1798 {
1799   g_object_set (pipeline, "current-text", gtk_combo_box_get_active (combo),
1800       NULL);
1801 }
1802
1803 static gboolean
1804 filter_features (GstPluginFeature * feature, gpointer data)
1805 {
1806   GstElementFactory *f;
1807
1808   if (!GST_IS_ELEMENT_FACTORY (feature))
1809     return FALSE;
1810   f = GST_ELEMENT_FACTORY (feature);
1811   if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
1812     return FALSE;
1813
1814   return TRUE;
1815 }
1816
1817 static void
1818 init_visualization_features (void)
1819 {
1820   GList *list, *walk;
1821
1822   vis_entries = g_array_new (FALSE, FALSE, sizeof (VisEntry));
1823
1824   list = gst_registry_feature_filter (gst_registry_get_default (),
1825       filter_features, FALSE, NULL);
1826
1827   for (walk = list; walk; walk = g_list_next (walk)) {
1828     VisEntry entry;
1829     const gchar *name;
1830
1831     entry.factory = GST_ELEMENT_FACTORY (walk->data);
1832     name = gst_element_factory_get_longname (entry.factory);
1833
1834     g_array_append_val (vis_entries, entry);
1835     gtk_combo_box_append_text (GTK_COMBO_BOX (vis_combo), name);
1836   }
1837   gtk_combo_box_set_active (GTK_COMBO_BOX (vis_combo), 0);
1838 }
1839
1840 static void
1841 vis_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1842 {
1843   guint index;
1844   VisEntry *entry;
1845   GstElement *element;
1846
1847   /* get the selected index and get the factory for this index */
1848   index = gtk_combo_box_get_active (GTK_COMBO_BOX (vis_combo));
1849   entry = &g_array_index (vis_entries, VisEntry, index);
1850   /* create an instance of the element from the factory */
1851   element = gst_element_factory_create (entry->factory, NULL);
1852   if (!element)
1853     return;
1854
1855   /* set vis plugin for playbin2 */
1856   g_object_set (pipeline, "vis-plugin", element, NULL);
1857 }
1858
1859 static void
1860 volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1861 {
1862   gdouble volume;
1863
1864   volume = gtk_spin_button_get_value (button);
1865
1866   g_object_set (pipeline, "volume", volume, NULL);
1867 }
1868
1869 static void
1870 shot_cb (GtkButton * button, gpointer data)
1871 {
1872   GstBuffer *buffer;
1873   GstCaps *caps;
1874
1875   /* convert to our desired format (RGB24) */
1876   caps = gst_caps_new_simple ("video/x-raw-rgb",
1877       "bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
1878       /* Note: we don't ask for a specific width/height here, so that
1879        * videoscale can adjust dimensions from a non-1/1 pixel aspect
1880        * ratio to a 1/1 pixel-aspect-ratio */
1881       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1882       "endianness", G_TYPE_INT, G_BIG_ENDIAN,
1883       "red_mask", G_TYPE_INT, 0xff0000,
1884       "green_mask", G_TYPE_INT, 0x00ff00,
1885       "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
1886
1887   /* convert the latest frame to the requested format */
1888   g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
1889   gst_caps_unref (caps);
1890
1891   if (buffer) {
1892     GstCaps *caps;
1893     GstStructure *s;
1894     gboolean res;
1895     gint width, height;
1896     GdkPixbuf *pixbuf;
1897     GError *error = NULL;
1898
1899     /* get the snapshot buffer format now. We set the caps on the appsink so
1900      * that it can only be an rgb buffer. The only thing we have not specified
1901      * on the caps is the height, which is dependant on the pixel-aspect-ratio
1902      * of the source material */
1903     caps = GST_BUFFER_CAPS (buffer);
1904     if (!caps) {
1905       g_warning ("could not get snapshot format\n");
1906       goto done;
1907     }
1908     s = gst_caps_get_structure (caps, 0);
1909
1910     /* we need to get the final caps on the buffer to get the size */
1911     res = gst_structure_get_int (s, "width", &width);
1912     res |= gst_structure_get_int (s, "height", &height);
1913     if (!res) {
1914       g_warning ("could not get snapshot dimension\n");
1915       goto done;
1916     }
1917
1918     /* create pixmap from buffer and save, gstreamer video buffers have a stride
1919      * that is rounded up to the nearest multiple of 4 */
1920     pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
1921         GDK_COLORSPACE_RGB, FALSE, 8, width, height,
1922         GST_ROUND_UP_4 (width * 3), NULL, NULL);
1923
1924     /* save the pixbuf */
1925     gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
1926
1927   done:
1928     gst_buffer_unref (buffer);
1929   }
1930 }
1931
1932 static void
1933 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1934 {
1935   const GstStructure *s;
1936
1937   s = gst_message_get_structure (message);
1938   g_print ("message from \"%s\" (%s): ",
1939       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
1940       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
1941   if (s) {
1942     gchar *sstr;
1943
1944     sstr = gst_structure_to_string (s);
1945     g_print ("%s\n", sstr);
1946     g_free (sstr);
1947   } else {
1948     g_print ("no message details\n");
1949   }
1950 }
1951
1952 static void
1953 msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1954 {
1955   GST_DEBUG ("async done");
1956   /* when we get ASYNC_DONE we can query position, duration and other
1957    * properties */
1958   update_scale (pipeline);
1959
1960   /* update the available streams */
1961   update_streams (pipeline);
1962 }
1963
1964 static void
1965 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1966 {
1967   const GstStructure *s;
1968
1969   s = gst_message_get_structure (message);
1970
1971   /* We only care about state changed on the pipeline */
1972   if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
1973     GstState old, new, pending;
1974
1975     gst_message_parse_state_changed (message, &old, &new, &pending);
1976
1977     /* When state of the pipeline changes to paused or playing we start updating scale */
1978     if (new == GST_STATE_PLAYING) {
1979       set_update_scale (TRUE);
1980     } else {
1981       set_update_scale (FALSE);
1982     }
1983   }
1984 }
1985
1986 static void
1987 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1988 {
1989   GstEvent *s_event;
1990   GstSeekFlags flags;
1991   gboolean res;
1992   GstFormat format;
1993
1994   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1995   gst_message_parse_segment_done (message, &format, &position);
1996   GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1997
1998   flags = 0;
1999   /* in the segment-done callback we never flush as this would not make sense
2000    * for seamless playback. */
2001   if (loop_seek)
2002     flags = GST_SEEK_FLAG_SEGMENT;
2003
2004   s_event = gst_event_new_seek (rate,
2005       GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2006       GST_SEEK_TYPE_SET, duration);
2007
2008   GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
2009       rate, GST_TIME_ARGS (duration));
2010
2011   res = send_event (s_event);
2012   if (!res)
2013     g_print ("segment seek failed\n");
2014 }
2015
2016 /* in stream buffering mode we PAUSE the pipeline until we receive a 100%
2017  * message */
2018 static void
2019 do_stream_buffering (gint percent)
2020 {
2021   gchar *bufstr;
2022
2023   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2024   bufstr = g_strdup_printf ("Buffering...%d", percent);
2025   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2026   g_free (bufstr);
2027
2028   if (percent == 100) {
2029     /* a 100% message means buffering is done */
2030     buffering = FALSE;
2031     /* if the desired state is playing, go back */
2032     if (state == GST_STATE_PLAYING) {
2033       /* no state management needed for live pipelines */
2034       if (!is_live) {
2035         fprintf (stderr, "Done buffering, setting pipeline to PLAYING ...\n");
2036         gst_element_set_state (pipeline, GST_STATE_PLAYING);
2037       }
2038       gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2039       gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
2040     }
2041   } else {
2042     /* buffering busy */
2043     if (buffering == FALSE && state == GST_STATE_PLAYING) {
2044       /* we were not buffering but PLAYING, PAUSE  the pipeline. */
2045       if (!is_live) {
2046         fprintf (stderr, "Buffering, setting pipeline to PAUSED ...\n");
2047         gst_element_set_state (pipeline, GST_STATE_PAUSED);
2048       }
2049     }
2050     buffering = TRUE;
2051   }
2052 }
2053
2054 static void
2055 do_download_buffering (gint percent)
2056 {
2057   if (!buffering && percent < 100) {
2058     gchar *bufstr;
2059
2060     buffering = TRUE;
2061
2062     bufstr = g_strdup_printf ("Downloading...");
2063     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2064     g_free (bufstr);
2065
2066     /* once we get a buffering message, we'll do the fill update */
2067     set_update_fill (TRUE);
2068
2069     if (state == GST_STATE_PLAYING && !is_live) {
2070       fprintf (stderr, "Downloading, setting pipeline to PAUSED ...\n");
2071       gst_element_set_state (pipeline, GST_STATE_PAUSED);
2072     }
2073   }
2074 }
2075
2076 static void
2077 msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data)
2078 {
2079   gint percent;
2080
2081   gst_message_parse_buffering (message, &percent);
2082
2083   /* get more stats */
2084   gst_message_parse_buffering_stats (message, &mode, NULL, NULL,
2085       &buffering_left);
2086
2087   switch (mode) {
2088     case GST_BUFFERING_DOWNLOAD:
2089       do_download_buffering (percent);
2090       break;
2091     case GST_BUFFERING_LIVE:
2092     case GST_BUFFERING_TIMESHIFT:
2093     case GST_BUFFERING_STREAM:
2094       do_stream_buffering (percent);
2095       break;
2096   }
2097 }
2098
2099 #ifdef HAVE_X
2100 static GstBusSyncReply
2101 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
2102 {
2103   if ((GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) &&
2104       gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
2105     GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (message));
2106
2107     g_print ("got prepare-xwindow-id\n");
2108     if (!embed_xid) {
2109       embed_xid = GDK_WINDOW_XID (GDK_WINDOW (video_window->window));
2110     }
2111
2112     if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
2113             "force-aspect-ratio")) {
2114       g_object_set (element, "force-aspect-ratio", TRUE, NULL);
2115     }
2116
2117     gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (message)),
2118         embed_xid);
2119   }
2120   return GST_BUS_PASS;
2121 }
2122 #endif
2123
2124 static void
2125 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * data)
2126 {
2127   message_received (bus, message, data);
2128
2129   /* Set new uri for playerbins and continue playback */
2130   if (l && (pipeline_type == 14 || pipeline_type == 16)) {
2131     stop_cb (NULL, NULL);
2132     l = g_list_next (l);
2133     if (l) {
2134       playerbin_set_uri (GST_ELEMENT (data), l->data);
2135       play_cb (NULL, NULL);
2136     }
2137   }
2138 }
2139
2140 static void
2141 connect_bus_signals (GstElement * pipeline)
2142 {
2143   GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
2144
2145 #ifdef HAVE_X
2146   /* handle prepare-xwindow-id element message synchronously */
2147   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler,
2148       pipeline);
2149 #endif
2150
2151   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
2152
2153   g_signal_connect (bus, "message::state-changed",
2154       (GCallback) msg_state_changed, pipeline);
2155   g_signal_connect (bus, "message::segment-done", (GCallback) msg_segment_done,
2156       pipeline);
2157   g_signal_connect (bus, "message::async-done", (GCallback) msg_async_done,
2158       pipeline);
2159
2160   g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
2161       pipeline);
2162   g_signal_connect (bus, "message::error", (GCallback) message_received,
2163       pipeline);
2164   g_signal_connect (bus, "message::warning", (GCallback) message_received,
2165       pipeline);
2166   g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
2167   g_signal_connect (bus, "message::tag", (GCallback) message_received,
2168       pipeline);
2169   g_signal_connect (bus, "message::element", (GCallback) message_received,
2170       pipeline);
2171   g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
2172       pipeline);
2173   g_signal_connect (bus, "message::buffering", (GCallback) msg_buffering,
2174       pipeline);
2175
2176   gst_object_unref (bus);
2177 }
2178
2179 /* Return GList of paths described in location string */
2180 static GList *
2181 handle_wildcards (const gchar * location)
2182 {
2183   GList *res = NULL;
2184   gchar *path = g_path_get_dirname (location);
2185   gchar *pattern = g_path_get_basename (location);
2186   GPatternSpec *pspec = g_pattern_spec_new (pattern);
2187   GDir *dir = g_dir_open (path, 0, NULL);
2188   const gchar *name;
2189
2190   g_print ("matching %s from %s\n", pattern, path);
2191
2192   if (!dir) {
2193     g_print ("opening directory %s failed\n", path);
2194     goto out;
2195   }
2196
2197   while ((name = g_dir_read_name (dir)) != NULL) {
2198     if (g_pattern_match_string (pspec, name)) {
2199       res = g_list_append (res, g_strjoin ("/", path, name, NULL));
2200       g_print ("  found clip %s\n", name);
2201     }
2202   }
2203
2204   g_dir_close (dir);
2205 out:
2206   g_pattern_spec_free (pspec);
2207   g_free (pattern);
2208   g_free (path);
2209
2210   return res;
2211 }
2212
2213 static void
2214 delete_event_cb (void)
2215 {
2216   stop_cb (NULL, NULL);
2217   gtk_main_quit ();
2218 }
2219
2220 static void
2221 print_usage (int argc, char **argv)
2222 {
2223   gint i;
2224
2225   g_print ("usage: %s <type> <filename>\n", argv[0]);
2226   g_print ("   possible types:\n");
2227
2228   for (i = 0; i < NUM_TYPES; i++) {
2229     g_print ("     %d = %s\n", i, pipelines[i].name);
2230   }
2231 }
2232
2233 int
2234 main (int argc, char **argv)
2235 {
2236   GtkWidget *window, *hbox, *vbox, *panel, *boxes, *flagtable, *boxes2;
2237   GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
2238   GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
2239   GtkWidget *scrub_checkbox, *play_scrub_checkbox, *rate_spinbutton;
2240   GtkWidget *rate_label;
2241   GtkTooltips *tips;
2242   GOptionEntry options[] = {
2243     {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
2244         "Show pad stats", NULL},
2245     {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek,
2246         "Seek on elements instead of pads", NULL},
2247     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
2248         "Verbose properties", NULL},
2249     {NULL}
2250   };
2251   GOptionContext *ctx;
2252   GError *err = NULL;
2253
2254   if (!g_thread_supported ())
2255     g_thread_init (NULL);
2256
2257   ctx = g_option_context_new ("- test seeking in gsteamer");
2258   g_option_context_add_main_entries (ctx, options, NULL);
2259   g_option_context_add_group (ctx, gst_init_get_option_group ());
2260   g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
2261
2262   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
2263     g_print ("Error initializing: %s\n", err->message);
2264     exit (1);
2265   }
2266
2267   GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
2268
2269   if (argc != 3) {
2270     print_usage (argc, argv);
2271     exit (-1);
2272   }
2273
2274   pipeline_type = atoi (argv[1]);
2275
2276   if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
2277     print_usage (argc, argv);
2278     exit (-1);
2279   }
2280
2281   pipeline_spec = argv[2];
2282
2283   if (g_strrstr (pipeline_spec, "*") != NULL ||
2284       g_strrstr (pipeline_spec, "?") != NULL) {
2285     paths = handle_wildcards (pipeline_spec);
2286   } else {
2287     paths = g_list_prepend (paths, g_strdup (pipeline_spec));
2288   }
2289
2290   if (!paths) {
2291     g_print ("opening %s failed\n", pipeline_spec);
2292     exit (-1);
2293   }
2294
2295   l = paths;
2296
2297   pipeline = pipelines[pipeline_type].func ((gchar *) l->data);
2298   g_assert (pipeline);
2299
2300   /* initialize gui elements ... */
2301   tips = gtk_tooltips_new ();
2302   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2303   video_window = gtk_drawing_area_new ();
2304   gtk_widget_set_double_buffered (video_window, FALSE);
2305   statusbar = gtk_statusbar_new ();
2306   status_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "seek");
2307   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
2308   hbox = gtk_hbox_new (FALSE, 0);
2309   vbox = gtk_vbox_new (FALSE, 0);
2310   flagtable = gtk_table_new (4, 2, FALSE);
2311   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
2312
2313   /* media controls */
2314   play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
2315   pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
2316   stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
2317
2318   /* seek flags */
2319   accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
2320   key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
2321   loop_checkbox = gtk_check_button_new_with_label ("Loop");
2322   flush_checkbox = gtk_check_button_new_with_label ("Flush");
2323   scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
2324   play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
2325   rate_spinbutton = gtk_spin_button_new_with_range (-100, 100, 0.1);
2326   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (rate_spinbutton), 3);
2327   rate_label = gtk_label_new ("Rate");
2328
2329   gtk_tooltips_set_tip (tips, accurate_checkbox,
2330       "accurate position is requested, this might be considerably slower for some formats",
2331       NULL);
2332   gtk_tooltips_set_tip (tips, key_checkbox,
2333       "seek to the nearest keyframe. This might be faster but less accurate",
2334       NULL);
2335   gtk_tooltips_set_tip (tips, loop_checkbox, "loop playback", NULL);
2336   gtk_tooltips_set_tip (tips, flush_checkbox, "flush pipeline after seeking",
2337       NULL);
2338   gtk_tooltips_set_tip (tips, rate_spinbutton, "define the playback rate, "
2339       "negative value trigger reverse playback", NULL);
2340   gtk_tooltips_set_tip (tips, scrub_checkbox, "show images while seeking",
2341       NULL);
2342   gtk_tooltips_set_tip (tips, play_scrub_checkbox, "play video while seeking",
2343       NULL);
2344
2345   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
2346   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
2347
2348   gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
2349
2350   /* seek bar */
2351   adjustment =
2352       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0));
2353   hscale = gtk_hscale_new (adjustment);
2354   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
2355 #if GTK_CHECK_VERSION(2,12,0)
2356   gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
2357   gtk_range_set_fill_level (GTK_RANGE (hscale), 100.0);
2358 #endif
2359   gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS);
2360
2361   gtk_signal_connect (GTK_OBJECT (hscale),
2362       "button_press_event", G_CALLBACK (start_seek), pipeline);
2363   gtk_signal_connect (GTK_OBJECT (hscale),
2364       "button_release_event", G_CALLBACK (stop_seek), pipeline);
2365   gtk_signal_connect (GTK_OBJECT (hscale),
2366       "format_value", G_CALLBACK (format_value), pipeline);
2367
2368   if (pipeline_type == 16) {
2369     /* the playbin2 panel controls for the video/audio/subtitle tracks */
2370     panel = gtk_hbox_new (FALSE, 0);
2371     video_combo = gtk_combo_box_new_text ();
2372     audio_combo = gtk_combo_box_new_text ();
2373     text_combo = gtk_combo_box_new_text ();
2374     gtk_widget_set_sensitive (video_combo, FALSE);
2375     gtk_widget_set_sensitive (audio_combo, FALSE);
2376     gtk_widget_set_sensitive (text_combo, FALSE);
2377     gtk_box_pack_start (GTK_BOX (panel), video_combo, TRUE, TRUE, 2);
2378     gtk_box_pack_start (GTK_BOX (panel), audio_combo, TRUE, TRUE, 2);
2379     gtk_box_pack_start (GTK_BOX (panel), text_combo, TRUE, TRUE, 2);
2380     g_signal_connect (G_OBJECT (video_combo), "changed",
2381         G_CALLBACK (video_combo_cb), pipeline);
2382     g_signal_connect (G_OBJECT (audio_combo), "changed",
2383         G_CALLBACK (audio_combo_cb), pipeline);
2384     g_signal_connect (G_OBJECT (text_combo), "changed",
2385         G_CALLBACK (text_combo_cb), pipeline);
2386     /* playbin2 panel for flag checkboxes and volume/mute */
2387     boxes = gtk_hbox_new (FALSE, 0);
2388     vis_checkbox = gtk_check_button_new_with_label ("Vis");
2389     video_checkbox = gtk_check_button_new_with_label ("Video");
2390     audio_checkbox = gtk_check_button_new_with_label ("Audio");
2391     text_checkbox = gtk_check_button_new_with_label ("Text");
2392     mute_checkbox = gtk_check_button_new_with_label ("Mute");
2393     volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
2394     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
2395     gtk_box_pack_start (GTK_BOX (boxes), video_checkbox, TRUE, TRUE, 2);
2396     gtk_box_pack_start (GTK_BOX (boxes), audio_checkbox, TRUE, TRUE, 2);
2397     gtk_box_pack_start (GTK_BOX (boxes), text_checkbox, TRUE, TRUE, 2);
2398     gtk_box_pack_start (GTK_BOX (boxes), vis_checkbox, TRUE, TRUE, 2);
2399     gtk_box_pack_start (GTK_BOX (boxes), mute_checkbox, TRUE, TRUE, 2);
2400     gtk_box_pack_start (GTK_BOX (boxes), volume_spinbutton, TRUE, TRUE, 2);
2401     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vis_checkbox), FALSE);
2402     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audio_checkbox), TRUE);
2403     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (video_checkbox), TRUE);
2404     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_checkbox), TRUE);
2405     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mute_checkbox), FALSE);
2406     g_signal_connect (G_OBJECT (vis_checkbox), "toggled",
2407         G_CALLBACK (vis_toggle_cb), pipeline);
2408     g_signal_connect (G_OBJECT (audio_checkbox), "toggled",
2409         G_CALLBACK (audio_toggle_cb), pipeline);
2410     g_signal_connect (G_OBJECT (video_checkbox), "toggled",
2411         G_CALLBACK (video_toggle_cb), pipeline);
2412     g_signal_connect (G_OBJECT (text_checkbox), "toggled",
2413         G_CALLBACK (text_toggle_cb), pipeline);
2414     g_signal_connect (G_OBJECT (mute_checkbox), "toggled",
2415         G_CALLBACK (mute_toggle_cb), pipeline);
2416     g_signal_connect (G_OBJECT (volume_spinbutton), "value_changed",
2417         G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
2418     /* playbin2 panel for snapshot */
2419     boxes2 = gtk_hbox_new (FALSE, 0);
2420     shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
2421     gtk_tooltips_set_tip (tips, shot_button,
2422         "save a screenshot .png in the current directory", NULL);
2423     g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
2424         pipeline);
2425     vis_combo = gtk_combo_box_new_text ();
2426     g_signal_connect (G_OBJECT (vis_combo), "changed",
2427         G_CALLBACK (vis_combo_cb), pipeline);
2428     gtk_widget_set_sensitive (vis_combo, FALSE);
2429     gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
2430     gtk_box_pack_start (GTK_BOX (boxes2), vis_combo, TRUE, TRUE, 2);
2431
2432     /* fill the vis combo box and the array of factories */
2433     init_visualization_features ();
2434   } else {
2435     panel = boxes = boxes2 = NULL;
2436   }
2437
2438   /* do the packing stuff ... */
2439   gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
2440   /* FIXME: can we avoid this for audio only? */
2441   gtk_widget_set_size_request (GTK_WIDGET (video_window), -1,
2442       DEFAULT_VIDEO_HEIGHT);
2443   gtk_container_add (GTK_CONTAINER (window), vbox);
2444   gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 2);
2445   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
2446   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
2447   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
2448   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
2449   gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
2450   gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
2451       1);
2452   gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
2453   gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
2454   gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
2455   gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
2456   gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
2457       1, 2);
2458   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 3, 4, 0, 1);
2459   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 3, 4, 1,
2460       2);
2461   if (panel && boxes && boxes2) {
2462     gtk_box_pack_start (GTK_BOX (vbox), panel, FALSE, FALSE, 2);
2463     gtk_box_pack_start (GTK_BOX (vbox), boxes, FALSE, FALSE, 2);
2464     gtk_box_pack_start (GTK_BOX (vbox), boxes2, FALSE, FALSE, 2);
2465   }
2466   gtk_box_pack_start (GTK_BOX (vbox), hscale, FALSE, FALSE, 2);
2467   gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 2);
2468
2469   /* connect things ... */
2470   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
2471       pipeline);
2472   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
2473       pipeline);
2474   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
2475       pipeline);
2476   g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
2477       G_CALLBACK (accurate_toggle_cb), pipeline);
2478   g_signal_connect (G_OBJECT (key_checkbox), "toggled",
2479       G_CALLBACK (key_toggle_cb), pipeline);
2480   g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
2481       G_CALLBACK (loop_toggle_cb), pipeline);
2482   g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
2483       G_CALLBACK (flush_toggle_cb), pipeline);
2484   g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
2485       G_CALLBACK (scrub_toggle_cb), pipeline);
2486   g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
2487       G_CALLBACK (play_scrub_toggle_cb), pipeline);
2488   g_signal_connect (G_OBJECT (rate_spinbutton), "value_changed",
2489       G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
2490
2491   g_signal_connect (G_OBJECT (window), "delete-event", delete_event_cb, NULL);
2492
2493   /* show the gui. */
2494   gtk_widget_show_all (window);
2495
2496   if (verbose) {
2497     g_signal_connect (pipeline, "deep_notify",
2498         G_CALLBACK (gst_object_default_deep_notify), NULL);
2499   }
2500
2501   connect_bus_signals (pipeline);
2502   gtk_main ();
2503
2504   g_print ("NULL pipeline\n");
2505   gst_element_set_state (pipeline, GST_STATE_NULL);
2506
2507   g_print ("free pipeline\n");
2508   gst_object_unref (pipeline);
2509
2510   g_list_foreach (paths, (GFunc) g_free, NULL);
2511   g_list_free (paths);
2512
2513   return 0;
2514 }