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