tests/examples/seek/seek.c: There's a nice macro to check
[platform/upstream/gst-plugins-base.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  * FIXME: is the flush_seek part after sending the seek still needed?
25  *
26  */
27 #include <stdlib.h>
28 #include <glib.h>
29 #include <gtk/gtk.h>
30 #include <gst/gst.h>
31 #include <string.h>
32
33 GST_DEBUG_CATEGORY_STATIC (seek_debug);
34 #define GST_CAT_DEFAULT (seek_debug)
35
36 /* configuration */
37
38 //#define SOURCE "filesrc"
39 #define SOURCE "gnomevfssrc"
40
41 #define ASINK "alsasink"
42 //#define ASINK "osssink"
43
44 #define VSINK "xvimagesink"
45 //#define VSINK "sdlvideosink"
46 //#define VSINK "ximagesink"
47 //#define VSINK "aasink"
48 //#define VSINK "cacasink"
49
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;
80 static gboolean stats = FALSE;
81 static gboolean elem_seek = FALSE;
82 static gboolean verbose = FALSE;
83
84 static GstState state = GST_STATE_NULL;
85 static guint update_id = 0;
86 static guint seek_timeout_id = 0;
87 static gulong changed_id;
88
89
90 /* pipeline construction */
91
92 typedef struct
93 {
94   const gchar *padname;
95   GstPad *target;
96   GstElement *bin;
97 }
98 dyn_link;
99
100 static GstElement *
101 gst_element_factory_make_or_warn (gchar * type, gchar * name)
102 {
103   GstElement *element = gst_element_factory_make (type, name);
104
105   if (!element) {
106     g_warning ("Failed to create element %s of type %s", name, type);
107   }
108
109   return element;
110 }
111
112 static void
113 dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
114 {
115   gchar *padname;
116   dyn_link *connect = (dyn_link *) data;
117
118   padname = gst_pad_get_name (newpad);
119
120   if (connect->padname == NULL || !strcmp (padname, connect->padname)) {
121     if (connect->bin)
122       gst_bin_add (GST_BIN (pipeline), connect->bin);
123     gst_pad_link (newpad, connect->target);
124
125     //seekable_pads = g_list_prepend (seekable_pads, newpad);
126     rate_pads = g_list_prepend (rate_pads, newpad);
127   }
128   g_free (padname);
129 }
130
131 static void
132 setup_dynamic_link (GstElement * element, const gchar * padname,
133     GstPad * target, GstElement * bin)
134 {
135   dyn_link *connect;
136
137   connect = g_new0 (dyn_link, 1);
138   connect->padname = g_strdup (padname);
139   connect->target = target;
140   connect->bin = bin;
141
142   g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
143       connect);
144 }
145
146 static GstElement *
147 make_mod_pipeline (const gchar * location)
148 {
149   GstElement *pipeline;
150   GstElement *src, *decoder, *audiosink;
151   GstPad *seekable;
152
153   pipeline = gst_pipeline_new ("app");
154
155   src = gst_element_factory_make_or_warn (SOURCE, "src");
156   decoder = gst_element_factory_make_or_warn ("modplug", "decoder");
157   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
158   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
159
160   g_object_set (G_OBJECT (src), "location", location, NULL);
161
162   gst_bin_add (GST_BIN (pipeline), src);
163   gst_bin_add (GST_BIN (pipeline), decoder);
164   gst_bin_add (GST_BIN (pipeline), audiosink);
165
166   gst_element_link (src, decoder);
167   gst_element_link (decoder, audiosink);
168
169   seekable = gst_element_get_pad (decoder, "src");
170   seekable_pads = g_list_prepend (seekable_pads, seekable);
171   rate_pads = g_list_prepend (rate_pads, seekable);
172   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
173
174   return pipeline;
175 }
176
177 static GstElement *
178 make_dv_pipeline (const gchar * location)
179 {
180   GstElement *pipeline;
181   GstElement *src, *demux, *decoder, *audiosink, *videosink;
182   GstElement *a_queue, *v_queue;
183   GstPad *seekable;
184
185   pipeline = gst_pipeline_new ("app");
186
187   src = gst_element_factory_make_or_warn (SOURCE, "src");
188   demux = gst_element_factory_make_or_warn ("dvdemux", "demuxer");
189   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
190   decoder = gst_element_factory_make_or_warn ("ffdec_dvvideo", "decoder");
191   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
192   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
193   audiosink = gst_element_factory_make_or_warn ("alsasink", "a_sink");
194
195   g_object_set (G_OBJECT (src), "location", location, NULL);
196
197   gst_bin_add (GST_BIN (pipeline), src);
198   gst_bin_add (GST_BIN (pipeline), demux);
199   gst_bin_add (GST_BIN (pipeline), a_queue);
200   gst_bin_add (GST_BIN (pipeline), audiosink);
201   gst_bin_add (GST_BIN (pipeline), v_queue);
202   gst_bin_add (GST_BIN (pipeline), decoder);
203   gst_bin_add (GST_BIN (pipeline), videosink);
204
205   gst_element_link (src, demux);
206   gst_element_link (a_queue, audiosink);
207   gst_element_link (v_queue, decoder);
208   gst_element_link (decoder, videosink);
209
210   setup_dynamic_link (demux, "video", gst_element_get_pad (v_queue, "sink"),
211       NULL);
212   setup_dynamic_link (demux, "audio", gst_element_get_pad (a_queue, "sink"),
213       NULL);
214
215   seekable = gst_element_get_pad (decoder, "src");
216   seekable_pads = g_list_prepend (seekable_pads, seekable);
217   rate_pads = g_list_prepend (rate_pads, seekable);
218
219   return pipeline;
220 }
221
222 static GstElement *
223 make_wav_pipeline (const gchar * location)
224 {
225   GstElement *pipeline;
226   GstElement *src, *decoder, *audiosink;
227
228   pipeline = gst_pipeline_new ("app");
229
230   src = gst_element_factory_make_or_warn (SOURCE, "src");
231   decoder = gst_element_factory_make_or_warn ("wavparse", "decoder");
232   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
233
234   g_object_set (G_OBJECT (src), "location", location, NULL);
235
236   gst_bin_add (GST_BIN (pipeline), src);
237   gst_bin_add (GST_BIN (pipeline), decoder);
238   gst_bin_add (GST_BIN (pipeline), audiosink);
239
240   gst_element_link (src, decoder);
241
242   setup_dynamic_link (decoder, "src", gst_element_get_pad (audiosink, "sink"),
243       NULL);
244
245   seekable_elements = g_list_prepend (seekable_elements, audiosink);
246
247   /* force element seeking on this pipeline */
248   elem_seek = TRUE;
249
250   return pipeline;
251 }
252
253 static GstElement *
254 make_flac_pipeline (const gchar * location)
255 {
256   GstElement *pipeline;
257   GstElement *src, *decoder, *audiosink;
258   GstPad *seekable;
259
260   pipeline = gst_pipeline_new ("app");
261
262   src = gst_element_factory_make_or_warn (SOURCE, "src");
263   decoder = gst_element_factory_make_or_warn ("flacdec", "decoder");
264   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
265   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
266
267   g_object_set (G_OBJECT (src), "location", location, NULL);
268
269   gst_bin_add (GST_BIN (pipeline), src);
270   gst_bin_add (GST_BIN (pipeline), decoder);
271   gst_bin_add (GST_BIN (pipeline), audiosink);
272
273   gst_element_link (src, decoder);
274   gst_element_link (decoder, audiosink);
275
276   seekable = gst_element_get_pad (decoder, "src");
277   seekable_pads = g_list_prepend (seekable_pads, seekable);
278   rate_pads = g_list_prepend (rate_pads, seekable);
279   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
280
281   return pipeline;
282 }
283
284 static GstElement *
285 make_sid_pipeline (const gchar * location)
286 {
287   GstElement *pipeline;
288   GstElement *src, *decoder, *audiosink;
289   GstPad *seekable;
290
291   pipeline = gst_pipeline_new ("app");
292
293   src = gst_element_factory_make_or_warn (SOURCE, "src");
294   decoder = gst_element_factory_make_or_warn ("siddec", "decoder");
295   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
296   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
297
298   g_object_set (G_OBJECT (src), "location", location, NULL);
299
300   gst_bin_add (GST_BIN (pipeline), src);
301   gst_bin_add (GST_BIN (pipeline), decoder);
302   gst_bin_add (GST_BIN (pipeline), audiosink);
303
304   gst_element_link (src, decoder);
305   gst_element_link (decoder, audiosink);
306
307   seekable = gst_element_get_pad (decoder, "src");
308   seekable_pads = g_list_prepend (seekable_pads, seekable);
309   rate_pads = g_list_prepend (rate_pads, seekable);
310   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
311
312   return pipeline;
313 }
314
315 static GstElement *
316 make_parse_pipeline (const gchar * location)
317 {
318   GstElement *pipeline;
319   GstElement *src, *parser, *fakesink;
320   GstPad *seekable;
321
322   pipeline = gst_pipeline_new ("app");
323
324   src = gst_element_factory_make_or_warn (SOURCE, "src");
325   parser = gst_element_factory_make_or_warn ("mpegparse", "parse");
326   fakesink = gst_element_factory_make_or_warn ("fakesink", "sink");
327   g_object_set (G_OBJECT (fakesink), "silent", TRUE, NULL);
328   g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
329
330   g_object_set (G_OBJECT (src), "location", location, NULL);
331
332   gst_bin_add (GST_BIN (pipeline), src);
333   gst_bin_add (GST_BIN (pipeline), parser);
334   gst_bin_add (GST_BIN (pipeline), fakesink);
335
336   gst_element_link (src, parser);
337   gst_element_link (parser, fakesink);
338
339   seekable = gst_element_get_pad (parser, "src");
340   seekable_pads = g_list_prepend (seekable_pads, seekable);
341   rate_pads = g_list_prepend (rate_pads, seekable);
342   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (parser, "sink"));
343
344   return pipeline;
345 }
346
347 static GstElement *
348 make_vorbis_pipeline (const gchar * location)
349 {
350   GstElement *pipeline, *audio_bin;
351   GstElement *src, *demux, *decoder, *convert, *audiosink;
352   GstPad *pad, *seekable;
353
354   pipeline = gst_pipeline_new ("app");
355
356   src = gst_element_factory_make_or_warn (SOURCE, "src");
357   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
358   decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder");
359   convert = gst_element_factory_make_or_warn ("audioconvert", "convert");
360   audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
361   g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL);
362
363   g_object_set (G_OBJECT (src), "location", location, NULL);
364
365   audio_bin = gst_bin_new ("a_decoder_bin");
366
367   gst_bin_add (GST_BIN (pipeline), src);
368   gst_bin_add (GST_BIN (pipeline), demux);
369   gst_bin_add (GST_BIN (audio_bin), decoder);
370   gst_bin_add (GST_BIN (audio_bin), convert);
371   gst_bin_add (GST_BIN (audio_bin), audiosink);
372   gst_bin_add (GST_BIN (pipeline), audio_bin);
373
374   gst_element_link (src, demux);
375   gst_element_link (decoder, convert);
376   gst_element_link (convert, audiosink);
377
378   pad = gst_element_get_pad (decoder, "sink");
379   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
380   gst_object_unref (pad);
381
382   setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"),
383       NULL);
384
385   seekable = gst_element_get_pad (decoder, "src");
386   seekable_pads = g_list_prepend (seekable_pads, seekable);
387   rate_pads = g_list_prepend (rate_pads, seekable);
388   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
389
390   return pipeline;
391 }
392
393 static GstElement *
394 make_theora_pipeline (const gchar * location)
395 {
396   GstElement *pipeline, *video_bin;
397   GstElement *src, *demux, *decoder, *convert, *videosink;
398   GstPad *pad, *seekable;
399
400   pipeline = gst_pipeline_new ("app");
401
402   src = gst_element_factory_make_or_warn (SOURCE, "src");
403   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
404   decoder = gst_element_factory_make_or_warn ("theoradec", "decoder");
405   convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert");
406   videosink = gst_element_factory_make_or_warn (VSINK, "sink");
407
408   g_object_set (G_OBJECT (src), "location", location, NULL);
409
410   video_bin = gst_bin_new ("v_decoder_bin");
411
412   gst_bin_add (GST_BIN (pipeline), src);
413   gst_bin_add (GST_BIN (pipeline), demux);
414   gst_bin_add (GST_BIN (video_bin), decoder);
415   gst_bin_add (GST_BIN (video_bin), convert);
416   gst_bin_add (GST_BIN (video_bin), videosink);
417   gst_bin_add (GST_BIN (pipeline), video_bin);
418
419   gst_element_link (src, demux);
420   gst_element_link (decoder, convert);
421   gst_element_link (convert, videosink);
422
423   pad = gst_element_get_pad (decoder, "sink");
424   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
425   gst_object_unref (pad);
426
427   setup_dynamic_link (demux, NULL, gst_element_get_pad (video_bin, "sink"),
428       NULL);
429
430   seekable = gst_element_get_pad (decoder, "src");
431   seekable_pads = g_list_prepend (seekable_pads, seekable);
432   rate_pads = g_list_prepend (rate_pads, seekable);
433   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
434
435   return pipeline;
436 }
437
438 static GstElement *
439 make_vorbis_theora_pipeline (const gchar * location)
440 {
441   GstElement *pipeline, *audio_bin, *video_bin;
442   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
443   GstElement *audiosink, *videosink;
444   GstElement *a_queue, *v_queue, *v_scale;
445   GstPad *seekable;
446   GstPad *pad;
447
448   pipeline = gst_pipeline_new ("app");
449
450   src = gst_element_factory_make_or_warn (SOURCE, "src");
451   g_object_set (G_OBJECT (src), "location", location, NULL);
452
453   demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
454
455   gst_bin_add (GST_BIN (pipeline), src);
456   gst_bin_add (GST_BIN (pipeline), demux);
457   gst_element_link (src, demux);
458
459   audio_bin = gst_bin_new ("a_decoder_bin");
460   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
461   a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec");
462   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
463   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
464
465   gst_bin_add (GST_BIN (pipeline), audio_bin);
466
467   gst_bin_add (GST_BIN (audio_bin), a_queue);
468   gst_bin_add (GST_BIN (audio_bin), a_decoder);
469   gst_bin_add (GST_BIN (audio_bin), a_convert);
470   gst_bin_add (GST_BIN (audio_bin), audiosink);
471
472   gst_element_link (a_queue, a_decoder);
473   gst_element_link (a_decoder, a_convert);
474   gst_element_link (a_convert, audiosink);
475
476   pad = gst_element_get_pad (a_queue, "sink");
477   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
478   gst_object_unref (pad);
479
480   setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"),
481       NULL);
482
483   video_bin = gst_bin_new ("v_decoder_bin");
484   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
485   v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec");
486   v_convert =
487       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
488   v_scale = gst_element_factory_make_or_warn ("videoscale", "v_scale");
489   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
490
491   gst_bin_add (GST_BIN (pipeline), video_bin);
492
493   gst_bin_add (GST_BIN (video_bin), v_queue);
494   gst_bin_add (GST_BIN (video_bin), v_decoder);
495   gst_bin_add (GST_BIN (video_bin), v_convert);
496   gst_bin_add (GST_BIN (video_bin), v_scale);
497   gst_bin_add (GST_BIN (video_bin), videosink);
498
499   gst_element_link_many (v_queue, v_decoder, v_convert, v_scale, videosink,
500       NULL);
501
502   pad = gst_element_get_pad (v_queue, "sink");
503   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
504   gst_object_unref (pad);
505
506   setup_dynamic_link (demux, NULL, gst_element_get_pad (video_bin, "sink"),
507       NULL);
508
509   seekable = gst_element_get_pad (a_decoder, "src");
510   seekable_pads = g_list_prepend (seekable_pads, seekable);
511   rate_pads = g_list_prepend (rate_pads, seekable);
512   rate_pads =
513       g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
514
515   return pipeline;
516 }
517
518 static GstElement *
519 make_avi_msmpeg4v3_mp3_pipeline (const gchar * location)
520 {
521   GstElement *pipeline, *audio_bin, *video_bin;
522   GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
523   GstElement *audiosink, *videosink;
524   GstElement *a_queue, *v_queue;
525   GstPad *seekable, *pad;
526
527   pipeline = gst_pipeline_new ("app");
528
529   src = gst_element_factory_make_or_warn (SOURCE, "src");
530   g_object_set (G_OBJECT (src), "location", location, NULL);
531
532   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
533
534   gst_bin_add (GST_BIN (pipeline), src);
535   gst_bin_add (GST_BIN (pipeline), demux);
536   gst_element_link (src, demux);
537
538   audio_bin = gst_bin_new ("a_decoder_bin");
539   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
540   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
541   a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
542   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
543
544   gst_bin_add (GST_BIN (audio_bin), a_queue);
545   gst_bin_add (GST_BIN (audio_bin), a_decoder);
546   gst_bin_add (GST_BIN (audio_bin), a_convert);
547   gst_bin_add (GST_BIN (audio_bin), audiosink);
548
549   gst_element_link (a_queue, a_decoder);
550   gst_element_link (a_decoder, a_convert);
551   gst_element_link (a_convert, audiosink);
552
553   gst_bin_add (GST_BIN (pipeline), audio_bin);
554
555   pad = gst_element_get_pad (a_queue, "sink");
556   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
557   gst_object_unref (pad);
558
559   setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"),
560       NULL);
561
562   video_bin = gst_bin_new ("v_decoder_bin");
563   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
564   v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec");
565   v_convert =
566       gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
567   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
568
569   gst_bin_add (GST_BIN (video_bin), v_queue);
570   gst_bin_add (GST_BIN (video_bin), v_decoder);
571   gst_bin_add (GST_BIN (video_bin), v_convert);
572   gst_bin_add (GST_BIN (video_bin), videosink);
573
574   gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL);
575
576   gst_bin_add (GST_BIN (pipeline), video_bin);
577
578   pad = gst_element_get_pad (v_queue, "sink");
579   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
580   gst_object_unref (pad);
581
582   setup_dynamic_link (demux, NULL, gst_element_get_pad (video_bin, "sink"),
583       NULL);
584
585   seekable = gst_element_get_pad (a_decoder, "src");
586   seekable_pads = g_list_prepend (seekable_pads, seekable);
587   rate_pads = g_list_prepend (rate_pads, seekable);
588   rate_pads =
589       g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
590
591   return pipeline;
592 }
593
594 static GstElement *
595 make_mp3_pipeline (const gchar * location)
596 {
597   GstElement *pipeline;
598   GstElement *src, *decoder, *osssink, *queue;
599   GstPad *seekable;
600
601   pipeline = gst_pipeline_new ("app");
602
603   src = gst_element_factory_make_or_warn (SOURCE, "src");
604   decoder = gst_element_factory_make_or_warn ("mad", "dec");
605   queue = gst_element_factory_make_or_warn ("queue", "queue");
606   osssink = gst_element_factory_make_or_warn (ASINK, "sink");
607
608   seekable_elements = g_list_prepend (seekable_elements, osssink);
609
610   g_object_set (G_OBJECT (src), "location", location, NULL);
611   //g_object_set (G_OBJECT (osssink), "fragment", 0x00180008, NULL);
612
613   gst_bin_add (GST_BIN (pipeline), src);
614   gst_bin_add (GST_BIN (pipeline), decoder);
615   gst_bin_add (GST_BIN (pipeline), queue);
616   gst_bin_add (GST_BIN (pipeline), osssink);
617
618   gst_element_link (src, decoder);
619   gst_element_link (decoder, queue);
620   gst_element_link (queue, osssink);
621
622   seekable = gst_element_get_pad (queue, "src");
623   seekable_pads = g_list_prepend (seekable_pads, seekable);
624   rate_pads = g_list_prepend (rate_pads, seekable);
625   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
626
627   return pipeline;
628 }
629
630 static GstElement *
631 make_avi_pipeline (const gchar * location)
632 {
633   GstElement *pipeline, *audio_bin, *video_bin;
634   GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
635   GstElement *a_queue = NULL, *v_queue = NULL;
636   GstPad *seekable;
637
638   pipeline = gst_pipeline_new ("app");
639
640   src = gst_element_factory_make_or_warn (SOURCE, "src");
641   g_object_set (G_OBJECT (src), "location", location, NULL);
642
643   demux = gst_element_factory_make_or_warn ("avidemux", "demux");
644   seekable_elements = g_list_prepend (seekable_elements, demux);
645
646   gst_bin_add (GST_BIN (pipeline), src);
647   gst_bin_add (GST_BIN (pipeline), demux);
648   gst_element_link (src, demux);
649
650   audio_bin = gst_bin_new ("a_decoder_bin");
651   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
652   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
653   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
654   gst_element_link (a_decoder, a_queue);
655   gst_element_link (a_queue, audiosink);
656   gst_bin_add (GST_BIN (audio_bin), a_decoder);
657   gst_bin_add (GST_BIN (audio_bin), a_queue);
658   gst_bin_add (GST_BIN (audio_bin), audiosink);
659   gst_element_set_state (audio_bin, GST_STATE_PAUSED);
660
661   setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder,
662           "sink"), audio_bin);
663
664   seekable = gst_element_get_pad (a_queue, "src");
665   seekable_pads = g_list_prepend (seekable_pads, seekable);
666   rate_pads = g_list_prepend (rate_pads, seekable);
667   rate_pads =
668       g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
669
670   video_bin = gst_bin_new ("v_decoder_bin");
671   v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec");
672   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
673   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
674   gst_element_link (v_decoder, v_queue);
675   gst_element_link (v_queue, videosink);
676   gst_bin_add (GST_BIN (video_bin), v_decoder);
677   gst_bin_add (GST_BIN (video_bin), v_queue);
678   gst_bin_add (GST_BIN (video_bin), videosink);
679
680   gst_element_set_state (video_bin, GST_STATE_PAUSED);
681
682   setup_dynamic_link (demux, "video_00", gst_element_get_pad (v_decoder,
683           "sink"), video_bin);
684
685   seekable = gst_element_get_pad (v_queue, "src");
686   seekable_pads = g_list_prepend (seekable_pads, seekable);
687   rate_pads = g_list_prepend (rate_pads, seekable);
688   rate_pads =
689       g_list_prepend (rate_pads, gst_element_get_pad (v_decoder, "sink"));
690
691   return pipeline;
692 }
693
694 static GstElement *
695 make_mpeg_pipeline (const gchar * location)
696 {
697   GstElement *pipeline, *audio_bin, *video_bin;
698   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
699   GstElement *audiosink, *videosink;
700   GstElement *a_queue, *v_queue;
701   GstPad *seekable;
702   GstPad *pad;
703
704   pipeline = gst_pipeline_new ("app");
705
706   src = gst_element_factory_make_or_warn (SOURCE, "src");
707   g_object_set (G_OBJECT (src), "location", location, NULL);
708
709   //demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
710   demux = gst_element_factory_make_or_warn ("flupsdemux", "demux");
711
712   gst_bin_add (GST_BIN (pipeline), src);
713   gst_bin_add (GST_BIN (pipeline), demux);
714   gst_element_link (src, demux);
715
716   audio_bin = gst_bin_new ("a_decoder_bin");
717   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
718   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
719   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
720   gst_bin_add (GST_BIN (audio_bin), a_decoder);
721   gst_bin_add (GST_BIN (audio_bin), a_queue);
722   gst_bin_add (GST_BIN (audio_bin), audiosink);
723
724   gst_element_link (a_decoder, a_queue);
725   gst_element_link (a_queue, audiosink);
726
727   gst_bin_add (GST_BIN (pipeline), audio_bin);
728
729   pad = gst_element_get_pad (a_decoder, "sink");
730   gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
731   gst_object_unref (pad);
732
733   setup_dynamic_link (demux, "audio_c0", gst_element_get_pad (audio_bin,
734           "sink"), NULL);
735
736   video_bin = gst_bin_new ("v_decoder_bin");
737   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
738   v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
739   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
740   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
741
742   gst_bin_add (GST_BIN (video_bin), v_decoder);
743   gst_bin_add (GST_BIN (video_bin), v_queue);
744   gst_bin_add (GST_BIN (video_bin), v_filter);
745   gst_bin_add (GST_BIN (video_bin), videosink);
746
747   gst_element_link (v_decoder, v_queue);
748   gst_element_link (v_queue, v_filter);
749   gst_element_link (v_filter, videosink);
750
751   gst_bin_add (GST_BIN (pipeline), video_bin);
752
753   pad = gst_element_get_pad (v_decoder, "sink");
754   gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
755   gst_object_unref (pad);
756
757   setup_dynamic_link (demux, "video_e0", gst_element_get_pad (video_bin,
758           "sink"), NULL);
759
760   seekable = gst_element_get_pad (v_filter, "src");
761   seekable_pads = g_list_prepend (seekable_pads, seekable);
762   rate_pads = g_list_prepend (rate_pads, seekable);
763   rate_pads =
764       g_list_prepend (rate_pads, gst_element_get_pad (v_decoder, "sink"));
765
766   return pipeline;
767 }
768
769 static GstElement *
770 make_mpegnt_pipeline (const gchar * location)
771 {
772   GstElement *pipeline, *audio_bin, *video_bin;
773   GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
774   GstElement *audiosink, *videosink;
775   GstElement *a_queue;
776   GstPad *seekable;
777
778   pipeline = gst_pipeline_new ("app");
779
780   src = gst_element_factory_make_or_warn (SOURCE, "src");
781   g_object_set (G_OBJECT (src), "location", location, NULL);
782
783   demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
784   //g_object_set (G_OBJECT (demux), "sync", TRUE, NULL);
785
786   seekable_elements = g_list_prepend (seekable_elements, demux);
787
788   gst_bin_add (GST_BIN (pipeline), src);
789   gst_bin_add (GST_BIN (pipeline), demux);
790   gst_element_link (src, demux);
791
792   audio_bin = gst_bin_new ("a_decoder_bin");
793   a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
794   a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
795   audiosink = gst_element_factory_make_or_warn (ASINK, "a_sink");
796   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
797   g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
798   gst_element_link (a_decoder, a_queue);
799   gst_element_link (a_queue, audiosink);
800   gst_bin_add (GST_BIN (audio_bin), a_decoder);
801   gst_bin_add (GST_BIN (audio_bin), a_queue);
802   gst_bin_add (GST_BIN (audio_bin), audiosink);
803
804   setup_dynamic_link (demux, "audio_00", gst_element_get_pad (a_decoder,
805           "sink"), audio_bin);
806
807   seekable = gst_element_get_pad (a_queue, "src");
808   seekable_pads = g_list_prepend (seekable_pads, seekable);
809   rate_pads = g_list_prepend (rate_pads, seekable);
810   rate_pads =
811       g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
812
813   video_bin = gst_bin_new ("v_decoder_bin");
814   v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
815   v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
816   videosink = gst_element_factory_make_or_warn (VSINK, "v_sink");
817   gst_element_link_many (v_decoder, v_filter, videosink, NULL);
818
819   gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL);
820
821   setup_dynamic_link (demux, "video_00", gst_element_get_pad (v_decoder,
822           "sink"), video_bin);
823
824   seekable = gst_element_get_pad (v_decoder, "src");
825   seekable_pads = g_list_prepend (seekable_pads, seekable);
826   rate_pads = g_list_prepend (rate_pads, seekable);
827   rate_pads =
828       g_list_prepend (rate_pads, gst_element_get_pad (v_decoder, "sink"));
829
830   return pipeline;
831 }
832
833 static GstElement *
834 make_playerbin_pipeline (const gchar * location)
835 {
836   GstElement *player;
837
838   player = gst_element_factory_make ("playbin", "player");
839   g_assert (player);
840
841   g_object_set (G_OBJECT (player), "uri", location, NULL);
842
843   seekable_elements = g_list_prepend (seekable_elements, player);
844
845   /* force element seeking on this pipeline */
846   elem_seek = TRUE;
847
848   return player;
849 }
850
851 static GstElement *
852 make_playerbin2_pipeline (const gchar * location)
853 {
854   GstElement *player;
855
856   player = gst_element_factory_make ("playbin2", "player");
857   g_assert (player);
858
859   g_object_set (G_OBJECT (player), "uri", location, NULL);
860
861   seekable_elements = g_list_prepend (seekable_elements, player);
862
863   /* force element seeking on this pipeline */
864   elem_seek = TRUE;
865
866   return player;
867 }
868
869 #ifndef GST_DISABLE_PARSE
870 static GstElement *
871 make_parselaunch_pipeline (const gchar * description)
872 {
873   GstElement *pipeline;
874   GError *error;
875
876   pipeline = gst_parse_launch (description, &error);
877
878   seekable_elements = g_list_prepend (seekable_elements, pipeline);
879
880   elem_seek = TRUE;
881
882   return pipeline;
883 }
884 #endif
885
886 typedef struct
887 {
888   gchar *name;
889   GstElement *(*func) (const gchar * location);
890 }
891 Pipeline;
892
893 static Pipeline pipelines[] = {
894   {"mp3", make_mp3_pipeline},
895   {"avi", make_avi_pipeline},
896   {"mpeg1", make_mpeg_pipeline},
897   {"mpegparse", make_parse_pipeline},
898   {"vorbis", make_vorbis_pipeline},
899   {"theora", make_theora_pipeline},
900   {"ogg/v/t", make_vorbis_theora_pipeline},
901   {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline},
902   {"sid", make_sid_pipeline},
903   {"flac", make_flac_pipeline},
904   {"wav", make_wav_pipeline},
905   {"mod", make_mod_pipeline},
906   {"dv", make_dv_pipeline},
907   {"mpeg1nothreads", make_mpegnt_pipeline},
908   {"playerbin", make_playerbin_pipeline},
909 #ifndef GST_DISABLE_PARSE
910   {"parse-launch", make_parselaunch_pipeline},
911 #endif
912   {"playerbin2", make_playerbin2_pipeline},
913   {NULL, NULL},
914 };
915
916 #define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
917
918 /* ui callbacks and helpers */
919
920 static gchar *
921 format_value (GtkScale * scale, gdouble value)
922 {
923   gint64 real;
924   gint64 seconds;
925   gint64 subseconds;
926
927   real = value * duration / 100;
928   seconds = (gint64) real / GST_SECOND;
929   subseconds = (gint64) real / (GST_SECOND / 100);
930
931   return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
932       G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
933 }
934
935 typedef struct
936 {
937   const gchar *name;
938   const GstFormat format;
939 }
940 seek_format;
941
942 static seek_format seek_formats[] = {
943   {"tim", GST_FORMAT_TIME},
944   {"byt", GST_FORMAT_BYTES},
945   {"buf", GST_FORMAT_BUFFERS},
946   {"def", GST_FORMAT_DEFAULT},
947   {NULL, 0},
948 };
949
950 G_GNUC_UNUSED static void
951 query_rates (void)
952 {
953   GList *walk = rate_pads;
954
955   while (walk) {
956     GstPad *pad = GST_PAD (walk->data);
957     gint i = 0;
958
959     g_print ("rate/sec  %8.8s: ", GST_PAD_NAME (pad));
960     while (seek_formats[i].name) {
961       gint64 value;
962       GstFormat format;
963
964       format = seek_formats[i].format;
965
966       if (gst_pad_query_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format,
967               &value)) {
968         g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value);
969       } else {
970         g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
971       }
972
973       i++;
974     }
975     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
976
977     walk = g_list_next (walk);
978   }
979 }
980
981 G_GNUC_UNUSED static void
982 query_positions_elems ()
983 {
984   GList *walk = seekable_elements;
985
986   while (walk) {
987     GstElement *element = GST_ELEMENT (walk->data);
988     gint i = 0;
989
990     g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
991     while (seek_formats[i].name) {
992       gint64 position, total;
993       GstFormat format;
994
995       format = seek_formats[i].format;
996
997       if (gst_element_query_position (element, &format, &position) &&
998           gst_element_query_duration (element, &format, &total)) {
999         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1000             seek_formats[i].name, position, total);
1001       } else {
1002         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1003             "*NA*");
1004       }
1005       i++;
1006     }
1007     g_print (" %s\n", GST_ELEMENT_NAME (element));
1008
1009     walk = g_list_next (walk);
1010   }
1011 }
1012
1013 G_GNUC_UNUSED static void
1014 query_positions_pads ()
1015 {
1016   GList *walk = seekable_pads;
1017
1018   while (walk) {
1019     GstPad *pad = GST_PAD (walk->data);
1020     gint i = 0;
1021
1022     g_print ("positions %8.8s: ", GST_PAD_NAME (pad));
1023     while (seek_formats[i].name) {
1024       GstFormat format;
1025       gint64 position, total;
1026
1027       format = seek_formats[i].format;
1028
1029       if (gst_pad_query_position (pad, &format, &position) &&
1030           gst_pad_query_duration (pad, &format, &total)) {
1031         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1032             seek_formats[i].name, position, total);
1033       } else {
1034         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1035             "*NA*");
1036       }
1037
1038       i++;
1039     }
1040     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1041
1042     walk = g_list_next (walk);
1043   }
1044 }
1045
1046 static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
1047     gpointer user_data);
1048 static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
1049     gpointer user_data);
1050
1051 static void
1052 set_scale (gdouble value)
1053 {
1054   g_signal_handlers_block_by_func (hscale, (void *) start_seek,
1055       (void *) pipeline);
1056   g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
1057       (void *) pipeline);
1058   gtk_adjustment_set_value (adjustment, value);
1059   g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
1060       (void *) pipeline);
1061   g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
1062       (void *) pipeline);
1063   gtk_widget_queue_draw (hscale);
1064 }
1065
1066 static gboolean
1067 update_scale (gpointer data)
1068 {
1069   GstFormat format = GST_FORMAT_TIME;
1070
1071   //position = 0;
1072   //duration = 0;
1073
1074   if (elem_seek) {
1075     if (seekable_elements) {
1076       GstElement *element = GST_ELEMENT (seekable_elements->data);
1077
1078       gst_element_query_position (element, &format, &position);
1079       gst_element_query_duration (element, &format, &duration);
1080     }
1081   } else {
1082     if (seekable_pads) {
1083       GstPad *pad = GST_PAD (seekable_pads->data);
1084
1085       gst_pad_query_position (pad, &format, &position);
1086       gst_pad_query_duration (pad, &format, &duration);
1087     }
1088   }
1089
1090   if (stats) {
1091     if (elem_seek) {
1092       query_positions_elems ();
1093     } else {
1094       query_positions_pads ();
1095     }
1096     query_rates ();
1097   }
1098   if (position >= duration)
1099     duration = position;
1100
1101   if (duration > 0) {
1102     set_scale (position * 100.0 / duration);
1103   }
1104
1105   return TRUE;
1106 }
1107
1108 static void do_seek (GtkWidget * widget);
1109
1110 static void set_update_scale (gboolean active);
1111
1112 static gboolean
1113 end_scrub (GtkWidget * widget)
1114 {
1115   GST_DEBUG ("end scrub, PAUSE");
1116   gst_element_set_state (pipeline, GST_STATE_PAUSED);
1117   seek_timeout_id = 0;
1118
1119   return FALSE;
1120 }
1121
1122 static gboolean
1123 send_event (GstEvent * event)
1124 {
1125   gboolean res = FALSE;
1126
1127   if (!elem_seek) {
1128     GList *walk = seekable_pads;
1129
1130     while (walk) {
1131       GstPad *seekable = GST_PAD (walk->data);
1132
1133       GST_DEBUG ("send event on pad %s:%s", GST_DEBUG_PAD_NAME (seekable));
1134
1135       gst_event_ref (event);
1136       res = gst_pad_send_event (seekable, event);
1137
1138       walk = g_list_next (walk);
1139     }
1140   } else {
1141     GList *walk = seekable_elements;
1142
1143     while (walk) {
1144       GstElement *seekable = GST_ELEMENT (walk->data);
1145
1146       GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
1147
1148       gst_event_ref (event);
1149       res = gst_element_send_event (seekable, event);
1150
1151       walk = g_list_next (walk);
1152     }
1153   }
1154   gst_event_unref (event);
1155   return res;
1156 }
1157
1158 static void
1159 do_seek (GtkWidget * widget)
1160 {
1161   gint64 real;
1162   gboolean res = FALSE;
1163   GstEvent *s_event;
1164   GstSeekFlags flags;
1165
1166   real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100;
1167
1168   flags = 0;
1169   if (flush_seek)
1170     flags |= GST_SEEK_FLAG_FLUSH;
1171   if (accurate_seek)
1172     flags |= GST_SEEK_FLAG_ACCURATE;
1173   if (keyframe_seek)
1174     flags |= GST_SEEK_FLAG_KEY_UNIT;
1175   if (loop_seek)
1176     flags |= GST_SEEK_FLAG_SEGMENT;
1177
1178   if (rate >= 0) {
1179     s_event = gst_event_new_seek (rate,
1180         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET, -1);
1181     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1182         rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
1183   } else {
1184     s_event = gst_event_new_seek (rate,
1185         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1186         GST_SEEK_TYPE_SET, real);
1187     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1188         rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
1189   }
1190
1191   res = send_event (s_event);
1192
1193   if (res) {
1194     if (flush_seek) {
1195       gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0);
1196       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1197     } else {
1198       set_update_scale (TRUE);
1199     }
1200   } else {
1201     g_print ("seek failed\n");
1202     set_update_scale (TRUE);
1203   }
1204 }
1205
1206 static void
1207 seek_cb (GtkWidget * widget)
1208 {
1209   /* If the timer hasn't expired yet, then the pipeline is running */
1210   if (play_scrub && seek_timeout_id != 0) {
1211     GST_DEBUG ("do scrub seek, PAUSED");
1212     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1213   }
1214
1215   GST_DEBUG ("do seek");
1216   do_seek (widget);
1217
1218   if (play_scrub) {
1219     GST_DEBUG ("do scrub seek, PLAYING");
1220     gst_element_set_state (pipeline, GST_STATE_PLAYING);
1221
1222     if (seek_timeout_id == 0) {
1223       seek_timeout_id =
1224           g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
1225     }
1226   }
1227 }
1228
1229 static void
1230 set_update_scale (gboolean active)
1231 {
1232
1233   GST_DEBUG ("update scale is %d", active);
1234
1235   if (active) {
1236     if (update_id == 0) {
1237       update_id =
1238           g_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
1239     }
1240   } else {
1241     if (update_id) {
1242       g_source_remove (update_id);
1243       update_id = 0;
1244     }
1245   }
1246 }
1247
1248 static gboolean
1249 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1250 {
1251   if (event->type != GDK_BUTTON_PRESS)
1252     return FALSE;
1253
1254   set_update_scale (FALSE);
1255
1256   if (state == GST_STATE_PLAYING && flush_seek && scrub) {
1257     GST_DEBUG ("start scrub seek, PAUSE");
1258     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1259   }
1260
1261   if (changed_id == 0 && flush_seek && scrub) {
1262     changed_id = gtk_signal_connect (GTK_OBJECT (hscale),
1263         "value_changed", G_CALLBACK (seek_cb), pipeline);
1264   }
1265
1266   return FALSE;
1267 }
1268
1269 static gboolean
1270 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1271 {
1272   if (changed_id) {
1273     g_signal_handler_disconnect (GTK_OBJECT (hscale), changed_id);
1274     changed_id = 0;
1275   }
1276
1277   if (!flush_seek || !scrub) {
1278     GST_DEBUG ("do final seek");
1279     do_seek (widget);
1280   }
1281
1282   if (seek_timeout_id != 0) {
1283     g_source_remove (seek_timeout_id);
1284     seek_timeout_id = 0;
1285     /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
1286      * instead. */
1287     if (state == GST_STATE_PAUSED) {
1288       GST_DEBUG ("stop scrub seek, PAUSED");
1289       gst_element_set_state (pipeline, GST_STATE_PAUSED);
1290     }
1291   } else {
1292     if (state == GST_STATE_PLAYING) {
1293       GST_DEBUG ("stop scrub seek, PLAYING");
1294       gst_element_set_state (pipeline, GST_STATE_PLAYING);
1295     }
1296   }
1297
1298   return FALSE;
1299 }
1300
1301 static void
1302 play_cb (GtkButton * button, gpointer data)
1303 {
1304   GstStateChangeReturn ret;
1305
1306   if (state != GST_STATE_PLAYING) {
1307     g_print ("PLAY pipeline\n");
1308     ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
1309     if (ret == GST_STATE_CHANGE_FAILURE)
1310       goto failed;
1311     //do_seek(hscale);
1312
1313     state = GST_STATE_PLAYING;
1314   }
1315   return;
1316
1317 failed:
1318   {
1319     g_print ("PLAY failed\n");
1320   }
1321 }
1322
1323 static void
1324 pause_cb (GtkButton * button, gpointer data)
1325 {
1326   if (state != GST_STATE_PAUSED) {
1327     GstStateChangeReturn ret;
1328
1329     g_print ("PAUSE pipeline\n");
1330     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1331     if (ret == GST_STATE_CHANGE_FAILURE)
1332       goto failed;
1333
1334     state = GST_STATE_PAUSED;
1335   }
1336   return;
1337
1338 failed:
1339   {
1340     g_print ("PAUSE failed\n");
1341   }
1342 }
1343
1344 static void
1345 stop_cb (GtkButton * button, gpointer data)
1346 {
1347   if (state != GST_STATE_READY) {
1348     GstStateChangeReturn ret;
1349
1350     g_print ("READY pipeline\n");
1351     ret = gst_element_set_state (pipeline, GST_STATE_READY);
1352     if (ret == GST_STATE_CHANGE_FAILURE)
1353       goto failed;
1354
1355     set_scale (0.0);
1356
1357     state = GST_STATE_READY;
1358
1359     /* if one uses parse_launch, play, stop and play again it fails as all the
1360      * pads after the demuxer can't be reconnected
1361      */
1362     if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
1363       gst_element_set_state (pipeline, GST_STATE_NULL);
1364       gst_object_unref (pipeline);
1365
1366       pipeline = pipelines[pipeline_type].func (pipeline_spec);
1367       g_assert (pipeline);
1368       gst_element_set_state (pipeline, GST_STATE_READY);
1369     }
1370   }
1371   return;
1372
1373 failed:
1374   {
1375     g_print ("STOP failed\n");
1376   }
1377 }
1378
1379 static void
1380 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1381 {
1382   accurate_seek = gtk_toggle_button_get_active (button);
1383 }
1384
1385 static void
1386 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1387 {
1388   keyframe_seek = gtk_toggle_button_get_active (button);
1389 }
1390
1391 static void
1392 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1393 {
1394   loop_seek = gtk_toggle_button_get_active (button);
1395   if (state == GST_STATE_PLAYING) {
1396     do_seek (hscale);
1397   }
1398 }
1399
1400 static void
1401 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1402 {
1403   flush_seek = gtk_toggle_button_get_active (button);
1404 }
1405
1406 static void
1407 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1408 {
1409   scrub = gtk_toggle_button_get_active (button);
1410 }
1411
1412 static void
1413 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1414 {
1415   play_scrub = gtk_toggle_button_get_active (button);
1416 }
1417
1418 static void
1419 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1420 {
1421   gboolean res = FALSE;
1422   GstEvent *s_event;
1423   GstSeekFlags flags;
1424
1425   rate = gtk_spin_button_get_value (button);
1426
1427   flags = 0;
1428   if (flush_seek)
1429     flags |= GST_SEEK_FLAG_FLUSH;
1430   if (loop_seek)
1431     flags |= GST_SEEK_FLAG_SEGMENT;
1432
1433   if (rate >= 0) {
1434     s_event = gst_event_new_seek (rate,
1435         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1436         GST_SEEK_TYPE_SET, -1);
1437   } else {
1438     s_event = gst_event_new_seek (rate,
1439         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1440         GST_SEEK_TYPE_SET, position);
1441   }
1442
1443   GST_DEBUG ("rate changed to %lf", rate);
1444
1445   res = send_event (s_event);
1446
1447   if (res) {
1448     if (flush_seek) {
1449       gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0);
1450       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1451     }
1452   } else
1453     g_print ("seek failed\n");
1454 }
1455
1456 static void
1457 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1458 {
1459   const GstStructure *s;
1460
1461   s = gst_message_get_structure (message);
1462   g_print ("message from \"%s\" (%s): ",
1463       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
1464       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
1465   if (s) {
1466     gchar *sstr;
1467
1468     sstr = gst_structure_to_string (s);
1469     g_print ("%s\n", sstr);
1470     g_free (sstr);
1471   } else {
1472     g_print ("no message details\n");
1473   }
1474 }
1475
1476 static void
1477 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1478 {
1479   GstStateChangeReturn ret;
1480
1481   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1482
1483   g_print ("READY pipeline\n");
1484   ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY);
1485   if (ret == GST_STATE_CHANGE_FAILURE)
1486     g_print ("READY failed\n");
1487
1488   set_scale (0.0);
1489
1490   state = GST_STATE_READY;
1491 }
1492
1493 static void
1494 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1495 {
1496   const GstStructure *s;
1497
1498   s = gst_message_get_structure (message);
1499
1500   /* We only care about state changed on the pipeline */
1501   if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
1502     GstState old, new, pending;
1503
1504     gst_message_parse_state_changed (message, &old, &new, &pending);
1505
1506     /* When state of the pipeline changes to playing we start updating scale */
1507     if (new == GST_STATE_PLAYING) {
1508       set_update_scale (TRUE);
1509     } else {
1510       set_update_scale (FALSE);
1511     }
1512   }
1513 }
1514
1515 static void
1516 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1517 {
1518   GstEvent *s_event;
1519   GstSeekFlags flags;
1520   gboolean res;
1521   GstFormat format;
1522
1523   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1524   format = GST_FORMAT_TIME;
1525   gst_message_parse_segment_done (message, &format, &position);
1526   GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1527
1528   set_update_scale (FALSE);
1529
1530   flags = GST_SEEK_FLAG_SEGMENT;
1531   if (flush_seek)
1532     flags |= GST_SEEK_FLAG_FLUSH;
1533
1534   s_event = gst_event_new_seek (rate,
1535       GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1536       GST_SEEK_TYPE_SET, duration);
1537
1538   GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
1539       rate, GST_TIME_ARGS (duration));
1540
1541   res = send_event (s_event);
1542   if (res) {
1543     if (flush_seek) {
1544       gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0);
1545       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1546     }
1547   } else
1548     g_print ("segment seek failed\n");
1549
1550   position = 0;
1551   set_update_scale (TRUE);
1552 }
1553
1554 static void
1555 print_usage (int argc, char **argv)
1556 {
1557   gint i;
1558
1559   g_print ("usage: %s <type> <filename>\n", argv[0]);
1560   g_print ("   possible types:\n");
1561
1562   for (i = 0; i < NUM_TYPES; i++) {
1563     g_print ("     %d = %s\n", i, pipelines[i].name);
1564   }
1565 }
1566
1567 int
1568 main (int argc, char **argv)
1569 {
1570   GtkWidget *window, *hbox, *vbox, *flagtable;
1571   GtkWidget *play_button, *pause_button, *stop_button;
1572   GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
1573   GtkWidget *scrub_checkbox, *play_scrub_checkbox, *rate_spinbutton;
1574   GtkWidget *rate_label;
1575   GtkTooltips *tips;
1576   GOptionEntry options[] = {
1577     {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
1578         "Show pad stats", NULL},
1579     {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek,
1580         "Seek on elements instead of pads", NULL},
1581     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
1582         "Verbose properties", NULL},
1583     {NULL}
1584   };
1585   GOptionContext *ctx;
1586   GError *err = NULL;
1587
1588   if (!g_thread_supported ())
1589     g_thread_init (NULL);
1590
1591   ctx = g_option_context_new ("- test seeking in gsteamer");
1592   g_option_context_add_main_entries (ctx, options, NULL);
1593   g_option_context_add_group (ctx, gst_init_get_option_group ());
1594
1595   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1596     g_print ("Error initializing: %s\n", err->message);
1597     exit (1);
1598   }
1599
1600   GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
1601
1602   gtk_init (&argc, &argv);
1603
1604   if (argc != 3) {
1605     print_usage (argc, argv);
1606     exit (-1);
1607   }
1608
1609   pipeline_type = atoi (argv[1]);
1610
1611   if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
1612     print_usage (argc, argv);
1613     exit (-1);
1614   }
1615
1616   pipeline_spec = argv[2];
1617
1618   pipeline = pipelines[pipeline_type].func (pipeline_spec);
1619   g_assert (pipeline);
1620
1621   /* initialize gui elements ... */
1622   tips = gtk_tooltips_new ();
1623   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1624   hbox = gtk_hbox_new (FALSE, 0);
1625   vbox = gtk_vbox_new (FALSE, 0);
1626   flagtable = gtk_table_new (4, 2, FALSE);
1627   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
1628
1629   /* media controls */
1630   play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
1631   pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
1632   stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
1633
1634   /* seek flags */
1635   accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
1636   key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
1637   loop_checkbox = gtk_check_button_new_with_label ("Loop");
1638   flush_checkbox = gtk_check_button_new_with_label ("Flush");
1639   scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
1640   play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
1641   rate_spinbutton = gtk_spin_button_new_with_range (-10, 10, 0.1);
1642   rate_label = gtk_label_new ("Rate");
1643
1644   gtk_tooltips_set_tip (tips, accurate_checkbox,
1645       "accurate position is requested, this might be considerably slower for some formats",
1646       NULL);
1647   gtk_tooltips_set_tip (tips, key_checkbox,
1648       "seek to the nearest keyframe. This might be faster but less accurate",
1649       NULL);
1650   gtk_tooltips_set_tip (tips, loop_checkbox, "loop playback", NULL);
1651   gtk_tooltips_set_tip (tips, flush_checkbox, "flush pipeline after seeking",
1652       NULL);
1653   gtk_tooltips_set_tip (tips, rate_spinbutton, "define the playback rate, "
1654       "negative value trigger reverse playback", NULL);
1655   gtk_tooltips_set_tip (tips, scrub_checkbox, "show images while seeking",
1656       NULL);
1657   gtk_tooltips_set_tip (tips, play_scrub_checkbox, "play video while seeking",
1658       NULL);
1659
1660   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
1661   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
1662
1663   gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
1664
1665   /* seek bar */
1666   adjustment =
1667       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0));
1668   hscale = gtk_hscale_new (adjustment);
1669   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
1670 #if GTK_CHECK_VERSION(2,12,0)
1671   gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
1672   gtk_range_set_fill_level (GTK_RANGE (hscale), 100.0);
1673 #endif
1674   gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS);
1675
1676   gtk_signal_connect (GTK_OBJECT (hscale),
1677       "button_press_event", G_CALLBACK (start_seek), pipeline);
1678   gtk_signal_connect (GTK_OBJECT (hscale),
1679       "button_release_event", G_CALLBACK (stop_seek), pipeline);
1680   gtk_signal_connect (GTK_OBJECT (hscale),
1681       "format_value", G_CALLBACK (format_value), pipeline);
1682
1683   /* do the packing stuff ... */
1684   gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
1685   gtk_container_add (GTK_CONTAINER (window), vbox);
1686   gtk_container_add (GTK_CONTAINER (vbox), hbox);
1687   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
1688   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
1689   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
1690   gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
1691   gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
1692       1);
1693   gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
1694   gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
1695   gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
1696   gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
1697   gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
1698       1, 2);
1699   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 3, 4, 0, 1);
1700   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 3, 4, 1,
1701       2);
1702   gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2);
1703
1704   /* connect things ... */
1705   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
1706       pipeline);
1707   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
1708       pipeline);
1709   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
1710       pipeline);
1711   g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
1712       G_CALLBACK (accurate_toggle_cb), pipeline);
1713   g_signal_connect (G_OBJECT (key_checkbox), "toggled",
1714       G_CALLBACK (key_toggle_cb), pipeline);
1715   g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
1716       G_CALLBACK (loop_toggle_cb), pipeline);
1717   g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
1718       G_CALLBACK (flush_toggle_cb), pipeline);
1719   g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
1720       G_CALLBACK (scrub_toggle_cb), pipeline);
1721   g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
1722       G_CALLBACK (play_scrub_toggle_cb), pipeline);
1723   g_signal_connect (G_OBJECT (rate_spinbutton), "value_changed",
1724       G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
1725
1726   g_signal_connect (G_OBJECT (window), "destroy", gtk_main_quit, NULL);
1727
1728   /* show the gui. */
1729   gtk_widget_show_all (window);
1730
1731   if (verbose) {
1732     g_signal_connect (pipeline, "deep_notify",
1733         G_CALLBACK (gst_object_default_deep_notify), NULL);
1734   }
1735   {
1736     GstBus *bus;
1737
1738     bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
1739     gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1740
1741     g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
1742     g_signal_connect (bus, "message::state-changed",
1743         (GCallback) msg_state_changed, pipeline);
1744     g_signal_connect (bus, "message::segment-done",
1745         (GCallback) msg_segment_done, pipeline);
1746
1747     g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
1748         pipeline);
1749     g_signal_connect (bus, "message::error", (GCallback) message_received,
1750         pipeline);
1751     g_signal_connect (bus, "message::warning", (GCallback) message_received,
1752         pipeline);
1753     g_signal_connect (bus, "message::eos", (GCallback) message_received,
1754         pipeline);
1755     g_signal_connect (bus, "message::tag", (GCallback) message_received,
1756         pipeline);
1757     g_signal_connect (bus, "message::element", (GCallback) message_received,
1758         pipeline);
1759     g_signal_connect (bus, "message::segment-done",
1760         (GCallback) message_received, pipeline);
1761     /*g_signal_connect (bus, "message::state-changed",
1762        (GCallback) message_received, pipeline);
1763      */
1764
1765   }
1766   gtk_main ();
1767
1768   g_print ("NULL pipeline\n");
1769   gst_element_set_state (pipeline, GST_STATE_NULL);
1770
1771   g_print ("free pipeline\n");
1772   gst_object_unref (pipeline);
1773
1774   return 0;
1775 }