seek: Directly use navigation interface on playbin2
[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 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
28  * with newer GTK versions (>= 3.3.0) */
29 #define GDK_DISABLE_DEPRECATION_WARNINGS
30
31 #include <stdlib.h>
32 #include <math.h>
33 #include <glib.h>
34 #include <gtk/gtk.h>
35 #include <gst/gst.h>
36 #include <string.h>
37
38 #include <gdk/gdk.h>
39 #if defined (GDK_WINDOWING_X11)
40 #include <gdk/gdkx.h>
41 #elif defined (GDK_WINDOWING_WIN32)
42 #include <gdk/gdkwin32.h>
43 #elif defined (GDK_WINDOWING_QUARTZ)
44 #include <gdk/gdkquartzwindow.h>
45 #endif
46
47 #include <gst/interfaces/xoverlay.h>
48 #include <gst/interfaces/navigation.h>
49 #include <gst/interfaces/colorbalance.h>
50
51 GST_DEBUG_CATEGORY_STATIC (seek_debug);
52 #define GST_CAT_DEFAULT (seek_debug)
53
54 /* Copied from gst-plugins-base/gst/playback/gstplay-enum.h */
55 typedef enum
56 {
57   GST_PLAY_FLAG_VIDEO = (1 << 0),
58   GST_PLAY_FLAG_AUDIO = (1 << 1),
59   GST_PLAY_FLAG_TEXT = (1 << 2),
60   GST_PLAY_FLAG_VIS = (1 << 3),
61   GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
62   GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
63   GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
64   GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
65   GST_PLAY_FLAG_BUFFERING = (1 << 8),
66   GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
67   GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
68 } GstPlayFlags;
69
70
71 /* configuration */
72
73 #define SOURCE "filesrc"
74
75 static gchar *opt_audiosink_str;        /* NULL */
76 static gchar *opt_videosink_str;        /* NULL */
77
78 #define FILL_INTERVAL 100
79 //#define UPDATE_INTERVAL 500
80 //#define UPDATE_INTERVAL 100
81 #define UPDATE_INTERVAL 40
82
83 /* number of milliseconds to play for after a seek */
84 #define SCRUB_TIME 100
85
86 /* timeout for gst_element_get_state() after a seek */
87 #define SEEK_TIMEOUT 40 * GST_MSECOND
88
89 #define DEFAULT_VIDEO_HEIGHT 300
90
91 /* the state to go to when stop is pressed */
92 #define STOP_STATE      GST_STATE_READY
93
94 #define N_GRAD 1000.0
95
96 static GList *seekable_pads = NULL;
97 static GList *rate_pads = NULL;
98 static GList *seekable_elements = NULL;
99
100 static gboolean accurate_seek = FALSE;
101 static gboolean keyframe_seek = FALSE;
102 static gboolean loop_seek = FALSE;
103 static gboolean flush_seek = TRUE;
104 static gboolean scrub = TRUE;
105 static gboolean play_scrub = FALSE;
106 static gboolean skip_seek = FALSE;
107 static gdouble rate = 1.0;
108
109 static GstElement *pipeline;
110 static gint pipeline_type;
111 static const gchar *pipeline_spec;
112 static gint64 position = -1;
113 static gint64 duration = -1;
114 static GtkAdjustment *adjustment;
115 static GtkWidget *hscale, *statusbar;
116 static guint status_id = 0;
117 static gboolean stats = FALSE;
118 static gboolean elem_seek = FALSE;
119 static gboolean verbose = FALSE;
120
121 static gboolean is_live = FALSE;
122 static gboolean buffering = FALSE;
123 static GstBufferingMode mode;
124 static gint64 buffering_left;
125 static GstState state = GST_STATE_NULL;
126 static guint update_id = 0;
127 static guint seek_timeout_id = 0;
128 static gulong changed_id;
129 static guint fill_id = 0;
130
131 static gint n_video = 0, n_audio = 0, n_text = 0;
132 static gboolean need_streams = TRUE;
133 static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
134 static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
135 static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
136 static GtkWidget *soft_volume_checkbox, *native_audio_checkbox;
137 static GtkWidget *native_video_checkbox, *deinterlace_checkbox;
138 static GtkWidget *soft_colorbalance_checkbox;
139 static GtkWidget *skip_checkbox, *video_window, *download_checkbox;
140 static GtkWidget *buffering_checkbox, *rate_spinbutton;
141
142 static GStaticMutex state_mutex = G_STATIC_MUTEX_INIT;
143
144 static GtkWidget *format_combo, *step_amount_spinbutton, *step_rate_spinbutton;
145 static GtkWidget *shuttle_checkbox, *step_button;
146 static GtkWidget *shuttle_hscale;
147 static GtkAdjustment *shuttle_adjustment;
148
149 static GtkWidget *contrast_scale, *brightness_scale, *hue_scale,
150     *saturation_scale;
151
152 static GstElement *navigation_element = NULL;
153 static GstElement *colorbalance_element = NULL;
154
155 static struct
156 {
157   GstNavigationCommand cmd;
158   GtkWidget *button;
159 } navigation_buttons[14];
160
161 static GList *paths = NULL, *l = NULL;
162
163 /* we keep an array of the visualisation entries so that we can easily switch
164  * with the combo box index. */
165 typedef struct
166 {
167   GstElementFactory *factory;
168 } VisEntry;
169
170 static GArray *vis_entries;
171
172 static void clear_streams (GstElement * pipeline);
173 static void volume_notify_cb (GstElement * pipeline, GParamSpec * arg,
174     gpointer user_dat);
175 static void find_interface_elements (void);
176
177 /* pipeline construction */
178
179 typedef struct
180 {
181   const gchar *padname;
182   GstPad *target;
183   GstElement *bin;
184 }
185 dyn_link;
186
187 static GstElement *
188 gst_element_factory_make_or_warn (const gchar * type, const gchar * name)
189 {
190   GstElement *element = gst_element_factory_make (type, name);
191
192 #ifndef GST_DISABLE_PARSE
193   if (!element) {
194     /* Try parsing it as a pipeline description */
195     element = gst_parse_bin_from_description (type, TRUE, NULL);
196     if (element) {
197       gst_element_set_name (element, name);
198     }
199   }
200 #endif
201
202   if (!element) {
203     g_warning ("Failed to create element %s of type %s", name, type);
204   }
205
206   return element;
207 }
208
209 static void
210 dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
211 {
212   gchar *padname;
213   dyn_link *connect = (dyn_link *) data;
214
215   padname = gst_pad_get_name (newpad);
216
217   if (connect->padname == NULL || !strcmp (padname, connect->padname)) {
218     if (connect->bin)
219       gst_bin_add (GST_BIN (pipeline), connect->bin);
220     gst_pad_link (newpad, connect->target);
221
222     //seekable_pads = g_list_prepend (seekable_pads, newpad);
223     rate_pads = g_list_prepend (rate_pads, newpad);
224   }
225   g_free (padname);
226 }
227
228 static void
229 setup_dynamic_link (GstElement * element, const gchar * padname,
230     GstPad * target, GstElement * bin)
231 {
232   dyn_link *connect;
233
234   connect = g_new0 (dyn_link, 1);
235   connect->padname = g_strdup (padname);
236   connect->target = target;
237   connect->bin = bin;
238
239   g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
240       connect);
241 }
242
243 static GstElement *
244 make_mod_pipeline (const gchar * location)
245 {
246   GstElement *pipeline;
247   GstElement *src, *decoder, *audiosink;
248   GstPad *seekable;
249
250   pipeline = gst_pipeline_new ("app");
251
252   src = gst_element_factory_make_or_warn (SOURCE, "src");
253   decoder = gst_element_factory_make_or_warn ("modplug", "decoder");
254   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
255   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
256
257   g_object_set (G_OBJECT (src), "location", location, NULL);
258
259   gst_bin_add (GST_BIN (pipeline), src);
260   gst_bin_add (GST_BIN (pipeline), decoder);
261   gst_bin_add (GST_BIN (pipeline), audiosink);
262
263   gst_element_link (src, decoder);
264   gst_element_link (decoder, audiosink);
265
266   seekable = gst_element_get_static_pad (decoder, "src");
267   seekable_pads = g_list_prepend (seekable_pads, seekable);
268   rate_pads = g_list_prepend (rate_pads, seekable);
269   rate_pads =
270       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
271
272   return pipeline;
273 }
274
275 static GstElement *
276 make_dv_pipeline (const gchar * location)
277 {
278   GstElement *pipeline;
279   GstElement *src, *demux, *decoder, *audiosink, *videosink;
280   GstElement *a_queue, *v_queue;
281   GstPad *seekable;
282
283   pipeline = gst_pipeline_new ("app");
284
285   src = gst_element_factory_make_or_warn (SOURCE, "src");
286   demux = gst_element_factory_make_or_warn ("dvdemux", "demuxer");
287   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
288   decoder = gst_element_factory_make_or_warn ("ffdec_dvvideo", "decoder");
289   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
290   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
291   audiosink = gst_element_factory_make_or_warn ("alsasink", "a_sink");
292
293   g_object_set (G_OBJECT (src), "location", location, NULL);
294
295   gst_bin_add (GST_BIN (pipeline), src);
296   gst_bin_add (GST_BIN (pipeline), demux);
297   gst_bin_add (GST_BIN (pipeline), a_queue);
298   gst_bin_add (GST_BIN (pipeline), audiosink);
299   gst_bin_add (GST_BIN (pipeline), v_queue);
300   gst_bin_add (GST_BIN (pipeline), decoder);
301   gst_bin_add (GST_BIN (pipeline), videosink);
302
303   gst_element_link (src, demux);
304   gst_element_link (a_queue, audiosink);
305   gst_element_link (v_queue, decoder);
306   gst_element_link (decoder, videosink);
307
308   setup_dynamic_link (demux, "video", gst_element_get_static_pad (v_queue,
309           "sink"), NULL);
310   setup_dynamic_link (demux, "audio", gst_element_get_static_pad (a_queue,
311           "sink"), NULL);
312
313   seekable = gst_element_get_static_pad (decoder, "src");
314   seekable_pads = g_list_prepend (seekable_pads, seekable);
315   rate_pads = g_list_prepend (rate_pads, seekable);
316
317   return pipeline;
318 }
319
320 static GstElement *
321 make_wav_pipeline (const gchar * location)
322 {
323   GstElement *pipeline;
324   GstElement *src, *decoder, *audiosink;
325
326   pipeline = gst_pipeline_new ("app");
327
328   src = gst_element_factory_make_or_warn (SOURCE, "src");
329   decoder = gst_element_factory_make_or_warn ("wavparse", "decoder");
330   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
331
332   g_object_set (G_OBJECT (src), "location", location, NULL);
333
334   gst_bin_add (GST_BIN (pipeline), src);
335   gst_bin_add (GST_BIN (pipeline), decoder);
336   gst_bin_add (GST_BIN (pipeline), audiosink);
337
338   gst_element_link (src, decoder);
339
340   setup_dynamic_link (decoder, "src", gst_element_get_static_pad (audiosink,
341           "sink"), NULL);
342
343   seekable_elements = g_list_prepend (seekable_elements, audiosink);
344
345   /* force element seeking on this pipeline */
346   elem_seek = TRUE;
347
348   return pipeline;
349 }
350
351 static GstElement *
352 make_flac_pipeline (const gchar * location)
353 {
354   GstElement *pipeline;
355   GstElement *src, *decoder, *audiosink;
356   GstPad *seekable;
357
358   pipeline = gst_pipeline_new ("app");
359
360   src = gst_element_factory_make_or_warn (SOURCE, "src");
361   decoder = gst_element_factory_make_or_warn ("flacdec", "decoder");
362   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
363   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
364
365   g_object_set (G_OBJECT (src), "location", location, NULL);
366
367   gst_bin_add (GST_BIN (pipeline), src);
368   gst_bin_add (GST_BIN (pipeline), decoder);
369   gst_bin_add (GST_BIN (pipeline), audiosink);
370
371   gst_element_link (src, decoder);
372   gst_element_link (decoder, audiosink);
373
374   seekable = gst_element_get_static_pad (decoder, "src");
375   seekable_pads = g_list_prepend (seekable_pads, seekable);
376   rate_pads = g_list_prepend (rate_pads, seekable);
377   rate_pads =
378       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
379
380   return pipeline;
381 }
382
383 static GstElement *
384 make_sid_pipeline (const gchar * location)
385 {
386   GstElement *pipeline;
387   GstElement *src, *decoder, *audiosink;
388   GstPad *seekable;
389
390   pipeline = gst_pipeline_new ("app");
391
392   src = gst_element_factory_make_or_warn (SOURCE, "src");
393   decoder = gst_element_factory_make_or_warn ("siddec", "decoder");
394   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
395   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
396
397   g_object_set (G_OBJECT (src), "location", location, NULL);
398
399   gst_bin_add (GST_BIN (pipeline), src);
400   gst_bin_add (GST_BIN (pipeline), decoder);
401   gst_bin_add (GST_BIN (pipeline), audiosink);
402
403   gst_element_link (src, decoder);
404   gst_element_link (decoder, audiosink);
405
406   seekable = gst_element_get_static_pad (decoder, "src");
407   seekable_pads = g_list_prepend (seekable_pads, seekable);
408   rate_pads = g_list_prepend (rate_pads, seekable);
409   rate_pads =
410       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
411
412   return pipeline;
413 }
414
415 static GstElement *
416 make_parse_pipeline (const gchar * location)
417 {
418   GstElement *pipeline;
419   GstElement *src, *parser, *fakesink;
420   GstPad *seekable;
421
422   pipeline = gst_pipeline_new ("app");
423
424   src = gst_element_factory_make_or_warn (SOURCE, "src");
425   parser = gst_element_factory_make_or_warn ("mpegparse", "parse");
426   fakesink = gst_element_factory_make_or_warn ("fakesink", "sink");
427   g_object_set (G_OBJECT (fakesink), "silent", TRUE, NULL);
428   g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
429
430   g_object_set (G_OBJECT (src), "location", location, NULL);
431
432   gst_bin_add (GST_BIN (pipeline), src);
433   gst_bin_add (GST_BIN (pipeline), parser);
434   gst_bin_add (GST_BIN (pipeline), fakesink);
435
436   gst_element_link (src, parser);
437   gst_element_link (parser, fakesink);
438
439   seekable = gst_element_get_static_pad (parser, "src");
440   seekable_pads = g_list_prepend (seekable_pads, seekable);
441   rate_pads = g_list_prepend (rate_pads, seekable);
442   rate_pads =
443       g_list_prepend (rate_pads, gst_element_get_static_pad (parser, "sink"));
444
445   return pipeline;
446 }
447
448 static GstElement *
449 make_vorbis_pipeline (const gchar * location)
450 {
451   GstElement *pipeline, *audio_bin;
452   GstElement *src, *demux, *decoder, *convert, *audiosink;
453   GstPad *pad, *seekable;
454
455   pipeline = gst_pipeline_new ("app");
456
457   src = gst_element_factory_make_or_warn (SOURCE, "src");
458   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
459   decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder");
460   convert = gst_element_factory_make_or_warn ("audioconvert", "convert");
461   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
462   g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL);
463
464   g_object_set (G_OBJECT (src), "location", location, NULL);
465
466   audio_bin = gst_bin_new ("a_decoder_bin");
467
468   gst_bin_add (GST_BIN (pipeline), src);
469   gst_bin_add (GST_BIN (pipeline), demux);
470   gst_bin_add (GST_BIN (audio_bin), decoder);
471   gst_bin_add (GST_BIN (audio_bin), convert);
472   gst_bin_add (GST_BIN (audio_bin), audiosink);
473   gst_bin_add (GST_BIN (pipeline), audio_bin);
474
475   gst_element_link (src, demux);
476   gst_element_link (decoder, convert);
477   gst_element_link (convert, audiosink);
478
479   pad = gst_element_get_static_pad (decoder, "sink");
480   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
481   gst_object_unref (pad);
482
483   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
484           "sink"), NULL);
485
486   seekable = gst_element_get_static_pad (decoder, "src");
487   seekable_pads = g_list_prepend (seekable_pads, seekable);
488   rate_pads = g_list_prepend (rate_pads, seekable);
489   rate_pads =
490       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
491
492   return pipeline;
493 }
494
495 static GstElement *
496 make_theora_pipeline (const gchar * location)
497 {
498   GstElement *pipeline, *video_bin;
499   GstElement *src, *demux, *decoder, *convert, *videosink;
500   GstPad *pad, *seekable;
501
502   pipeline = gst_pipeline_new ("app");
503
504   src = gst_element_factory_make_or_warn (SOURCE, "src");
505   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
506   decoder = gst_element_factory_make_or_warn ("theoradec", "decoder");
507   convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert");
508   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "sink");
509
510   g_object_set (G_OBJECT (src), "location", location, NULL);
511
512   video_bin = gst_bin_new ("v_decoder_bin");
513
514   gst_bin_add (GST_BIN (pipeline), src);
515   gst_bin_add (GST_BIN (pipeline), demux);
516   gst_bin_add (GST_BIN (video_bin), decoder);
517   gst_bin_add (GST_BIN (video_bin), convert);
518   gst_bin_add (GST_BIN (video_bin), videosink);
519   gst_bin_add (GST_BIN (pipeline), video_bin);
520
521   gst_element_link (src, demux);
522   gst_element_link (decoder, convert);
523   gst_element_link (convert, videosink);
524
525   pad = gst_element_get_static_pad (decoder, "sink");
526   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
527   gst_object_unref (pad);
528
529   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
530           "sink"), NULL);
531
532   seekable = gst_element_get_static_pad (decoder, "src");
533   seekable_pads = g_list_prepend (seekable_pads, seekable);
534   rate_pads = g_list_prepend (rate_pads, seekable);
535   rate_pads =
536       g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
537
538   return pipeline;
539 }
540
541 static GstElement *
542 make_vorbis_theora_pipeline (const gchar * location)
543 {
544   GstElement *pipeline, *audio_bin, *video_bin;
545   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
546   GstElement *audiosink, *videosink;
547   GstElement *a_queue, *v_queue, *v_scale;
548   GstPad *seekable;
549   GstPad *pad;
550
551   pipeline = gst_pipeline_new ("app");
552
553   src = gst_element_factory_make_or_warn (SOURCE, "src");
554   g_object_set (G_OBJECT (src), "location", location, NULL);
555
556   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
557
558   gst_bin_add (GST_BIN (pipeline), src);
559   gst_bin_add (GST_BIN (pipeline), demux);
560   gst_element_link (src, demux);
561
562   audio_bin = gst_bin_new ("a_decoder_bin");
563   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
564   a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec");
565   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
566   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
567
568   gst_bin_add (GST_BIN (pipeline), audio_bin);
569
570   gst_bin_add (GST_BIN (audio_bin), a_queue);
571   gst_bin_add (GST_BIN (audio_bin), a_decoder);
572   gst_bin_add (GST_BIN (audio_bin), a_convert);
573   gst_bin_add (GST_BIN (audio_bin), audiosink);
574
575   gst_element_link (a_queue, a_decoder);
576   gst_element_link (a_decoder, a_convert);
577   gst_element_link (a_convert, audiosink);
578
579   pad = gst_element_get_static_pad (a_queue, "sink");
580   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
581   gst_object_unref (pad);
582
583   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
584           "sink"), NULL);
585
586   video_bin = gst_bin_new ("v_decoder_bin");
587   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
588   v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec");
589   v_convert =
590       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
591   v_scale = gst_element_factory_make_or_warn ("videoscale", "v_scale");
592   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
593
594   gst_bin_add (GST_BIN (pipeline), video_bin);
595
596   gst_bin_add (GST_BIN (video_bin), v_queue);
597   gst_bin_add (GST_BIN (video_bin), v_decoder);
598   gst_bin_add (GST_BIN (video_bin), v_convert);
599   gst_bin_add (GST_BIN (video_bin), v_scale);
600   gst_bin_add (GST_BIN (video_bin), videosink);
601
602   gst_element_link_many (v_queue, v_decoder, v_convert, v_scale, videosink,
603       NULL);
604
605   pad = gst_element_get_static_pad (v_queue, "sink");
606   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
607   gst_object_unref (pad);
608
609   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
610           "sink"), NULL);
611
612   seekable = gst_element_get_static_pad (a_decoder, "src");
613   seekable_pads = g_list_prepend (seekable_pads, seekable);
614   rate_pads = g_list_prepend (rate_pads, seekable);
615   rate_pads =
616       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
617           "sink"));
618
619   return pipeline;
620 }
621
622 static GstElement *
623 make_avi_msmpeg4v3_mp3_pipeline (const gchar * location)
624 {
625   GstElement *pipeline, *audio_bin, *video_bin;
626   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
627   GstElement *audiosink, *videosink;
628   GstElement *a_queue, *v_queue;
629   GstPad *seekable, *pad;
630
631   pipeline = gst_pipeline_new ("app");
632
633   src = gst_element_factory_make_or_warn (SOURCE, "src");
634   g_object_set (G_OBJECT (src), "location", location, NULL);
635
636   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
637
638   gst_bin_add (GST_BIN (pipeline), src);
639   gst_bin_add (GST_BIN (pipeline), demux);
640   gst_element_link (src, demux);
641
642   audio_bin = gst_bin_new ("a_decoder_bin");
643   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
644   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
645   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
646   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
647
648   gst_bin_add (GST_BIN (audio_bin), a_queue);
649   gst_bin_add (GST_BIN (audio_bin), a_decoder);
650   gst_bin_add (GST_BIN (audio_bin), a_convert);
651   gst_bin_add (GST_BIN (audio_bin), audiosink);
652
653   gst_element_link (a_queue, a_decoder);
654   gst_element_link (a_decoder, a_convert);
655   gst_element_link (a_convert, audiosink);
656
657   gst_bin_add (GST_BIN (pipeline), audio_bin);
658
659   pad = gst_element_get_static_pad (a_queue, "sink");
660   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
661   gst_object_unref (pad);
662
663   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
664           "sink"), NULL);
665
666   video_bin = gst_bin_new ("v_decoder_bin");
667   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
668   v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec");
669   v_convert =
670       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
671   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
672
673   gst_bin_add (GST_BIN (video_bin), v_queue);
674   gst_bin_add (GST_BIN (video_bin), v_decoder);
675   gst_bin_add (GST_BIN (video_bin), v_convert);
676   gst_bin_add (GST_BIN (video_bin), videosink);
677
678   gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL);
679
680   gst_bin_add (GST_BIN (pipeline), video_bin);
681
682   pad = gst_element_get_static_pad (v_queue, "sink");
683   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
684   gst_object_unref (pad);
685
686   setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
687           "sink"), NULL);
688
689   seekable = gst_element_get_static_pad (a_decoder, "src");
690   seekable_pads = g_list_prepend (seekable_pads, seekable);
691   rate_pads = g_list_prepend (rate_pads, seekable);
692   rate_pads =
693       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
694           "sink"));
695
696   return pipeline;
697 }
698
699 static GstElement *
700 make_mp3_pipeline (const gchar * location)
701 {
702   GstElement *pipeline;
703   GstElement *src, *parser, *decoder, *audiosink, *queue;
704   GstPad *seekable;
705
706   pipeline = gst_pipeline_new ("app");
707
708   src = gst_element_factory_make_or_warn (SOURCE, "src");
709   parser = gst_element_factory_make_or_warn ("mp3parse", "parse");
710   decoder = gst_element_factory_make_or_warn ("mad", "dec");
711   queue = gst_element_factory_make_or_warn ("queue", "queue");
712   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
713
714   seekable_elements = g_list_prepend (seekable_elements, audiosink);
715
716   g_object_set (G_OBJECT (src), "location", location, NULL);
717   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
718
719   gst_bin_add (GST_BIN (pipeline), src);
720   gst_bin_add (GST_BIN (pipeline), parser);
721   gst_bin_add (GST_BIN (pipeline), decoder);
722   gst_bin_add (GST_BIN (pipeline), queue);
723   gst_bin_add (GST_BIN (pipeline), audiosink);
724
725   gst_element_link (src, parser);
726   gst_element_link (parser, decoder);
727   gst_element_link (decoder, queue);
728   gst_element_link (queue, audiosink);
729
730   seekable = gst_element_get_static_pad (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 (decoder, "sink"));
735
736   return pipeline;
737 }
738
739 static GstElement *
740 make_avi_pipeline (const gchar * location)
741 {
742   GstElement *pipeline, *audio_bin, *video_bin;
743   GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
744   GstElement *a_queue = NULL, *v_queue = NULL;
745   GstPad *seekable;
746
747   pipeline = gst_pipeline_new ("app");
748
749   src = gst_element_factory_make_or_warn (SOURCE, "src");
750   g_object_set (G_OBJECT (src), "location", location, NULL);
751
752   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
753   seekable_elements = g_list_prepend (seekable_elements, demux);
754
755   gst_bin_add (GST_BIN (pipeline), src);
756   gst_bin_add (GST_BIN (pipeline), demux);
757   gst_element_link (src, demux);
758
759   audio_bin = gst_bin_new ("a_decoder_bin");
760   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
761   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
762   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
763   gst_element_link (a_decoder, a_queue);
764   gst_element_link (a_queue, audiosink);
765   gst_bin_add (GST_BIN (audio_bin), a_decoder);
766   gst_bin_add (GST_BIN (audio_bin), a_queue);
767   gst_bin_add (GST_BIN (audio_bin), audiosink);
768   gst_element_set_state (audio_bin, GST_STATE_PAUSED);
769
770   setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
771           "sink"), audio_bin);
772
773   seekable = gst_element_get_static_pad (a_queue, "src");
774   seekable_pads = g_list_prepend (seekable_pads, seekable);
775   rate_pads = g_list_prepend (rate_pads, seekable);
776   rate_pads =
777       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
778           "sink"));
779
780   video_bin = gst_bin_new ("v_decoder_bin");
781   v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec");
782   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
783   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
784   gst_element_link (v_decoder, v_queue);
785   gst_element_link (v_queue, videosink);
786   gst_bin_add (GST_BIN (video_bin), v_decoder);
787   gst_bin_add (GST_BIN (video_bin), v_queue);
788   gst_bin_add (GST_BIN (video_bin), videosink);
789
790   gst_element_set_state (video_bin, GST_STATE_PAUSED);
791
792   setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
793           "sink"), video_bin);
794
795   seekable = gst_element_get_static_pad (v_queue, "src");
796   seekable_pads = g_list_prepend (seekable_pads, seekable);
797   rate_pads = g_list_prepend (rate_pads, seekable);
798   rate_pads =
799       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
800           "sink"));
801
802   return pipeline;
803 }
804
805 static GstElement *
806 make_mpeg_pipeline (const gchar * location)
807 {
808   GstElement *pipeline, *audio_bin, *video_bin;
809   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
810   GstElement *audiosink, *videosink;
811   GstElement *a_queue, *v_queue;
812   GstPad *seekable;
813   GstPad *pad;
814
815   pipeline = gst_pipeline_new ("app");
816
817   src = gst_element_factory_make_or_warn (SOURCE, "src");
818   g_object_set (G_OBJECT (src), "location", location, NULL);
819
820   //demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
821   demux = gst_element_factory_make_or_warn ("flupsdemux", "demux");
822
823   gst_bin_add (GST_BIN (pipeline), src);
824   gst_bin_add (GST_BIN (pipeline), demux);
825   gst_element_link (src, demux);
826
827   audio_bin = gst_bin_new ("a_decoder_bin");
828   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
829   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
830   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
831   gst_bin_add (GST_BIN (audio_bin), a_decoder);
832   gst_bin_add (GST_BIN (audio_bin), a_queue);
833   gst_bin_add (GST_BIN (audio_bin), audiosink);
834
835   gst_element_link (a_decoder, a_queue);
836   gst_element_link (a_queue, audiosink);
837
838   gst_bin_add (GST_BIN (pipeline), audio_bin);
839
840   pad = gst_element_get_static_pad (a_decoder, "sink");
841   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
842   gst_object_unref (pad);
843
844   setup_dynamic_link (demux, "audio_c0", gst_element_get_static_pad (audio_bin,
845           "sink"), NULL);
846
847   video_bin = gst_bin_new ("v_decoder_bin");
848   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
849   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
850   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
851   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
852
853   gst_bin_add (GST_BIN (video_bin), v_decoder);
854   gst_bin_add (GST_BIN (video_bin), v_queue);
855   gst_bin_add (GST_BIN (video_bin), v_filter);
856   gst_bin_add (GST_BIN (video_bin), videosink);
857
858   gst_element_link (v_decoder, v_queue);
859   gst_element_link (v_queue, v_filter);
860   gst_element_link (v_filter, videosink);
861
862   gst_bin_add (GST_BIN (pipeline), video_bin);
863
864   pad = gst_element_get_static_pad (v_decoder, "sink");
865   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
866   gst_object_unref (pad);
867
868   setup_dynamic_link (demux, "video_e0", gst_element_get_static_pad (video_bin,
869           "sink"), NULL);
870
871   seekable = gst_element_get_static_pad (v_filter, "src");
872   seekable_pads = g_list_prepend (seekable_pads, seekable);
873   rate_pads = g_list_prepend (rate_pads, seekable);
874   rate_pads =
875       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
876           "sink"));
877
878   return pipeline;
879 }
880
881 static GstElement *
882 make_mpegnt_pipeline (const gchar * location)
883 {
884   GstElement *pipeline, *audio_bin, *video_bin;
885   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
886   GstElement *audiosink, *videosink;
887   GstElement *a_queue;
888   GstPad *seekable;
889
890   pipeline = gst_pipeline_new ("app");
891
892   src = gst_element_factory_make_or_warn (SOURCE, "src");
893   g_object_set (G_OBJECT (src), "location", location, NULL);
894
895   demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
896   //g_object_set (G_OBJECT (demux), "sync", TRUE, NULL);
897
898   seekable_elements = g_list_prepend (seekable_elements, demux);
899
900   gst_bin_add (GST_BIN (pipeline), src);
901   gst_bin_add (GST_BIN (pipeline), demux);
902   gst_element_link (src, demux);
903
904   audio_bin = gst_bin_new ("a_decoder_bin");
905   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
906   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
907   audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
908   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
909   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
910   gst_element_link (a_decoder, a_queue);
911   gst_element_link (a_queue, audiosink);
912   gst_bin_add (GST_BIN (audio_bin), a_decoder);
913   gst_bin_add (GST_BIN (audio_bin), a_queue);
914   gst_bin_add (GST_BIN (audio_bin), audiosink);
915
916   setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
917           "sink"), audio_bin);
918
919   seekable = gst_element_get_static_pad (a_queue, "src");
920   seekable_pads = g_list_prepend (seekable_pads, seekable);
921   rate_pads = g_list_prepend (rate_pads, seekable);
922   rate_pads =
923       g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
924           "sink"));
925
926   video_bin = gst_bin_new ("v_decoder_bin");
927   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
928   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
929   videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
930   gst_element_link_many (v_decoder, v_filter, videosink, NULL);
931
932   gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL);
933
934   setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
935           "sink"), video_bin);
936
937   seekable = gst_element_get_static_pad (v_decoder, "src");
938   seekable_pads = g_list_prepend (seekable_pads, seekable);
939   rate_pads = g_list_prepend (rate_pads, seekable);
940   rate_pads =
941       g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
942           "sink"));
943
944   return pipeline;
945 }
946
947 static void
948 playerbin_set_uri (GstElement * player, const gchar * location)
949 {
950   gchar *uri;
951
952   /* Add "file://" prefix for convenience */
953   if (g_str_has_prefix (location, "/") || !gst_uri_is_valid (location)) {
954     uri = gst_filename_to_uri (location, NULL);
955     g_print ("Setting URI: %s\n", uri);
956     g_object_set (G_OBJECT (player), "uri", uri, NULL);
957     g_free (uri);
958   } else {
959     g_print ("Setting URI: %s\n", location);
960     g_object_set (G_OBJECT (player), "uri", location, NULL);
961   }
962 }
963
964 static GstElement *
965 construct_playerbin (const gchar * name, const gchar * location)
966 {
967   GstElement *player;
968   GstElement *avsink;
969
970   player = gst_element_factory_make (name, "player");
971   g_assert (player);
972
973   playerbin_set_uri (player, location);
974
975   seekable_elements = g_list_prepend (seekable_elements, player);
976
977   /* force element seeking on this pipeline */
978   elem_seek = TRUE;
979
980   avsink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
981   if (avsink)
982     g_object_set (player, "audio-sink", avsink, NULL);
983
984   avsink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
985   if (avsink)
986     g_object_set (player, "video-sink", avsink, NULL);
987
988   return player;
989 }
990
991 static GstElement *
992 make_playerbin_pipeline (const gchar * location)
993 {
994   return construct_playerbin ("playbin", location);
995 }
996
997 static GstElement *
998 make_playerbin2_pipeline (const gchar * location)
999 {
1000   GstElement *pipeline = construct_playerbin ("playbin2", location);
1001
1002   /* FIXME: this is not triggered, playbin2 is not forwarding it from the sink */
1003   g_signal_connect (pipeline, "notify::volume", G_CALLBACK (volume_notify_cb),
1004       NULL);
1005   return pipeline;
1006 }
1007
1008 #ifndef GST_DISABLE_PARSE
1009 static GstElement *
1010 make_parselaunch_pipeline (const gchar * description)
1011 {
1012   GstElement *pipeline;
1013   GError *error = NULL;
1014
1015   pipeline = gst_parse_launch (description, &error);
1016
1017   seekable_elements = g_list_prepend (seekable_elements, pipeline);
1018
1019   elem_seek = TRUE;
1020
1021   return pipeline;
1022 }
1023 #endif
1024
1025 typedef struct
1026 {
1027   const gchar *name;
1028   GstElement *(*func) (const gchar * location);
1029 }
1030 Pipeline;
1031
1032 static Pipeline pipelines[] = {
1033   {"mp3", make_mp3_pipeline},
1034   {"avi", make_avi_pipeline},
1035   {"mpeg1", make_mpeg_pipeline},
1036   {"mpegparse", make_parse_pipeline},
1037   {"vorbis", make_vorbis_pipeline},
1038   {"theora", make_theora_pipeline},
1039   {"ogg/v/t", make_vorbis_theora_pipeline},
1040   {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline},
1041   {"sid", make_sid_pipeline},
1042   {"flac", make_flac_pipeline},
1043   {"wav", make_wav_pipeline},
1044   {"mod", make_mod_pipeline},
1045   {"dv", make_dv_pipeline},
1046   {"mpeg1nothreads", make_mpegnt_pipeline},
1047   {"playerbin", make_playerbin_pipeline},
1048 #ifndef GST_DISABLE_PARSE
1049   {"parse-launch", make_parselaunch_pipeline},
1050 #endif
1051   {"playerbin2", make_playerbin2_pipeline},
1052   {NULL, NULL},
1053 };
1054
1055 #define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
1056
1057 /* ui callbacks and helpers */
1058
1059 static gchar *
1060 format_value (GtkScale * scale, gdouble value)
1061 {
1062   gint64 real;
1063   gint64 seconds;
1064   gint64 subseconds;
1065
1066   real = value * duration / N_GRAD;
1067   seconds = (gint64) real / GST_SECOND;
1068   subseconds = (gint64) real / (GST_SECOND / N_GRAD);
1069
1070   return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
1071       G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
1072 }
1073
1074
1075 static gchar *
1076 shuttle_format_value (GtkScale * scale, gdouble value)
1077 {
1078   return g_strdup_printf ("%0.*g", gtk_scale_get_digits (scale), value);
1079 }
1080
1081 typedef struct
1082 {
1083   const gchar *name;
1084   const GstFormat format;
1085 }
1086 seek_format;
1087
1088 static seek_format seek_formats[] = {
1089   {"tim", GST_FORMAT_TIME},
1090   {"byt", GST_FORMAT_BYTES},
1091   {"buf", GST_FORMAT_BUFFERS},
1092   {"def", GST_FORMAT_DEFAULT},
1093   {NULL, 0},
1094 };
1095
1096 G_GNUC_UNUSED static void
1097 query_rates (void)
1098 {
1099   GList *walk = rate_pads;
1100
1101   while (walk) {
1102     GstPad *pad = GST_PAD (walk->data);
1103     gint i = 0;
1104
1105     g_print ("rate/sec  %8.8s: ", GST_PAD_NAME (pad));
1106     while (seek_formats[i].name) {
1107       gint64 value;
1108       GstFormat format;
1109
1110       format = seek_formats[i].format;
1111
1112       if (gst_pad_query_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format,
1113               &value)) {
1114         g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value);
1115       } else {
1116         g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
1117       }
1118
1119       i++;
1120     }
1121     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1122
1123     walk = g_list_next (walk);
1124   }
1125 }
1126
1127 G_GNUC_UNUSED static void
1128 query_positions_elems (void)
1129 {
1130   GList *walk = seekable_elements;
1131
1132   while (walk) {
1133     GstElement *element = GST_ELEMENT (walk->data);
1134     gint i = 0;
1135
1136     g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
1137     while (seek_formats[i].name) {
1138       gint64 position, total;
1139       GstFormat format;
1140
1141       format = seek_formats[i].format;
1142
1143       if (gst_element_query_position (element, &format, &position) &&
1144           gst_element_query_duration (element, &format, &total)) {
1145         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1146             seek_formats[i].name, position, total);
1147       } else {
1148         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1149             "*NA*");
1150       }
1151       i++;
1152     }
1153     g_print (" %s\n", GST_ELEMENT_NAME (element));
1154
1155     walk = g_list_next (walk);
1156   }
1157 }
1158
1159 G_GNUC_UNUSED static void
1160 query_positions_pads (void)
1161 {
1162   GList *walk = seekable_pads;
1163
1164   while (walk) {
1165     GstPad *pad = GST_PAD (walk->data);
1166     gint i = 0;
1167
1168     g_print ("positions %8.8s: ", GST_PAD_NAME (pad));
1169     while (seek_formats[i].name) {
1170       GstFormat format;
1171       gint64 position, total;
1172
1173       format = seek_formats[i].format;
1174
1175       if (gst_pad_query_position (pad, &format, &position) &&
1176           gst_pad_query_duration (pad, &format, &total)) {
1177         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1178             seek_formats[i].name, position, total);
1179       } else {
1180         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1181             "*NA*");
1182       }
1183
1184       i++;
1185     }
1186     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1187
1188     walk = g_list_next (walk);
1189   }
1190 }
1191
1192 static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
1193     gpointer user_data);
1194 static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
1195     gpointer user_data);
1196 static void seek_cb (GtkWidget * widget);
1197
1198 static void
1199 set_scale (gdouble value)
1200 {
1201   g_signal_handlers_block_by_func (hscale, (void *) start_seek,
1202       (void *) pipeline);
1203   g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
1204       (void *) pipeline);
1205   g_signal_handlers_block_by_func (hscale, (void *) seek_cb, (void *) pipeline);
1206   gtk_adjustment_set_value (adjustment, value);
1207   g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
1208       (void *) pipeline);
1209   g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
1210       (void *) pipeline);
1211   g_signal_handlers_unblock_by_func (hscale, (void *) seek_cb,
1212       (void *) pipeline);
1213   gtk_widget_queue_draw (hscale);
1214 }
1215
1216 static gboolean
1217 update_fill (gpointer data)
1218 {
1219   if (elem_seek) {
1220     if (seekable_elements) {
1221       GstElement *element = GST_ELEMENT (seekable_elements->data);
1222       GstQuery *query;
1223
1224       query = gst_query_new_buffering (GST_FORMAT_PERCENT);
1225       if (gst_element_query (element, query)) {
1226         gint64 start, stop, buffering_total;
1227         GstFormat format;
1228         gdouble fill;
1229         gboolean busy;
1230         gint percent;
1231         GstBufferingMode mode;
1232         gint avg_in, avg_out;
1233         gint64 buffering_left;
1234
1235         gst_query_parse_buffering_percent (query, &busy, &percent);
1236         gst_query_parse_buffering_range (query, &format, &start, &stop,
1237             &buffering_total);
1238         gst_query_parse_buffering_stats (query, &mode, &avg_in, &avg_out,
1239             &buffering_left);
1240
1241         /* note that we could start the playback when buffering_left < remaining
1242          * playback time */
1243         GST_DEBUG ("buffering total %" G_GINT64_FORMAT " ms, left %"
1244             G_GINT64_FORMAT " ms", buffering_total, buffering_left);
1245         GST_DEBUG ("start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT,
1246             start, stop);
1247
1248         if (stop != -1)
1249           fill = N_GRAD * stop / GST_FORMAT_PERCENT_MAX;
1250         else
1251           fill = N_GRAD;
1252
1253         gtk_range_set_fill_level (GTK_RANGE (hscale), fill);
1254       }
1255       gst_query_unref (query);
1256     }
1257   }
1258   return TRUE;
1259 }
1260
1261 static gboolean
1262 update_scale (gpointer data)
1263 {
1264   GstFormat format = GST_FORMAT_TIME;
1265
1266   //position = 0;
1267   //duration = 0;
1268
1269   if (elem_seek) {
1270     if (seekable_elements) {
1271       GstElement *element = GST_ELEMENT (seekable_elements->data);
1272
1273       gst_element_query_position (element, &format, &position);
1274       gst_element_query_duration (element, &format, &duration);
1275     }
1276   } else {
1277     if (seekable_pads) {
1278       GstPad *pad = GST_PAD (seekable_pads->data);
1279
1280       gst_pad_query_position (pad, &format, &position);
1281       gst_pad_query_duration (pad, &format, &duration);
1282     }
1283   }
1284
1285   if (stats) {
1286     if (elem_seek) {
1287       query_positions_elems ();
1288     } else {
1289       query_positions_pads ();
1290     }
1291     query_rates ();
1292   }
1293
1294   if (position >= duration)
1295     duration = position;
1296
1297   if (duration > 0) {
1298     set_scale (position * N_GRAD / duration);
1299   }
1300
1301   /* FIXME: see make_playerbin2_pipeline() and volume_notify_cb() */
1302   if (pipeline_type == 16) {
1303     g_object_notify (G_OBJECT (pipeline), "volume");
1304   }
1305
1306   return TRUE;
1307 }
1308
1309 static void do_seek (GtkWidget * widget);
1310 static void connect_bus_signals (GstElement * pipeline);
1311 static void set_update_scale (gboolean active);
1312 static void set_update_fill (gboolean active);
1313
1314 static gboolean
1315 end_scrub (GtkWidget * widget)
1316 {
1317   GST_DEBUG ("end scrub, PAUSE");
1318   gst_element_set_state (pipeline, GST_STATE_PAUSED);
1319   seek_timeout_id = 0;
1320
1321   return FALSE;
1322 }
1323
1324 static gboolean
1325 send_event (GstEvent * event)
1326 {
1327   gboolean res = FALSE;
1328
1329   if (!elem_seek) {
1330     GList *walk = seekable_pads;
1331
1332     while (walk) {
1333       GstPad *seekable = GST_PAD (walk->data);
1334
1335       GST_DEBUG ("send event on pad %s:%s", GST_DEBUG_PAD_NAME (seekable));
1336
1337       gst_event_ref (event);
1338       res = gst_pad_send_event (seekable, event);
1339
1340       walk = g_list_next (walk);
1341     }
1342   } else {
1343     GList *walk = seekable_elements;
1344
1345     while (walk) {
1346       GstElement *seekable = GST_ELEMENT (walk->data);
1347
1348       GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
1349
1350       gst_event_ref (event);
1351       res = gst_element_send_event (seekable, event);
1352
1353       walk = g_list_next (walk);
1354     }
1355   }
1356   gst_event_unref (event);
1357   return res;
1358 }
1359
1360 static void
1361 do_seek (GtkWidget * widget)
1362 {
1363   gint64 real;
1364   gboolean res = FALSE;
1365   GstEvent *s_event;
1366   GstSeekFlags flags;
1367
1368   real = gtk_range_get_value (GTK_RANGE (widget)) * duration / N_GRAD;
1369
1370   GST_DEBUG ("value=%f, real=%" G_GINT64_FORMAT,
1371       gtk_range_get_value (GTK_RANGE (widget)), real);
1372
1373   flags = 0;
1374   if (flush_seek)
1375     flags |= GST_SEEK_FLAG_FLUSH;
1376   if (accurate_seek)
1377     flags |= GST_SEEK_FLAG_ACCURATE;
1378   if (keyframe_seek)
1379     flags |= GST_SEEK_FLAG_KEY_UNIT;
1380   if (loop_seek)
1381     flags |= GST_SEEK_FLAG_SEGMENT;
1382   if (skip_seek)
1383     flags |= GST_SEEK_FLAG_SKIP;
1384
1385   if (rate >= 0) {
1386     s_event = gst_event_new_seek (rate,
1387         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET,
1388         GST_CLOCK_TIME_NONE);
1389     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1390         rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
1391   } else {
1392     s_event = gst_event_new_seek (rate,
1393         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1394         GST_SEEK_TYPE_SET, real);
1395     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1396         rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
1397   }
1398
1399   res = send_event (s_event);
1400
1401   if (res) {
1402     if (flush_seek) {
1403       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1404     } else {
1405       set_update_scale (TRUE);
1406     }
1407   } else {
1408     g_print ("seek failed\n");
1409     set_update_scale (TRUE);
1410   }
1411 }
1412
1413 static void
1414 seek_cb (GtkWidget * widget)
1415 {
1416   /* If the timer hasn't expired yet, then the pipeline is running */
1417   if (play_scrub && seek_timeout_id != 0) {
1418     GST_DEBUG ("do scrub seek, PAUSED");
1419     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1420   }
1421
1422   GST_DEBUG ("do seek");
1423   do_seek (widget);
1424
1425   if (play_scrub) {
1426     GST_DEBUG ("do scrub seek, PLAYING");
1427     gst_element_set_state (pipeline, GST_STATE_PLAYING);
1428
1429     if (seek_timeout_id == 0) {
1430       seek_timeout_id =
1431           g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
1432     }
1433   }
1434 }
1435
1436 static void
1437 set_update_fill (gboolean active)
1438 {
1439   GST_DEBUG ("fill scale is %d", active);
1440
1441   if (active) {
1442     if (fill_id == 0) {
1443       fill_id =
1444           g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline);
1445     }
1446   } else {
1447     if (fill_id) {
1448       g_source_remove (fill_id);
1449       fill_id = 0;
1450     }
1451   }
1452 }
1453
1454 static void
1455 set_update_scale (gboolean active)
1456 {
1457
1458   GST_DEBUG ("update scale is %d", active);
1459
1460   if (active) {
1461     if (update_id == 0) {
1462       update_id =
1463           g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline);
1464     }
1465   } else {
1466     if (update_id) {
1467       g_source_remove (update_id);
1468       update_id = 0;
1469     }
1470   }
1471 }
1472
1473 static gboolean
1474 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1475 {
1476   if (event->type != GDK_BUTTON_PRESS)
1477     return FALSE;
1478
1479   set_update_scale (FALSE);
1480
1481   if (state == GST_STATE_PLAYING && flush_seek && scrub) {
1482     GST_DEBUG ("start scrub seek, PAUSE");
1483     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1484   }
1485
1486   if (changed_id == 0 && flush_seek && scrub) {
1487     changed_id =
1488         g_signal_connect (hscale, "value-changed", G_CALLBACK (seek_cb),
1489         pipeline);
1490   }
1491
1492   return FALSE;
1493 }
1494
1495 static gboolean
1496 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1497 {
1498   if (changed_id) {
1499     g_signal_handler_disconnect (hscale, changed_id);
1500     changed_id = 0;
1501   }
1502
1503   if (!flush_seek || !scrub) {
1504     GST_DEBUG ("do final seek");
1505     do_seek (widget);
1506   }
1507
1508   if (seek_timeout_id != 0) {
1509     g_source_remove (seek_timeout_id);
1510     seek_timeout_id = 0;
1511     /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
1512      * instead. */
1513     if (state == GST_STATE_PAUSED) {
1514       GST_DEBUG ("stop scrub seek, PAUSED");
1515       gst_element_set_state (pipeline, GST_STATE_PAUSED);
1516     }
1517   } else {
1518     if (state == GST_STATE_PLAYING) {
1519       GST_DEBUG ("stop scrub seek, PLAYING");
1520       gst_element_set_state (pipeline, GST_STATE_PLAYING);
1521     }
1522   }
1523
1524   return FALSE;
1525 }
1526
1527 static void
1528 play_cb (GtkButton * button, gpointer data)
1529 {
1530   GstStateChangeReturn ret;
1531
1532   if (state != GST_STATE_PLAYING) {
1533     g_print ("PLAY pipeline\n");
1534     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1535
1536     ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
1537     switch (ret) {
1538       case GST_STATE_CHANGE_FAILURE:
1539         goto failed;
1540       case GST_STATE_CHANGE_NO_PREROLL:
1541         is_live = TRUE;
1542         break;
1543       default:
1544         break;
1545     }
1546     state = GST_STATE_PLAYING;
1547     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
1548   }
1549
1550   return;
1551
1552 failed:
1553   {
1554     g_print ("PLAY failed\n");
1555     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
1556   }
1557 }
1558
1559 static void
1560 pause_cb (GtkButton * button, gpointer data)
1561 {
1562   g_static_mutex_lock (&state_mutex);
1563   if (state != GST_STATE_PAUSED) {
1564     GstStateChangeReturn ret;
1565
1566     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1567     g_print ("PAUSE pipeline\n");
1568     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1569     switch (ret) {
1570       case GST_STATE_CHANGE_FAILURE:
1571         goto failed;
1572       case GST_STATE_CHANGE_NO_PREROLL:
1573         is_live = TRUE;
1574         break;
1575       default:
1576         break;
1577     }
1578
1579     state = GST_STATE_PAUSED;
1580     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
1581   }
1582   g_static_mutex_unlock (&state_mutex);
1583
1584   return;
1585
1586 failed:
1587   {
1588     g_static_mutex_unlock (&state_mutex);
1589     g_print ("PAUSE failed\n");
1590     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
1591   }
1592 }
1593
1594 static void
1595 stop_cb (GtkButton * button, gpointer data)
1596 {
1597   if (state != STOP_STATE) {
1598     GstStateChangeReturn ret;
1599     gint i;
1600
1601     g_print ("READY pipeline\n");
1602     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1603
1604     g_static_mutex_lock (&state_mutex);
1605     ret = gst_element_set_state (pipeline, STOP_STATE);
1606     if (ret == GST_STATE_CHANGE_FAILURE)
1607       goto failed;
1608
1609     state = STOP_STATE;
1610     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
1611     gtk_widget_queue_draw (video_window);
1612
1613     is_live = FALSE;
1614     buffering = FALSE;
1615     set_update_scale (FALSE);
1616     set_scale (0.0);
1617     set_update_fill (FALSE);
1618
1619     if (pipeline_type == 16)
1620       clear_streams (pipeline);
1621     g_static_mutex_unlock (&state_mutex);
1622
1623 #if 0
1624     /* if one uses parse_launch, play, stop and play again it fails as all the
1625      * pads after the demuxer can't be reconnected
1626      */
1627     if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
1628       gst_element_set_state (pipeline, GST_STATE_NULL);
1629       gst_object_unref (pipeline);
1630
1631       g_list_free (seekable_elements);
1632       seekable_elements = NULL;
1633       g_list_free (seekable_pads);
1634       seekable_pads = NULL;
1635       g_list_free (rate_pads);
1636       rate_pads = NULL;
1637
1638       pipeline = pipelines[pipeline_type].func (pipeline_spec);
1639       g_assert (pipeline);
1640       gst_element_set_state (pipeline, STOP_STATE);
1641       connect_bus_signals (pipeline);
1642     }
1643 #endif
1644     gtk_widget_set_sensitive (GTK_WIDGET (hscale), TRUE);
1645     for (i = 0; i < G_N_ELEMENTS (navigation_buttons); i++)
1646       gtk_widget_set_sensitive (navigation_buttons[i].button, FALSE);
1647   }
1648   return;
1649
1650 failed:
1651   {
1652     g_static_mutex_unlock (&state_mutex);
1653     g_print ("STOP failed\n");
1654     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
1655   }
1656 }
1657
1658 static void
1659 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1660 {
1661   accurate_seek = gtk_toggle_button_get_active (button);
1662 }
1663
1664 static void
1665 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1666 {
1667   keyframe_seek = gtk_toggle_button_get_active (button);
1668 }
1669
1670 static void
1671 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1672 {
1673   loop_seek = gtk_toggle_button_get_active (button);
1674   if (state == GST_STATE_PLAYING) {
1675     do_seek (hscale);
1676   }
1677 }
1678
1679 static void
1680 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1681 {
1682   flush_seek = gtk_toggle_button_get_active (button);
1683 }
1684
1685 static void
1686 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1687 {
1688   scrub = gtk_toggle_button_get_active (button);
1689 }
1690
1691 static void
1692 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1693 {
1694   play_scrub = gtk_toggle_button_get_active (button);
1695 }
1696
1697 static void
1698 skip_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1699 {
1700   skip_seek = gtk_toggle_button_get_active (button);
1701   if (state == GST_STATE_PLAYING) {
1702     do_seek (hscale);
1703   }
1704 }
1705
1706 static void
1707 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1708 {
1709   gboolean res = FALSE;
1710   GstEvent *s_event;
1711   GstSeekFlags flags;
1712
1713   rate = gtk_spin_button_get_value (button);
1714
1715   GST_DEBUG ("rate changed to %lf", rate);
1716
1717   flags = 0;
1718   if (flush_seek)
1719     flags |= GST_SEEK_FLAG_FLUSH;
1720   if (loop_seek)
1721     flags |= GST_SEEK_FLAG_SEGMENT;
1722   if (accurate_seek)
1723     flags |= GST_SEEK_FLAG_ACCURATE;
1724   if (keyframe_seek)
1725     flags |= GST_SEEK_FLAG_KEY_UNIT;
1726   if (skip_seek)
1727     flags |= GST_SEEK_FLAG_SKIP;
1728
1729   if (rate >= 0.0) {
1730     s_event = gst_event_new_seek (rate,
1731         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1732         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1733   } else {
1734     s_event = gst_event_new_seek (rate,
1735         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1736         GST_SEEK_TYPE_SET, position);
1737   }
1738
1739   res = send_event (s_event);
1740
1741   if (res) {
1742     if (flush_seek) {
1743       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1744     }
1745   } else
1746     g_print ("seek failed\n");
1747 }
1748
1749 static void
1750 update_flag (GstPipeline * pipeline, GstPlayFlags flag, gboolean state)
1751 {
1752   gint flags;
1753
1754   g_print ("%ssetting flag 0x%08x\n", (state ? "" : "un"), flag);
1755
1756   g_object_get (pipeline, "flags", &flags, NULL);
1757   if (state)
1758     flags |= flag;
1759   else
1760     flags &= ~(flag);
1761   g_object_set (pipeline, "flags", flags, NULL);
1762 }
1763
1764 static void
1765 vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1766 {
1767   gboolean state;
1768
1769   state = gtk_toggle_button_get_active (button);
1770   update_flag (pipeline, GST_PLAY_FLAG_VIS, state);
1771   gtk_widget_set_sensitive (vis_combo, state);
1772 }
1773
1774 static void
1775 audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1776 {
1777   gboolean state;
1778
1779   state = gtk_toggle_button_get_active (button);
1780   update_flag (pipeline, GST_PLAY_FLAG_AUDIO, state);
1781   gtk_widget_set_sensitive (audio_combo, state);
1782 }
1783
1784 static void
1785 video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1786 {
1787   gboolean state;
1788
1789   state = gtk_toggle_button_get_active (button);
1790   update_flag (pipeline, GST_PLAY_FLAG_VIDEO, state);
1791   gtk_widget_set_sensitive (video_combo, state);
1792 }
1793
1794 static void
1795 text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1796 {
1797   gboolean state;
1798
1799   state = gtk_toggle_button_get_active (button);
1800   update_flag (pipeline, GST_PLAY_FLAG_TEXT, state);
1801   gtk_widget_set_sensitive (text_combo, state);
1802 }
1803
1804 static void
1805 mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1806 {
1807   gboolean mute;
1808
1809   mute = gtk_toggle_button_get_active (button);
1810   g_object_set (pipeline, "mute", mute, NULL);
1811 }
1812
1813 static void
1814 download_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1815 {
1816   gboolean state;
1817
1818   state = gtk_toggle_button_get_active (button);
1819   update_flag (pipeline, GST_PLAY_FLAG_DOWNLOAD, state);
1820 }
1821
1822 static void
1823 buffering_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1824 {
1825   gboolean state;
1826
1827   state = gtk_toggle_button_get_active (button);
1828   update_flag (pipeline, GST_PLAY_FLAG_BUFFERING, state);
1829 }
1830
1831 static void
1832 soft_volume_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1833 {
1834   gboolean state;
1835
1836   state = gtk_toggle_button_get_active (button);
1837   update_flag (pipeline, GST_PLAY_FLAG_SOFT_VOLUME, state);
1838 }
1839
1840 static void
1841 native_audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1842 {
1843   gboolean state;
1844
1845   state = gtk_toggle_button_get_active (button);
1846   update_flag (pipeline, GST_PLAY_FLAG_NATIVE_AUDIO, state);
1847 }
1848
1849 static void
1850 native_video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1851 {
1852   gboolean state;
1853
1854   state = gtk_toggle_button_get_active (button);
1855   update_flag (pipeline, GST_PLAY_FLAG_NATIVE_VIDEO, state);
1856 }
1857
1858 static void
1859 deinterlace_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1860 {
1861   gboolean state;
1862
1863   state = gtk_toggle_button_get_active (button);
1864   update_flag (pipeline, GST_PLAY_FLAG_DEINTERLACE, state);
1865 }
1866
1867 static void
1868 soft_colorbalance_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1869 {
1870   gboolean state;
1871
1872   state = gtk_toggle_button_get_active (button);
1873   update_flag (pipeline, GST_PLAY_FLAG_SOFT_COLORBALANCE, state);
1874 }
1875
1876 static void
1877 clear_streams (GstElement * pipeline)
1878 {
1879   gint i;
1880
1881   /* remove previous info */
1882   for (i = 0; i < n_video; i++)
1883     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (video_combo), 0);
1884   for (i = 0; i < n_audio; i++)
1885     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (audio_combo), 0);
1886   for (i = 0; i < n_text; i++)
1887     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (text_combo), 0);
1888
1889   n_audio = n_video = n_text = 0;
1890   gtk_widget_set_sensitive (video_combo, FALSE);
1891   gtk_widget_set_sensitive (audio_combo, FALSE);
1892   gtk_widget_set_sensitive (text_combo, FALSE);
1893
1894   need_streams = TRUE;
1895 }
1896
1897 static void
1898 update_streams (GstPipeline * pipeline)
1899 {
1900   gint i;
1901
1902   if (pipeline_type == 16 && need_streams) {
1903     GstTagList *tags;
1904     gchar *name, *str;
1905     gint active_idx;
1906     gboolean state;
1907
1908     /* remove previous info */
1909     clear_streams (GST_ELEMENT_CAST (pipeline));
1910
1911     /* here we get and update the different streams detected by playbin2 */
1912     g_object_get (pipeline, "n-video", &n_video, NULL);
1913     g_object_get (pipeline, "n-audio", &n_audio, NULL);
1914     g_object_get (pipeline, "n-text", &n_text, NULL);
1915
1916     g_print ("video %d, audio %d, text %d\n", n_video, n_audio, n_text);
1917
1918     active_idx = 0;
1919     for (i = 0; i < n_video; i++) {
1920       g_signal_emit_by_name (pipeline, "get-video-tags", i, &tags);
1921       if (tags) {
1922         str = gst_structure_to_string ((GstStructure *) tags);
1923         g_print ("video %d: %s\n", i, str);
1924         g_free (str);
1925       }
1926       /* find good name for the label */
1927       name = g_strdup_printf ("video %d", i + 1);
1928       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (video_combo), name);
1929       g_free (name);
1930     }
1931     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (video_checkbox));
1932     gtk_widget_set_sensitive (video_combo, state && n_video > 0);
1933     gtk_combo_box_set_active (GTK_COMBO_BOX (video_combo), active_idx);
1934
1935     active_idx = 0;
1936     for (i = 0; i < n_audio; i++) {
1937       g_signal_emit_by_name (pipeline, "get-audio-tags", i, &tags);
1938       if (tags) {
1939         str = gst_structure_to_string ((GstStructure *) tags);
1940         g_print ("audio %d: %s\n", i, str);
1941         g_free (str);
1942       }
1943       /* find good name for the label */
1944       name = g_strdup_printf ("audio %d", i + 1);
1945       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (audio_combo), name);
1946       g_free (name);
1947     }
1948     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (audio_checkbox));
1949     gtk_widget_set_sensitive (audio_combo, state && n_audio > 0);
1950     gtk_combo_box_set_active (GTK_COMBO_BOX (audio_combo), active_idx);
1951
1952     active_idx = 0;
1953     for (i = 0; i < n_text; i++) {
1954       g_signal_emit_by_name (pipeline, "get-text-tags", i, &tags);
1955
1956       name = NULL;
1957       if (tags) {
1958         const GValue *value;
1959
1960         str = gst_structure_to_string ((GstStructure *) tags);
1961         g_print ("text %d: %s\n", i, str);
1962         g_free (str);
1963
1964         /* get the language code if we can */
1965         value = gst_tag_list_get_value_index (tags, GST_TAG_LANGUAGE_CODE, 0);
1966         if (value && G_VALUE_HOLDS_STRING (value)) {
1967           name = g_strdup_printf ("text %s", g_value_get_string (value));
1968         }
1969       }
1970       /* find good name for the label if we didn't use a tag */
1971       if (name == NULL)
1972         name = g_strdup_printf ("text %d", i + 1);
1973
1974       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (text_combo), name);
1975       g_free (name);
1976     }
1977     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_checkbox));
1978     gtk_widget_set_sensitive (text_combo, state && n_text > 0);
1979     gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), active_idx);
1980
1981     need_streams = FALSE;
1982   }
1983 }
1984
1985 static void
1986 video_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1987 {
1988   gint active;
1989
1990   active = gtk_combo_box_get_active (combo);
1991
1992   g_print ("setting current video track %d\n", active);
1993   g_object_set (pipeline, "current-video", active, NULL);
1994 }
1995
1996 static void
1997 audio_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1998 {
1999   gint active;
2000
2001   active = gtk_combo_box_get_active (combo);
2002
2003   g_print ("setting current audio track %d\n", active);
2004   g_object_set (pipeline, "current-audio", active, NULL);
2005 }
2006
2007 static void
2008 text_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
2009 {
2010   gint active;
2011
2012   active = gtk_combo_box_get_active (combo);
2013
2014   g_print ("setting current text track %d\n", active);
2015   g_object_set (pipeline, "current-text", active, NULL);
2016 }
2017
2018 static gboolean
2019 filter_features (GstPluginFeature * feature, gpointer data)
2020 {
2021   GstElementFactory *f;
2022
2023   if (!GST_IS_ELEMENT_FACTORY (feature))
2024     return FALSE;
2025   f = GST_ELEMENT_FACTORY (feature);
2026   if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
2027     return FALSE;
2028
2029   return TRUE;
2030 }
2031
2032 static void
2033 init_visualization_features (void)
2034 {
2035   GList *list, *walk;
2036
2037   vis_entries = g_array_new (FALSE, FALSE, sizeof (VisEntry));
2038
2039   list = gst_registry_feature_filter (gst_registry_get_default (),
2040       filter_features, FALSE, NULL);
2041
2042   for (walk = list; walk; walk = g_list_next (walk)) {
2043     VisEntry entry;
2044     const gchar *name;
2045
2046     entry.factory = GST_ELEMENT_FACTORY (walk->data);
2047     name = gst_element_factory_get_longname (entry.factory);
2048
2049     g_array_append_val (vis_entries, entry);
2050     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (vis_combo), name);
2051   }
2052   gtk_combo_box_set_active (GTK_COMBO_BOX (vis_combo), 0);
2053 }
2054
2055 static void
2056 vis_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
2057 {
2058   guint index;
2059   VisEntry *entry;
2060   GstElement *element;
2061
2062   /* get the selected index and get the factory for this index */
2063   index = gtk_combo_box_get_active (GTK_COMBO_BOX (vis_combo));
2064   if (vis_entries->len > 0) {
2065     entry = &g_array_index (vis_entries, VisEntry, index);
2066
2067     /* create an instance of the element from the factory */
2068     element = gst_element_factory_create (entry->factory, NULL);
2069     if (!element)
2070       return;
2071
2072     /* set vis plugin for playbin2 */
2073     g_object_set (pipeline, "vis-plugin", element, NULL);
2074   }
2075 }
2076
2077 static void
2078 volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
2079 {
2080   gdouble volume;
2081
2082   volume = gtk_spin_button_get_value (button);
2083
2084   g_object_set (pipeline, "volume", volume, NULL);
2085 }
2086
2087 static void
2088 volume_notify_cb (GstElement * pipeline, GParamSpec * arg, gpointer user_dat)
2089 {
2090   gdouble cur_volume, new_volume;
2091
2092   g_object_get (pipeline, "volume", &new_volume, NULL);
2093   cur_volume = gtk_spin_button_get_value (GTK_SPIN_BUTTON (volume_spinbutton));
2094   if (fabs (cur_volume - new_volume) > 0.001) {
2095     g_signal_handlers_block_by_func (volume_spinbutton,
2096         volume_spinbutton_changed_cb, pipeline);
2097     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), new_volume);
2098     g_signal_handlers_unblock_by_func (volume_spinbutton,
2099         volume_spinbutton_changed_cb, pipeline);
2100   }
2101 }
2102
2103 static void
2104 shot_cb (GtkButton * button, gpointer data)
2105 {
2106   GstBuffer *buffer;
2107   GstCaps *caps;
2108
2109   /* convert to our desired format (RGB24) */
2110   caps = gst_caps_new_simple ("video/x-raw-rgb",
2111       "bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
2112       /* Note: we don't ask for a specific width/height here, so that
2113        * videoscale can adjust dimensions from a non-1/1 pixel aspect
2114        * ratio to a 1/1 pixel-aspect-ratio */
2115       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
2116       "endianness", G_TYPE_INT, G_BIG_ENDIAN,
2117       "red_mask", G_TYPE_INT, 0xff0000,
2118       "green_mask", G_TYPE_INT, 0x00ff00,
2119       "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
2120
2121   /* convert the latest frame to the requested format */
2122   g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
2123   gst_caps_unref (caps);
2124
2125   if (buffer) {
2126     GstCaps *caps;
2127     GstStructure *s;
2128     gboolean res;
2129     gint width, height;
2130     GdkPixbuf *pixbuf;
2131     GError *error = NULL;
2132
2133     /* get the snapshot buffer format now. We set the caps on the appsink so
2134      * that it can only be an rgb buffer. The only thing we have not specified
2135      * on the caps is the height, which is dependant on the pixel-aspect-ratio
2136      * of the source material */
2137     caps = GST_BUFFER_CAPS (buffer);
2138     if (!caps) {
2139       g_warning ("could not get snapshot format\n");
2140       goto done;
2141     }
2142     s = gst_caps_get_structure (caps, 0);
2143
2144     /* we need to get the final caps on the buffer to get the size */
2145     res = gst_structure_get_int (s, "width", &width);
2146     res |= gst_structure_get_int (s, "height", &height);
2147     if (!res) {
2148       g_warning ("could not get snapshot dimension\n");
2149       goto done;
2150     }
2151
2152     /* create pixmap from buffer and save, gstreamer video buffers have a stride
2153      * that is rounded up to the nearest multiple of 4 */
2154     pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
2155         GDK_COLORSPACE_RGB, FALSE, 8, width, height,
2156         GST_ROUND_UP_4 (width * 3), NULL, NULL);
2157
2158     /* save the pixbuf */
2159     gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
2160
2161   done:
2162     gst_buffer_unref (buffer);
2163   }
2164 }
2165
2166 /* called when the Step button is pressed */
2167 static void
2168 step_cb (GtkButton * button, gpointer data)
2169 {
2170   GstEvent *event;
2171   GstFormat format;
2172   guint64 amount;
2173   gdouble rate;
2174   gboolean flush, res;
2175   gint active;
2176
2177   active = gtk_combo_box_get_active (GTK_COMBO_BOX (format_combo));
2178   amount =
2179       gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON
2180       (step_amount_spinbutton));
2181   rate = gtk_spin_button_get_value (GTK_SPIN_BUTTON (step_rate_spinbutton));
2182   flush = TRUE;
2183
2184   switch (active) {
2185     case 0:
2186       format = GST_FORMAT_BUFFERS;
2187       break;
2188     case 1:
2189       format = GST_FORMAT_TIME;
2190       amount *= GST_MSECOND;
2191       break;
2192     default:
2193       format = GST_FORMAT_UNDEFINED;
2194       break;
2195   }
2196
2197   event = gst_event_new_step (format, amount, rate, flush, FALSE);
2198
2199   res = send_event (event);
2200
2201   if (!res) {
2202     g_print ("Sending step event failed\n");
2203   }
2204 }
2205
2206 static void
2207 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2208 {
2209   const GstStructure *s;
2210
2211   switch (GST_MESSAGE_TYPE (message)) {
2212     case GST_MESSAGE_ERROR:
2213       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2214           GST_DEBUG_GRAPH_SHOW_ALL, "seek.error");
2215       break;
2216     case GST_MESSAGE_WARNING:
2217       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2218           GST_DEBUG_GRAPH_SHOW_ALL, "seek.warning");
2219       break;
2220     default:
2221       break;
2222   }
2223
2224   s = gst_message_get_structure (message);
2225   g_print ("message from \"%s\" (%s): ",
2226       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
2227       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2228   if (s) {
2229     gchar *sstr;
2230
2231     sstr = gst_structure_to_string (s);
2232     g_print ("%s\n", sstr);
2233     g_free (sstr);
2234   } else {
2235     g_print ("no message details\n");
2236   }
2237 }
2238
2239 static gboolean shuttling = FALSE;
2240 static gdouble shuttle_rate = 0.0;
2241 static gdouble play_rate = 1.0;
2242
2243 static void
2244 do_shuttle (GstElement * element)
2245 {
2246   guint64 duration;
2247
2248   if (shuttling)
2249     duration = 40 * GST_MSECOND;
2250   else
2251     duration = -1;
2252
2253   gst_element_send_event (element,
2254       gst_event_new_step (GST_FORMAT_TIME, duration, shuttle_rate, FALSE,
2255           FALSE));
2256 }
2257
2258 static void
2259 msg_sync_step_done (GstBus * bus, GstMessage * message, GstElement * element)
2260 {
2261   GstFormat format;
2262   guint64 amount;
2263   gdouble rate;
2264   gboolean flush;
2265   gboolean intermediate;
2266   guint64 duration;
2267   gboolean eos;
2268
2269   gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
2270       &intermediate, &duration, &eos);
2271
2272   if (eos) {
2273     g_print ("stepped till EOS\n");
2274     return;
2275   }
2276
2277   if (g_static_mutex_trylock (&state_mutex)) {
2278     if (shuttling)
2279       do_shuttle (element);
2280     g_static_mutex_unlock (&state_mutex);
2281   } else {
2282     /* ignore step messages that come while we are doing a state change */
2283     g_print ("state change is busy\n");
2284   }
2285 }
2286
2287 static void
2288 shuttle_toggled (GtkToggleButton * button, GstElement * element)
2289 {
2290   gboolean active;
2291
2292   active = gtk_toggle_button_get_active (button);
2293
2294   if (active != shuttling) {
2295     shuttling = active;
2296     g_print ("shuttling %s\n", shuttling ? "active" : "inactive");
2297     if (active) {
2298       shuttle_rate = 0.0;
2299       play_rate = 1.0;
2300       pause_cb (NULL, NULL);
2301       gst_element_get_state (element, NULL, NULL, -1);
2302     }
2303   }
2304 }
2305
2306 static void
2307 shuttle_rate_switch (GstElement * element)
2308 {
2309   GstSeekFlags flags;
2310   GstEvent *s_event;
2311   gboolean res;
2312
2313   if (state == GST_STATE_PLAYING) {
2314     /* pause when we need to */
2315     pause_cb (NULL, NULL);
2316     gst_element_get_state (element, NULL, NULL, -1);
2317   }
2318
2319   if (play_rate == 1.0)
2320     play_rate = -1.0;
2321   else
2322     play_rate = 1.0;
2323
2324   g_print ("rate changed to %lf %" GST_TIME_FORMAT "\n", play_rate,
2325       GST_TIME_ARGS (position));
2326
2327   flags = GST_SEEK_FLAG_FLUSH;
2328   flags |= GST_SEEK_FLAG_ACCURATE;
2329
2330   if (play_rate >= 0.0) {
2331     s_event = gst_event_new_seek (play_rate,
2332         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
2333         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
2334   } else {
2335     s_event = gst_event_new_seek (play_rate,
2336         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2337         GST_SEEK_TYPE_SET, position);
2338   }
2339   res = send_event (s_event);
2340   if (res) {
2341     gst_element_get_state (element, NULL, NULL, SEEK_TIMEOUT);
2342   } else {
2343     g_print ("seek failed\n");
2344   }
2345 }
2346
2347 static void
2348 shuttle_value_changed (GtkRange * range, GstElement * element)
2349 {
2350   gdouble rate;
2351
2352   rate = gtk_adjustment_get_value (shuttle_adjustment);
2353
2354   if (rate == 0.0) {
2355     g_print ("rate 0.0, pause\n");
2356     pause_cb (NULL, NULL);
2357     gst_element_get_state (element, NULL, NULL, -1);
2358   } else {
2359     g_print ("rate changed %0.3g\n", rate);
2360
2361     if ((rate < 0.0 && play_rate > 0.0) || (rate > 0.0 && play_rate < 0.0)) {
2362       shuttle_rate_switch (element);
2363     }
2364
2365     shuttle_rate = ABS (rate);
2366     if (state != GST_STATE_PLAYING) {
2367       do_shuttle (element);
2368       play_cb (NULL, NULL);
2369     }
2370   }
2371 }
2372
2373 static void
2374 colorbalance_value_changed (GtkRange * range, gpointer user_data)
2375 {
2376   const gchar *label = user_data;
2377   gdouble val;
2378   gint ival;
2379   GstColorBalanceChannel *channel = NULL;
2380   const GList *channels, *l;
2381
2382   val = gtk_range_get_value (range);
2383
2384   g_print ("colorbalance %s value changed %lf\n", label, val / 100.);
2385
2386   if (!colorbalance_element) {
2387     find_interface_elements ();
2388     if (!colorbalance_element)
2389       return;
2390   }
2391
2392   channels =
2393       gst_color_balance_list_channels (GST_COLOR_BALANCE
2394       (colorbalance_element));
2395   for (l = channels; l; l = l->next) {
2396     GstColorBalanceChannel *tmp = l->data;
2397
2398     if (g_strrstr (tmp->label, label)) {
2399       channel = tmp;
2400       break;
2401     }
2402   }
2403
2404   if (!channel)
2405     return;
2406
2407   ival =
2408       (gint) (0.5 + channel->min_value +
2409       (val / 100.0) * ((gdouble) channel->max_value -
2410           (gdouble) channel->min_value));
2411   gst_color_balance_set_value (GST_COLOR_BALANCE (colorbalance_element),
2412       channel, ival);
2413 }
2414
2415 static void
2416 msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2417 {
2418   GST_DEBUG ("async done");
2419   /* when we get ASYNC_DONE we can query position, duration and other
2420    * properties */
2421   update_scale (pipeline);
2422
2423   /* update the available streams */
2424   update_streams (pipeline);
2425
2426   find_interface_elements ();
2427 }
2428
2429 static void
2430 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2431 {
2432   const GstStructure *s;
2433
2434   s = gst_message_get_structure (message);
2435
2436   /* We only care about state changed on the pipeline */
2437   if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
2438     GstState old, new, pending;
2439
2440     gst_message_parse_state_changed (message, &old, &new, &pending);
2441
2442     /* When state of the pipeline changes to paused or playing we start updating scale */
2443     if (new == GST_STATE_PLAYING) {
2444       set_update_scale (TRUE);
2445     } else {
2446       set_update_scale (FALSE);
2447     }
2448
2449     /* dump graph for (some) pipeline state changes */
2450     {
2451       gchar *dump_name;
2452
2453       dump_name = g_strdup_printf ("seek.%s_%s",
2454           gst_element_state_get_name (old), gst_element_state_get_name (new));
2455
2456       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2457           GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
2458
2459       g_free (dump_name);
2460     }
2461   }
2462 }
2463
2464 static void
2465 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2466 {
2467   GstEvent *s_event;
2468   GstSeekFlags flags;
2469   gboolean res;
2470   GstFormat format;
2471
2472   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2473   gst_message_parse_segment_done (message, &format, &position);
2474   GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2475
2476   flags = 0;
2477   /* in the segment-done callback we never flush as this would not make sense
2478    * for seamless playback. */
2479   if (loop_seek)
2480     flags |= GST_SEEK_FLAG_SEGMENT;
2481   if (skip_seek)
2482     flags |= GST_SEEK_FLAG_SKIP;
2483
2484   s_event = gst_event_new_seek (rate,
2485       GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2486       GST_SEEK_TYPE_SET, duration);
2487
2488   GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
2489       rate, GST_TIME_ARGS (duration));
2490
2491   res = send_event (s_event);
2492   if (!res)
2493     g_print ("segment seek failed\n");
2494 }
2495
2496 /* in stream buffering mode we PAUSE the pipeline until we receive a 100%
2497  * message */
2498 static void
2499 do_stream_buffering (gint percent)
2500 {
2501   gchar *bufstr;
2502
2503   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2504   bufstr = g_strdup_printf ("Buffering...%d", percent);
2505   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2506   g_free (bufstr);
2507
2508   if (percent == 100) {
2509     /* a 100% message means buffering is done */
2510     buffering = FALSE;
2511     /* if the desired state is playing, go back */
2512     if (state == GST_STATE_PLAYING) {
2513       /* no state management needed for live pipelines */
2514       if (!is_live) {
2515         fprintf (stderr, "Done buffering, setting pipeline to PLAYING ...\n");
2516         gst_element_set_state (pipeline, GST_STATE_PLAYING);
2517       }
2518       gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2519       gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
2520     }
2521   } else {
2522     /* buffering busy */
2523     if (buffering == FALSE && state == GST_STATE_PLAYING) {
2524       /* we were not buffering but PLAYING, PAUSE  the pipeline. */
2525       if (!is_live) {
2526         fprintf (stderr, "Buffering, setting pipeline to PAUSED ...\n");
2527         gst_element_set_state (pipeline, GST_STATE_PAUSED);
2528       }
2529     }
2530     buffering = TRUE;
2531   }
2532 }
2533
2534 static void
2535 do_download_buffering (gint percent)
2536 {
2537   if (!buffering && percent < 100) {
2538     gchar *bufstr;
2539
2540     buffering = TRUE;
2541
2542     bufstr = g_strdup_printf ("Downloading...");
2543     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2544     g_free (bufstr);
2545
2546     /* once we get a buffering message, we'll do the fill update */
2547     set_update_fill (TRUE);
2548
2549     if (state == GST_STATE_PLAYING && !is_live) {
2550       fprintf (stderr, "Downloading, setting pipeline to PAUSED ...\n");
2551       gst_element_set_state (pipeline, GST_STATE_PAUSED);
2552       /* user has to manually start the playback */
2553       state = GST_STATE_PAUSED;
2554     }
2555   }
2556 }
2557
2558 static void
2559 msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data)
2560 {
2561   gint percent;
2562
2563   gst_message_parse_buffering (message, &percent);
2564
2565   /* get more stats */
2566   gst_message_parse_buffering_stats (message, &mode, NULL, NULL,
2567       &buffering_left);
2568
2569   switch (mode) {
2570     case GST_BUFFERING_DOWNLOAD:
2571       do_download_buffering (percent);
2572       break;
2573     case GST_BUFFERING_LIVE:
2574       is_live = TRUE;
2575     case GST_BUFFERING_TIMESHIFT:
2576     case GST_BUFFERING_STREAM:
2577       do_stream_buffering (percent);
2578       break;
2579   }
2580 }
2581
2582 static void
2583 msg_clock_lost (GstBus * bus, GstMessage * message, GstPipeline * data)
2584 {
2585   g_print ("clock lost! PAUSE and PLAY to select a new clock\n");
2586   if (state == GST_STATE_PLAYING) {
2587     gst_element_set_state (pipeline, GST_STATE_PAUSED);
2588     gst_element_set_state (pipeline, GST_STATE_PLAYING);
2589   }
2590 }
2591
2592 static gboolean
2593 is_valid_color_balance_element (GstElement * element)
2594 {
2595   GstColorBalance *bal = GST_COLOR_BALANCE (element);
2596   gboolean have_brightness = FALSE;
2597   gboolean have_contrast = FALSE;
2598   gboolean have_hue = FALSE;
2599   gboolean have_saturation = FALSE;
2600   const GList *channels, *l;
2601
2602   channels = gst_color_balance_list_channels (bal);
2603   for (l = channels; l; l = l->next) {
2604     GstColorBalanceChannel *ch = l->data;
2605
2606     if (g_strrstr (ch->label, "BRIGHTNESS"))
2607       have_brightness = TRUE;
2608     else if (g_strrstr (ch->label, "CONTRAST"))
2609       have_contrast = TRUE;
2610     else if (g_strrstr (ch->label, "HUE"))
2611       have_hue = TRUE;
2612     else if (g_strrstr (ch->label, "SATURATION"))
2613       have_saturation = TRUE;
2614   }
2615
2616   return have_brightness && have_contrast && have_hue && have_saturation;
2617 }
2618
2619 static void
2620 find_interface_elements (void)
2621 {
2622   GstIterator *it;
2623   gpointer item;
2624   gboolean done = FALSE, hardware = FALSE;
2625
2626   if (navigation_element)
2627     gst_object_unref (navigation_element);
2628   navigation_element = NULL;
2629
2630   if (colorbalance_element)
2631     gst_object_unref (colorbalance_element);
2632   colorbalance_element = NULL;
2633
2634   if (pipeline_type != 16)
2635     navigation_element =
2636         gst_bin_get_by_interface (GST_BIN (pipeline), GST_TYPE_NAVIGATION);
2637
2638   it = gst_bin_iterate_all_by_interface (GST_BIN (pipeline),
2639       GST_TYPE_COLOR_BALANCE);
2640   while (!done) {
2641     switch (gst_iterator_next (it, &item)) {
2642       case GST_ITERATOR_OK:{
2643         GstElement *element = GST_ELEMENT (item);
2644
2645         if (is_valid_color_balance_element (element)) {
2646           if (!colorbalance_element) {
2647             colorbalance_element = GST_ELEMENT_CAST (gst_object_ref (element));
2648             hardware =
2649                 (gst_color_balance_get_balance_type (GST_COLOR_BALANCE
2650                     (element)) == GST_COLOR_BALANCE_HARDWARE);
2651           } else if (!hardware) {
2652             gboolean tmp =
2653                 (gst_color_balance_get_balance_type (GST_COLOR_BALANCE
2654                     (element)) == GST_COLOR_BALANCE_HARDWARE);
2655
2656             if (tmp) {
2657               if (colorbalance_element)
2658                 gst_object_unref (colorbalance_element);
2659               colorbalance_element =
2660                   GST_ELEMENT_CAST (gst_object_ref (element));
2661               hardware = TRUE;
2662             }
2663           }
2664         }
2665
2666         gst_object_unref (element);
2667
2668         if (hardware && colorbalance_element)
2669           done = TRUE;
2670         break;
2671       }
2672       case GST_ITERATOR_RESYNC:
2673         gst_iterator_resync (it);
2674         done = FALSE;
2675         hardware = FALSE;
2676         if (colorbalance_element)
2677           gst_object_unref (colorbalance_element);
2678         colorbalance_element = NULL;
2679         break;
2680       case GST_ITERATOR_DONE:
2681       case GST_ITERATOR_ERROR:
2682       default:
2683         done = TRUE;
2684     }
2685   }
2686
2687   gst_iterator_free (it);
2688 }
2689
2690 /* called when Navigation command button is pressed */
2691 static void
2692 navigation_cmd_cb (GtkButton * button, gpointer data)
2693 {
2694   GstNavigationCommand cmd = GPOINTER_TO_INT (data);
2695
2696   if (pipeline_type == 16) {
2697     gst_navigation_send_command (GST_NAVIGATION (pipeline), cmd);
2698   } else {
2699     if (!navigation_element) {
2700       find_interface_elements ();
2701       if (!navigation_element)
2702         return;
2703     }
2704
2705     gst_navigation_send_command (GST_NAVIGATION (navigation_element), cmd);
2706   }
2707 }
2708
2709 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
2710
2711 static GstElement *xoverlay_element = NULL;
2712 static guintptr embed_xid = 0;
2713
2714 /* We set the xid here in response to the prepare-xwindow-id message via a
2715  * bus sync handler because we don't know the actual videosink used from the
2716  * start (as we don't know the pipeline, or bin elements such as autovideosink
2717  * or gconfvideosink may be used which create the actual videosink only once
2718  * the pipeline is started) */
2719 static GstBusSyncReply
2720 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
2721 {
2722   if ((GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) &&
2723       gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
2724     GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (message));
2725
2726     if (xoverlay_element)
2727       gst_object_unref (xoverlay_element);
2728     xoverlay_element = GST_ELEMENT (gst_object_ref (element));
2729
2730     g_print ("got prepare-xwindow-id, setting XID %" G_GUINTPTR_FORMAT "\n",
2731         embed_xid);
2732
2733     if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
2734             "force-aspect-ratio")) {
2735       g_object_set (element, "force-aspect-ratio", TRUE, NULL);
2736     }
2737
2738     /* Should have been initialised from main thread before (can't use
2739      * GDK_WINDOW_XID here with Gtk+ >= 2.18, because the sync handler will
2740      * be called from a streaming thread and GDK_WINDOW_XID maps to more than
2741      * a simple structure lookup with Gtk+ >= 2.18, where 'more' is stuff that
2742      * shouldn't be done from a non-GUI thread without explicit locking).  */
2743     g_assert (embed_xid != 0);
2744
2745     gst_x_overlay_set_window_handle (GST_X_OVERLAY (element), embed_xid);
2746     gst_x_overlay_handle_events (GST_X_OVERLAY (element), FALSE);
2747
2748     find_interface_elements ();
2749   }
2750   return GST_BUS_PASS;
2751 }
2752 #endif
2753
2754 static gboolean
2755 draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data)
2756 {
2757   if (state < GST_STATE_PAUSED) {
2758     int width, height;
2759
2760     width = gtk_widget_get_allocated_width (widget);
2761     height = gtk_widget_get_allocated_height (widget);
2762     cairo_set_source_rgb (cr, 0, 0, 0);
2763     cairo_rectangle (cr, 0, 0, width, height);
2764     cairo_fill (cr);
2765     return TRUE;
2766   }
2767
2768   if (xoverlay_element)
2769     gst_x_overlay_expose (GST_X_OVERLAY (xoverlay_element));
2770   else if (pipeline_type == 16)
2771     gst_x_overlay_expose (GST_X_OVERLAY (pipeline));
2772
2773   return FALSE;
2774 }
2775
2776 static void
2777 realize_cb (GtkWidget * widget, gpointer data)
2778 {
2779   GdkWindow *window = gtk_widget_get_window (widget);
2780
2781   /* This is here just for pedagogical purposes, GDK_WINDOW_XID will call it
2782    * as well */
2783   if (!gdk_window_ensure_native (window))
2784     g_error ("Couldn't create native window needed for GstXOverlay!");
2785
2786 #if defined (GDK_WINDOWING_WIN32)
2787   embed_xid = GDK_WINDOW_HWND (window);
2788   g_print ("Window realize: video window HWND = %lu\n", embed_xid);
2789 #elif defined (GDK_WINDOWING_QUARTZ)
2790   embed_xid = gdk_quartz_window_get_nsview (window);
2791   g_print ("Window realize: video window NSView = %p\n", embed_xid);
2792 #elif defined (GDK_WINDOWING_X11)
2793   embed_xid = GDK_WINDOW_XID (window);
2794   g_print ("Window realize: video window XID = %" G_GUINTPTR_FORMAT "\n",
2795       embed_xid);
2796 #endif
2797 }
2798
2799 static gboolean
2800 button_press_cb (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
2801 {
2802   gtk_widget_grab_focus (widget);
2803
2804   if (navigation_element)
2805     gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2806         "mouse-button-press", event->button, event->x, event->y);
2807   else if (pipeline_type == 16)
2808     gst_navigation_send_mouse_event (GST_NAVIGATION (pipeline),
2809         "mouse-button-press", event->button, event->x, event->y);
2810
2811   return FALSE;
2812 }
2813
2814 static gboolean
2815 button_release_cb (GtkWidget * widget, GdkEventButton * event,
2816     gpointer user_data)
2817 {
2818   if (navigation_element)
2819     gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2820         "mouse-button-release", event->button, event->x, event->y);
2821   else if (pipeline_type == 16)
2822     gst_navigation_send_mouse_event (GST_NAVIGATION (pipeline),
2823         "mouse-button-release", event->button, event->x, event->y);
2824
2825   return FALSE;
2826 }
2827
2828 static gboolean
2829 key_press_cb (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
2830 {
2831   if (navigation_element)
2832     gst_navigation_send_key_event (GST_NAVIGATION (navigation_element),
2833         "key-press", gdk_keyval_name (event->keyval));
2834   else if (pipeline_type == 16)
2835     gst_navigation_send_key_event (GST_NAVIGATION (pipeline),
2836         "key-press", gdk_keyval_name (event->keyval));
2837
2838   return FALSE;
2839 }
2840
2841 static gboolean
2842 key_release_cb (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
2843 {
2844   if (navigation_element)
2845     gst_navigation_send_key_event (GST_NAVIGATION (navigation_element),
2846         "key-release", gdk_keyval_name (event->keyval));
2847   else if (pipeline_type == 16)
2848     gst_navigation_send_key_event (GST_NAVIGATION (pipeline),
2849         "key-release", gdk_keyval_name (event->keyval));
2850
2851   return FALSE;
2852 }
2853
2854 static gboolean
2855 motion_notify_cb (GtkWidget * widget, GdkEventMotion * event,
2856     gpointer user_data)
2857 {
2858   if (navigation_element)
2859     gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2860         "mouse-move", 0, event->x, event->y);
2861   else if (pipeline_type == 16)
2862     gst_navigation_send_mouse_event (GST_NAVIGATION (pipeline),
2863         "mouse-move", 0, event->x, event->y);
2864
2865   return FALSE;
2866 }
2867
2868 static void
2869 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * data)
2870 {
2871   message_received (bus, message, data);
2872
2873   /* Set new uri for playerbins and continue playback */
2874   if (l && (pipeline_type == 14 || pipeline_type == 16)) {
2875     stop_cb (NULL, NULL);
2876     l = g_list_next (l);
2877     if (l) {
2878       playerbin_set_uri (GST_ELEMENT (data), l->data);
2879       play_cb (NULL, NULL);
2880     }
2881   }
2882 }
2883
2884 static void
2885 msg_step_done (GstBus * bus, GstMessage * message, GstPipeline * data)
2886 {
2887   if (!shuttling)
2888     message_received (bus, message, data);
2889 }
2890
2891 static void
2892 msg (GstBus * bus, GstMessage * message, GstPipeline * data)
2893 {
2894   GstNavigationMessageType nav_type;
2895
2896   nav_type = gst_navigation_message_get_type (message);
2897   switch (nav_type) {
2898     case GST_NAVIGATION_MESSAGE_COMMANDS_CHANGED:{
2899       GstQuery *query;
2900       gboolean res, j;
2901
2902       /* Heuristic to detect if we're dealing with a DVD menu */
2903       query = gst_navigation_query_new_commands ();
2904       res = gst_element_query (GST_ELEMENT (GST_MESSAGE_SRC (message)), query);
2905
2906       for (j = 0; j < G_N_ELEMENTS (navigation_buttons); j++)
2907         gtk_widget_set_sensitive (navigation_buttons[j].button, FALSE);
2908
2909       if (res) {
2910         gboolean is_menu = FALSE;
2911         guint i, n;
2912
2913         if (gst_navigation_query_parse_commands_length (query, &n)) {
2914           for (i = 0; i < n; i++) {
2915             GstNavigationCommand cmd;
2916
2917             if (!gst_navigation_query_parse_commands_nth (query, i, &cmd))
2918               break;
2919
2920             is_menu |= (cmd == GST_NAVIGATION_COMMAND_ACTIVATE);
2921             is_menu |= (cmd == GST_NAVIGATION_COMMAND_LEFT);
2922             is_menu |= (cmd == GST_NAVIGATION_COMMAND_RIGHT);
2923             is_menu |= (cmd == GST_NAVIGATION_COMMAND_UP);
2924             is_menu |= (cmd == GST_NAVIGATION_COMMAND_DOWN);
2925
2926             for (j = 0; j < G_N_ELEMENTS (navigation_buttons); j++) {
2927               if (navigation_buttons[j].cmd != cmd)
2928                 continue;
2929
2930               gtk_widget_set_sensitive (navigation_buttons[j].button, TRUE);
2931             }
2932           }
2933         }
2934
2935         gtk_widget_set_sensitive (GTK_WIDGET (hscale), !is_menu);
2936       } else {
2937         g_assert_not_reached ();
2938       }
2939
2940       gst_query_unref (query);
2941       break;
2942     }
2943     default:
2944       break;
2945   }
2946 }
2947
2948 static void
2949 connect_bus_signals (GstElement * pipeline)
2950 {
2951   GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
2952
2953 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
2954   if (pipeline_type != 16) {
2955     /* handle prepare-xwindow-id element message synchronously, but only for non-playbin2 */
2956     gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler,
2957         pipeline);
2958   }
2959 #endif
2960
2961   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
2962   gst_bus_enable_sync_message_emission (bus);
2963
2964   g_signal_connect (bus, "message::state-changed",
2965       (GCallback) msg_state_changed, pipeline);
2966   g_signal_connect (bus, "message::segment-done", (GCallback) msg_segment_done,
2967       pipeline);
2968   g_signal_connect (bus, "message::async-done", (GCallback) msg_async_done,
2969       pipeline);
2970
2971   g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
2972       pipeline);
2973   g_signal_connect (bus, "message::clock-lost", (GCallback) msg_clock_lost,
2974       pipeline);
2975   g_signal_connect (bus, "message::error", (GCallback) message_received,
2976       pipeline);
2977   g_signal_connect (bus, "message::warning", (GCallback) message_received,
2978       pipeline);
2979   g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
2980   g_signal_connect (bus, "message::tag", (GCallback) message_received,
2981       pipeline);
2982   g_signal_connect (bus, "message::element", (GCallback) message_received,
2983       pipeline);
2984   g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
2985       pipeline);
2986   g_signal_connect (bus, "message::buffering", (GCallback) msg_buffering,
2987       pipeline);
2988 //  g_signal_connect (bus, "message::step-done", (GCallback) msg_step_done,
2989 //      pipeline);
2990   g_signal_connect (bus, "message::step-start", (GCallback) msg_step_done,
2991       pipeline);
2992   g_signal_connect (bus, "sync-message::step-done",
2993       (GCallback) msg_sync_step_done, pipeline);
2994   g_signal_connect (bus, "message", (GCallback) msg, pipeline);
2995
2996   gst_object_unref (bus);
2997 }
2998
2999 /* Return GList of paths described in location string */
3000 static GList *
3001 handle_wildcards (const gchar * location)
3002 {
3003   GList *res = NULL;
3004   gchar *path = g_path_get_dirname (location);
3005   gchar *pattern = g_path_get_basename (location);
3006   GPatternSpec *pspec = g_pattern_spec_new (pattern);
3007   GDir *dir = g_dir_open (path, 0, NULL);
3008   const gchar *name;
3009
3010   g_print ("matching %s from %s\n", pattern, path);
3011
3012   if (!dir) {
3013     g_print ("opening directory %s failed\n", path);
3014     goto out;
3015   }
3016
3017   while ((name = g_dir_read_name (dir)) != NULL) {
3018     if (g_pattern_match_string (pspec, name)) {
3019       res = g_list_append (res, g_strjoin ("/", path, name, NULL));
3020       g_print ("  found clip %s\n", name);
3021     }
3022   }
3023
3024   g_dir_close (dir);
3025 out:
3026   g_pattern_spec_free (pspec);
3027   g_free (pattern);
3028   g_free (path);
3029
3030   return res;
3031 }
3032
3033 static void
3034 delete_event_cb (void)
3035 {
3036   stop_cb (NULL, NULL);
3037   gtk_main_quit ();
3038 }
3039
3040 static void
3041 print_usage (int argc, char **argv)
3042 {
3043   gint i;
3044
3045   g_print ("usage: %s <type> <filename>\n", argv[0]);
3046   g_print ("   possible types:\n");
3047
3048   for (i = 0; i < NUM_TYPES; i++) {
3049     g_print ("     %d = %s\n", i, pipelines[i].name);
3050   }
3051 }
3052
3053 int
3054 main (int argc, char **argv)
3055 {
3056   GtkWidget *window, *hbox, *vbox, *panel, *expander, *pb2vbox, *boxes,
3057       *flagtable, *boxes2, *step, *navigation, *colorbalance = NULL;
3058   GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
3059   GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
3060   GtkWidget *scrub_checkbox, *play_scrub_checkbox;
3061   GtkWidget *rate_label, *volume_label;
3062   GOptionEntry options[] = {
3063     {"audiosink", '\0', 0, G_OPTION_ARG_STRING, &opt_audiosink_str,
3064         "audio sink to use (default: " DEFAULT_AUDIOSINK ")", NULL},
3065     {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
3066         "Show pad stats", NULL},
3067     {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek,
3068         "Seek on elements instead of pads", NULL},
3069     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
3070         "Verbose properties", NULL},
3071     {"videosink", '\0', 0, G_OPTION_ARG_STRING, &opt_videosink_str,
3072         "video sink to use (default: " DEFAULT_VIDEOSINK ")", NULL},
3073     {NULL}
3074   };
3075   GOptionContext *ctx;
3076   GError *err = NULL;
3077
3078 #if !GLIB_CHECK_VERSION (2, 31, 0)
3079   if (!g_thread_supported ())
3080     g_thread_init (NULL);
3081 #endif
3082
3083   ctx = g_option_context_new ("- test seeking in gsteamer");
3084   g_option_context_add_main_entries (ctx, options, NULL);
3085   g_option_context_add_group (ctx, gst_init_get_option_group ());
3086   g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
3087
3088   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
3089     g_print ("Error initializing: %s\n", err->message);
3090     exit (1);
3091   }
3092
3093   if (opt_audiosink_str == NULL)
3094     opt_audiosink_str = g_strdup (DEFAULT_AUDIOSINK);
3095
3096   if (opt_videosink_str == NULL)
3097     opt_videosink_str = g_strdup (DEFAULT_VIDEOSINK);
3098
3099   GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
3100
3101   if (argc != 3) {
3102     print_usage (argc, argv);
3103     exit (-1);
3104   }
3105
3106   pipeline_type = atoi (argv[1]);
3107
3108   if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
3109     print_usage (argc, argv);
3110     exit (-1);
3111   }
3112
3113   pipeline_spec = argv[2];
3114
3115   if (g_path_is_absolute (pipeline_spec) &&
3116       (g_strrstr (pipeline_spec, "*") != NULL ||
3117           g_strrstr (pipeline_spec, "?") != NULL)) {
3118     paths = handle_wildcards (pipeline_spec);
3119   } else {
3120     paths = g_list_prepend (paths, g_strdup (pipeline_spec));
3121   }
3122
3123   if (!paths) {
3124     g_print ("opening %s failed\n", pipeline_spec);
3125     exit (-1);
3126   }
3127
3128   l = paths;
3129
3130   pipeline = pipelines[pipeline_type].func ((gchar *) l->data);
3131   g_assert (pipeline);
3132
3133   /* initialize gui elements ... */
3134   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3135   video_window = gtk_drawing_area_new ();
3136   g_signal_connect (video_window, "draw", G_CALLBACK (draw_cb), NULL);
3137   g_signal_connect (video_window, "realize", G_CALLBACK (realize_cb), NULL);
3138   g_signal_connect (video_window, "button-press-event",
3139       G_CALLBACK (button_press_cb), NULL);
3140   g_signal_connect (video_window, "button-release-event",
3141       G_CALLBACK (button_release_cb), NULL);
3142   g_signal_connect (video_window, "key-press-event", G_CALLBACK (key_press_cb),
3143       NULL);
3144   g_signal_connect (video_window, "key-release-event",
3145       G_CALLBACK (key_release_cb), NULL);
3146   g_signal_connect (video_window, "motion-notify-event",
3147       G_CALLBACK (motion_notify_cb), NULL);
3148   gtk_widget_set_can_focus (video_window, TRUE);
3149   gtk_widget_set_double_buffered (video_window, FALSE);
3150   gtk_widget_add_events (video_window,
3151       GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3152       | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
3153
3154   statusbar = gtk_statusbar_new ();
3155   status_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "seek");
3156   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
3157   hbox = gtk_hbox_new (FALSE, 0);
3158   vbox = gtk_vbox_new (FALSE, 0);
3159   flagtable = gtk_table_new (4, 2, FALSE);
3160   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
3161
3162   /* media controls */
3163   play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
3164   pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
3165   stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
3166
3167   /* seek flags */
3168   accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
3169   key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
3170   loop_checkbox = gtk_check_button_new_with_label ("Loop");
3171   flush_checkbox = gtk_check_button_new_with_label ("Flush");
3172   scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
3173   play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
3174   skip_checkbox = gtk_check_button_new_with_label ("Play Skip");
3175   rate_spinbutton = gtk_spin_button_new_with_range (-100, 100, 0.1);
3176   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (rate_spinbutton), 3);
3177   rate_label = gtk_label_new ("Rate");
3178
3179   gtk_widget_set_tooltip_text (accurate_checkbox,
3180       "accurate position is requested, this might be considerably slower for some formats");
3181   gtk_widget_set_tooltip_text (key_checkbox,
3182       "seek to the nearest keyframe. This might be faster but less accurate");
3183   gtk_widget_set_tooltip_text (loop_checkbox, "loop playback");
3184   gtk_widget_set_tooltip_text (flush_checkbox, "flush pipeline after seeking");
3185   gtk_widget_set_tooltip_text (rate_spinbutton, "define the playback rate, "
3186       "negative value trigger reverse playback");
3187   gtk_widget_set_tooltip_text (scrub_checkbox, "show images while seeking");
3188   gtk_widget_set_tooltip_text (play_scrub_checkbox, "play video while seeking");
3189   gtk_widget_set_tooltip_text (skip_checkbox,
3190       "Skip frames while playing at high frame rates");
3191
3192   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
3193   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
3194
3195   gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
3196
3197   /* step expander */
3198   {
3199     GtkWidget *hbox;
3200
3201     step = gtk_expander_new ("step options");
3202     hbox = gtk_hbox_new (FALSE, 0);
3203
3204     format_combo = gtk_combo_box_text_new ();
3205     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
3206         "frames");
3207     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
3208         "time (ms)");
3209     gtk_combo_box_set_active (GTK_COMBO_BOX (format_combo), 0);
3210     gtk_box_pack_start (GTK_BOX (hbox), format_combo, FALSE, FALSE, 2);
3211
3212     step_amount_spinbutton = gtk_spin_button_new_with_range (1, 1000, 1);
3213     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_amount_spinbutton), 0);
3214     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_amount_spinbutton), 1.0);
3215     gtk_box_pack_start (GTK_BOX (hbox), step_amount_spinbutton, FALSE, FALSE,
3216         2);
3217
3218     step_rate_spinbutton = gtk_spin_button_new_with_range (0.0, 100, 0.1);
3219     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_rate_spinbutton), 3);
3220     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_rate_spinbutton), 1.0);
3221     gtk_box_pack_start (GTK_BOX (hbox), step_rate_spinbutton, FALSE, FALSE, 2);
3222
3223     step_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_FORWARD);
3224     gtk_button_set_label (GTK_BUTTON (step_button), "Step");
3225     gtk_box_pack_start (GTK_BOX (hbox), step_button, FALSE, FALSE, 2);
3226
3227     g_signal_connect (G_OBJECT (step_button), "clicked", G_CALLBACK (step_cb),
3228         pipeline);
3229
3230     /* shuttle scale */
3231     shuttle_checkbox = gtk_check_button_new_with_label ("Shuttle");
3232     gtk_box_pack_start (GTK_BOX (hbox), shuttle_checkbox, FALSE, FALSE, 2);
3233     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shuttle_checkbox), FALSE);
3234     g_signal_connect (shuttle_checkbox, "toggled", G_CALLBACK (shuttle_toggled),
3235         pipeline);
3236
3237     shuttle_adjustment =
3238         GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -3.00, 4.0, 0.1, 1.0, 1.0));
3239     shuttle_hscale = gtk_hscale_new (shuttle_adjustment);
3240     gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2);
3241     gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP);
3242     g_signal_connect (shuttle_hscale, "value-changed",
3243         G_CALLBACK (shuttle_value_changed), pipeline);
3244     g_signal_connect (shuttle_hscale, "format_value",
3245         G_CALLBACK (shuttle_format_value), pipeline);
3246
3247     gtk_box_pack_start (GTK_BOX (hbox), shuttle_hscale, TRUE, TRUE, 2);
3248
3249     gtk_container_add (GTK_CONTAINER (step), hbox);
3250   }
3251
3252   /* navigation command expander */
3253   {
3254     GtkWidget *navigation_button;
3255     GtkWidget *grid;
3256     gint i = 0;
3257
3258     navigation = gtk_expander_new ("navigation commands");
3259     grid = gtk_grid_new ();
3260     gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
3261     gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
3262     gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
3263     gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
3264
3265     navigation_button = gtk_button_new_with_label ("Menu 1");
3266     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3267         G_CALLBACK (navigation_cmd_cb),
3268         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU1));
3269     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3270     gtk_widget_set_sensitive (navigation_button, FALSE);
3271     gtk_widget_set_tooltip_text (navigation_button, "DVD Menu");
3272     navigation_buttons[i].button = navigation_button;
3273     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU1;
3274
3275     navigation_button = gtk_button_new_with_label ("Menu 2");
3276     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3277         G_CALLBACK (navigation_cmd_cb),
3278         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU2));
3279     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3280     gtk_widget_set_sensitive (navigation_button, FALSE);
3281     gtk_widget_set_tooltip_text (navigation_button, "DVD Title Menu");
3282     navigation_buttons[i].button = navigation_button;
3283     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU2;
3284
3285     navigation_button = gtk_button_new_with_label ("Menu 3");
3286     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3287         G_CALLBACK (navigation_cmd_cb),
3288         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU3));
3289     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3290     gtk_widget_set_sensitive (navigation_button, FALSE);
3291     gtk_widget_set_tooltip_text (navigation_button, "DVD Root Menu");
3292     navigation_buttons[i].button = navigation_button;
3293     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU3;
3294
3295     navigation_button = gtk_button_new_with_label ("Menu 4");
3296     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3297         G_CALLBACK (navigation_cmd_cb),
3298         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU4));
3299     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3300     gtk_widget_set_sensitive (navigation_button, FALSE);
3301     gtk_widget_set_tooltip_text (navigation_button, "DVD Subpicture Menu");
3302     navigation_buttons[i].button = navigation_button;
3303     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU4;
3304
3305     navigation_button = gtk_button_new_with_label ("Menu 5");
3306     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3307         G_CALLBACK (navigation_cmd_cb),
3308         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU5));
3309     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3310     gtk_widget_set_sensitive (navigation_button, FALSE);
3311     gtk_widget_set_tooltip_text (navigation_button, "DVD Audio Menu");
3312     navigation_buttons[i].button = navigation_button;
3313     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU5;
3314
3315     navigation_button = gtk_button_new_with_label ("Menu 6");
3316     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3317         G_CALLBACK (navigation_cmd_cb),
3318         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU6));
3319     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3320     gtk_widget_set_sensitive (navigation_button, FALSE);
3321     gtk_widget_set_tooltip_text (navigation_button, "DVD Angle Menu");
3322     navigation_buttons[i].button = navigation_button;
3323     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU6;
3324
3325     navigation_button = gtk_button_new_with_label ("Menu 7");
3326     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3327         G_CALLBACK (navigation_cmd_cb),
3328         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU7));
3329     gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3330     gtk_widget_set_sensitive (navigation_button, FALSE);
3331     gtk_widget_set_tooltip_text (navigation_button, "DVD Chapter Menu");
3332     navigation_buttons[i].button = navigation_button;
3333     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU7;
3334
3335     navigation_button = gtk_button_new_with_label ("Left");
3336     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3337         G_CALLBACK (navigation_cmd_cb),
3338         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_LEFT));
3339     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3340     gtk_widget_set_sensitive (navigation_button, FALSE);
3341     navigation_buttons[i].button = navigation_button;
3342     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_LEFT;
3343
3344     navigation_button = gtk_button_new_with_label ("Right");
3345     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3346         G_CALLBACK (navigation_cmd_cb),
3347         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_RIGHT));
3348     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3349     gtk_widget_set_sensitive (navigation_button, FALSE);
3350     navigation_buttons[i].button = navigation_button;
3351     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_RIGHT;
3352
3353     navigation_button = gtk_button_new_with_label ("Up");
3354     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3355         G_CALLBACK (navigation_cmd_cb),
3356         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_UP));
3357     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3358     gtk_widget_set_sensitive (navigation_button, FALSE);
3359     navigation_buttons[i].button = navigation_button;
3360     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_UP;
3361
3362     navigation_button = gtk_button_new_with_label ("Down");
3363     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3364         G_CALLBACK (navigation_cmd_cb),
3365         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_DOWN));
3366     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3367     gtk_widget_set_sensitive (navigation_button, FALSE);
3368     navigation_buttons[i].button = navigation_button;
3369     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_DOWN;
3370
3371     navigation_button = gtk_button_new_with_label ("Activate");
3372     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3373         G_CALLBACK (navigation_cmd_cb),
3374         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_ACTIVATE));
3375     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3376     gtk_widget_set_sensitive (navigation_button, FALSE);
3377     navigation_buttons[i].button = navigation_button;
3378     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_ACTIVATE;
3379
3380     navigation_button = gtk_button_new_with_label ("Prev. Angle");
3381     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3382         G_CALLBACK (navigation_cmd_cb),
3383         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_PREV_ANGLE));
3384     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3385     gtk_widget_set_sensitive (navigation_button, FALSE);
3386     navigation_buttons[i].button = navigation_button;
3387     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_PREV_ANGLE;
3388
3389     navigation_button = gtk_button_new_with_label ("Next. Angle");
3390     g_signal_connect (G_OBJECT (navigation_button), "clicked",
3391         G_CALLBACK (navigation_cmd_cb),
3392         GINT_TO_POINTER (GST_NAVIGATION_COMMAND_NEXT_ANGLE));
3393     gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3394     gtk_widget_set_sensitive (navigation_button, FALSE);
3395     navigation_buttons[i].button = navigation_button;
3396     navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_NEXT_ANGLE;
3397
3398     gtk_container_add (GTK_CONTAINER (navigation), grid);
3399   }
3400
3401   /* colorbalance expander */
3402   {
3403     GtkWidget *vbox, *frame;
3404
3405     colorbalance = gtk_expander_new ("color balance options");
3406     vbox = gtk_vbox_new (FALSE, 0);
3407
3408     /* contrast scale */
3409     frame = gtk_frame_new ("Contrast");
3410     adjustment =
3411         GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3412     contrast_scale = gtk_hscale_new (adjustment);
3413     gtk_scale_set_draw_value (GTK_SCALE (contrast_scale), FALSE);
3414     g_signal_connect (contrast_scale, "value-changed",
3415         G_CALLBACK (colorbalance_value_changed), (gpointer) "CONTRAST");
3416     gtk_container_add (GTK_CONTAINER (frame), contrast_scale);
3417     gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3418
3419     /* brightness scale */
3420     frame = gtk_frame_new ("Brightness");
3421     adjustment =
3422         GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3423     brightness_scale = gtk_hscale_new (adjustment);
3424     gtk_scale_set_draw_value (GTK_SCALE (brightness_scale), FALSE);
3425     g_signal_connect (brightness_scale, "value-changed",
3426         G_CALLBACK (colorbalance_value_changed), (gpointer) "BRIGHTNESS");
3427     gtk_container_add (GTK_CONTAINER (frame), brightness_scale);
3428     gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3429
3430     /* hue scale */
3431     frame = gtk_frame_new ("Hue");
3432     adjustment =
3433         GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3434     hue_scale = gtk_hscale_new (adjustment);
3435     gtk_scale_set_draw_value (GTK_SCALE (hue_scale), FALSE);
3436     g_signal_connect (hue_scale, "value-changed",
3437         G_CALLBACK (colorbalance_value_changed), (gpointer) "HUE");
3438     gtk_container_add (GTK_CONTAINER (frame), hue_scale);
3439     gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3440
3441     /* saturation scale */
3442     frame = gtk_frame_new ("Saturation");
3443     adjustment =
3444         GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3445     saturation_scale = gtk_hscale_new (adjustment);
3446     gtk_scale_set_draw_value (GTK_SCALE (saturation_scale), FALSE);
3447     g_signal_connect (saturation_scale, "value-changed",
3448         G_CALLBACK (colorbalance_value_changed), (gpointer) "SATURATION");
3449     gtk_container_add (GTK_CONTAINER (frame), saturation_scale);
3450     gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3451
3452     gtk_container_add (GTK_CONTAINER (colorbalance), vbox);
3453   }
3454
3455   /* seek bar */
3456   adjustment =
3457       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, N_GRAD, 0.1, 1.0, 1.0));
3458   hscale = gtk_hscale_new (adjustment);
3459   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
3460   gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
3461   gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
3462   gtk_range_set_fill_level (GTK_RANGE (hscale), N_GRAD);
3463
3464   g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek),
3465       pipeline);
3466   g_signal_connect (hscale, "button_release_event", G_CALLBACK (stop_seek),
3467       pipeline);
3468   g_signal_connect (hscale, "format_value", G_CALLBACK (format_value),
3469       pipeline);
3470
3471   if (pipeline_type == 16) {
3472     /* the playbin2 panel controls for the video/audio/subtitle tracks */
3473     panel = gtk_hbox_new (FALSE, 0);
3474     video_combo = gtk_combo_box_text_new ();
3475     audio_combo = gtk_combo_box_text_new ();
3476     text_combo = gtk_combo_box_text_new ();
3477     gtk_widget_set_sensitive (video_combo, FALSE);
3478     gtk_widget_set_sensitive (audio_combo, FALSE);
3479     gtk_widget_set_sensitive (text_combo, FALSE);
3480     gtk_box_pack_start (GTK_BOX (panel), video_combo, TRUE, TRUE, 2);
3481     gtk_box_pack_start (GTK_BOX (panel), audio_combo, TRUE, TRUE, 2);
3482     gtk_box_pack_start (GTK_BOX (panel), text_combo, TRUE, TRUE, 2);
3483     g_signal_connect (G_OBJECT (video_combo), "changed",
3484         G_CALLBACK (video_combo_cb), pipeline);
3485     g_signal_connect (G_OBJECT (audio_combo), "changed",
3486         G_CALLBACK (audio_combo_cb), pipeline);
3487     g_signal_connect (G_OBJECT (text_combo), "changed",
3488         G_CALLBACK (text_combo_cb), pipeline);
3489     /* playbin2 panel for flag checkboxes and volume/mute */
3490     boxes = gtk_grid_new ();
3491     gtk_grid_set_row_spacing (GTK_GRID (boxes), 2);
3492     gtk_grid_set_row_homogeneous (GTK_GRID (boxes), TRUE);
3493     gtk_grid_set_column_spacing (GTK_GRID (boxes), 2);
3494     gtk_grid_set_column_homogeneous (GTK_GRID (boxes), TRUE);
3495
3496     video_checkbox = gtk_check_button_new_with_label ("Video");
3497     audio_checkbox = gtk_check_button_new_with_label ("Audio");
3498     text_checkbox = gtk_check_button_new_with_label ("Text");
3499     vis_checkbox = gtk_check_button_new_with_label ("Vis");
3500     soft_volume_checkbox = gtk_check_button_new_with_label ("Soft Volume");
3501     native_audio_checkbox = gtk_check_button_new_with_label ("Native Audio");
3502     native_video_checkbox = gtk_check_button_new_with_label ("Native Video");
3503     download_checkbox = gtk_check_button_new_with_label ("Download");
3504     buffering_checkbox = gtk_check_button_new_with_label ("Buffering");
3505     deinterlace_checkbox = gtk_check_button_new_with_label ("Deinterlace");
3506     soft_colorbalance_checkbox =
3507         gtk_check_button_new_with_label ("Soft Colorbalance");
3508     mute_checkbox = gtk_check_button_new_with_label ("Mute");
3509     volume_label = gtk_label_new ("Volume");
3510     volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
3511     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
3512     gtk_grid_attach (GTK_GRID (boxes), video_checkbox, 0, 0, 1, 1);
3513     gtk_grid_attach (GTK_GRID (boxes), audio_checkbox, 1, 0, 1, 1);
3514     gtk_grid_attach (GTK_GRID (boxes), text_checkbox, 2, 0, 1, 1);
3515     gtk_grid_attach (GTK_GRID (boxes), vis_checkbox, 3, 0, 1, 1);
3516     gtk_grid_attach (GTK_GRID (boxes), soft_volume_checkbox, 4, 0, 1, 1);
3517     gtk_grid_attach (GTK_GRID (boxes), native_audio_checkbox, 5, 0, 1, 1);
3518     gtk_grid_attach (GTK_GRID (boxes), native_video_checkbox, 0, 1, 1, 1);
3519     gtk_grid_attach (GTK_GRID (boxes), download_checkbox, 1, 1, 1, 1);
3520     gtk_grid_attach (GTK_GRID (boxes), buffering_checkbox, 2, 1, 1, 1);
3521     gtk_grid_attach (GTK_GRID (boxes), deinterlace_checkbox, 3, 1, 1, 1);
3522     gtk_grid_attach (GTK_GRID (boxes), soft_colorbalance_checkbox, 4, 1, 1, 1);
3523
3524     gtk_grid_attach (GTK_GRID (boxes), mute_checkbox, 7, 0, 2, 1);
3525     gtk_grid_attach (GTK_GRID (boxes), volume_label, 6, 1, 1, 1);
3526     gtk_grid_attach (GTK_GRID (boxes), volume_spinbutton, 7, 1, 1, 1);
3527     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (video_checkbox), TRUE);
3528     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audio_checkbox), TRUE);
3529     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_checkbox), TRUE);
3530     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vis_checkbox), FALSE);
3531     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (soft_volume_checkbox),
3532         TRUE);
3533     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (native_audio_checkbox),
3534         FALSE);
3535     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (native_video_checkbox),
3536         FALSE);
3537     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (download_checkbox), FALSE);
3538     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (buffering_checkbox),
3539         FALSE);
3540     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (deinterlace_checkbox),
3541         FALSE);
3542     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
3543         (soft_colorbalance_checkbox), TRUE);
3544     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mute_checkbox), FALSE);
3545     g_signal_connect (G_OBJECT (video_checkbox), "toggled",
3546         G_CALLBACK (video_toggle_cb), pipeline);
3547     g_signal_connect (G_OBJECT (audio_checkbox), "toggled",
3548         G_CALLBACK (audio_toggle_cb), pipeline);
3549     g_signal_connect (G_OBJECT (text_checkbox), "toggled",
3550         G_CALLBACK (text_toggle_cb), pipeline);
3551     g_signal_connect (G_OBJECT (vis_checkbox), "toggled",
3552         G_CALLBACK (vis_toggle_cb), pipeline);
3553     g_signal_connect (G_OBJECT (soft_volume_checkbox), "toggled",
3554         G_CALLBACK (soft_volume_toggle_cb), pipeline);
3555     g_signal_connect (G_OBJECT (native_audio_checkbox), "toggled",
3556         G_CALLBACK (native_audio_toggle_cb), pipeline);
3557     g_signal_connect (G_OBJECT (native_video_checkbox), "toggled",
3558         G_CALLBACK (native_video_toggle_cb), pipeline);
3559     g_signal_connect (G_OBJECT (download_checkbox), "toggled",
3560         G_CALLBACK (download_toggle_cb), pipeline);
3561     g_signal_connect (G_OBJECT (buffering_checkbox), "toggled",
3562         G_CALLBACK (buffering_toggle_cb), pipeline);
3563     g_signal_connect (G_OBJECT (deinterlace_checkbox), "toggled",
3564         G_CALLBACK (deinterlace_toggle_cb), pipeline);
3565     g_signal_connect (G_OBJECT (soft_colorbalance_checkbox), "toggled",
3566         G_CALLBACK (soft_colorbalance_toggle_cb), pipeline);
3567     g_signal_connect (G_OBJECT (mute_checkbox), "toggled",
3568         G_CALLBACK (mute_toggle_cb), pipeline);
3569     g_signal_connect (G_OBJECT (volume_spinbutton), "value-changed",
3570         G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
3571     /* playbin2 panel for snapshot */
3572     boxes2 = gtk_hbox_new (FALSE, 0);
3573     shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
3574     gtk_widget_set_tooltip_text (shot_button,
3575         "save a screenshot .png in the current directory");
3576     g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
3577         pipeline);
3578     vis_combo = gtk_combo_box_text_new ();
3579     g_signal_connect (G_OBJECT (vis_combo), "changed",
3580         G_CALLBACK (vis_combo_cb), pipeline);
3581     gtk_widget_set_sensitive (vis_combo, FALSE);
3582     gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
3583     gtk_box_pack_start (GTK_BOX (boxes2), vis_combo, TRUE, TRUE, 2);
3584
3585     /* fill the vis combo box and the array of factories */
3586     init_visualization_features ();
3587   } else {
3588     panel = boxes = boxes2 = NULL;
3589   }
3590
3591   /* do the packing stuff ... */
3592   gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
3593   /* FIXME: can we avoid this for audio only? */
3594   gtk_widget_set_size_request (GTK_WIDGET (video_window), -1,
3595       DEFAULT_VIDEO_HEIGHT);
3596   gtk_container_add (GTK_CONTAINER (window), vbox);
3597   gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 2);
3598   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
3599   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
3600   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
3601   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
3602   gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
3603   gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
3604       1);
3605   gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
3606   gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
3607   gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
3608   gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
3609   gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
3610       1, 2);
3611   gtk_table_attach_defaults (GTK_TABLE (flagtable), skip_checkbox, 3, 4, 0, 1);
3612   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 4, 5, 0, 1);
3613   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 4, 5, 1,
3614       2);
3615   if (panel && boxes && boxes2) {
3616     expander = gtk_expander_new ("playbin2 options");
3617     pb2vbox = gtk_vbox_new (FALSE, 0);
3618     gtk_box_pack_start (GTK_BOX (pb2vbox), panel, FALSE, FALSE, 2);
3619     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes, FALSE, FALSE, 2);
3620     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes2, FALSE, FALSE, 2);
3621     gtk_container_add (GTK_CONTAINER (expander), pb2vbox);
3622     gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 2);
3623   }
3624   gtk_box_pack_start (GTK_BOX (vbox), step, FALSE, FALSE, 2);
3625   gtk_box_pack_start (GTK_BOX (vbox), navigation, FALSE, FALSE, 2);
3626   gtk_box_pack_start (GTK_BOX (vbox), colorbalance, FALSE, FALSE, 2);
3627   gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, FALSE, 2);
3628   gtk_box_pack_start (GTK_BOX (vbox), hscale, FALSE, FALSE, 2);
3629   gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 2);
3630
3631   /* connect things ... */
3632   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
3633       pipeline);
3634   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
3635       pipeline);
3636   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
3637       pipeline);
3638   g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
3639       G_CALLBACK (accurate_toggle_cb), pipeline);
3640   g_signal_connect (G_OBJECT (key_checkbox), "toggled",
3641       G_CALLBACK (key_toggle_cb), pipeline);
3642   g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
3643       G_CALLBACK (loop_toggle_cb), pipeline);
3644   g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
3645       G_CALLBACK (flush_toggle_cb), pipeline);
3646   g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
3647       G_CALLBACK (scrub_toggle_cb), pipeline);
3648   g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
3649       G_CALLBACK (play_scrub_toggle_cb), pipeline);
3650   g_signal_connect (G_OBJECT (skip_checkbox), "toggled",
3651       G_CALLBACK (skip_toggle_cb), pipeline);
3652   g_signal_connect (G_OBJECT (rate_spinbutton), "value-changed",
3653       G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
3654
3655   g_signal_connect (G_OBJECT (window), "delete-event", delete_event_cb, NULL);
3656
3657   /* show the gui. */
3658   gtk_widget_show_all (window);
3659
3660   /* realize window now so that the video window gets created and we can
3661    * obtain its XID before the pipeline is started up and the videosink
3662    * asks for the XID of the window to render onto */
3663   gtk_widget_realize (window);
3664
3665 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
3666   /* we should have the XID now */
3667   g_assert (embed_xid != 0);
3668
3669   if (pipeline_type == 16) {
3670     gst_x_overlay_set_window_handle (GST_X_OVERLAY (pipeline), embed_xid);
3671     gst_x_overlay_handle_events (GST_X_OVERLAY (pipeline), FALSE);
3672   }
3673 #endif
3674
3675   if (verbose) {
3676     g_signal_connect (pipeline, "deep_notify",
3677         G_CALLBACK (gst_object_default_deep_notify), NULL);
3678   }
3679
3680   connect_bus_signals (pipeline);
3681   gtk_main ();
3682
3683   g_print ("NULL pipeline\n");
3684   gst_element_set_state (pipeline, GST_STATE_NULL);
3685
3686   if (xoverlay_element)
3687     gst_object_unref (xoverlay_element);
3688   if (navigation_element)
3689     gst_object_unref (navigation_element);
3690
3691   g_print ("free pipeline\n");
3692   gst_object_unref (pipeline);
3693
3694   g_list_foreach (paths, (GFunc) g_free, NULL);
3695   g_list_free (paths);
3696
3697   return 0;
3698 }