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