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