Seek on the queue in mp3, added flac seeking test
[platform/upstream/gst-plugins-good.git] / examples / seeking / seek.c
1 #include <glib.h>
2 #include <gtk/gtk.h>
3 #include <gst/gst.h>
4 #include <string.h>
5
6 static GList *seekable_pads = NULL;
7 static GList *rate_pads = NULL;
8 static GList *seekable_elements = NULL;
9
10 static GstElement *pipeline;
11 static guint64 duration, position;
12 static GtkAdjustment *adjustment;
13
14 static guint update_id;
15
16 #define SOURCE "gnomevfssrc"
17 //#define SOURCE "filesrc"
18
19 #define UPDATE_INTERVAL 500
20
21 #define THREAD
22 #define PAD_SEEK
23
24 typedef struct
25 {
26   const gchar   *padname;
27   GstPad        *target;
28   GstElement    *bin;
29 } dyn_connect;
30
31 static void
32 dynamic_connect (GstPadTemplate *templ, GstPad *newpad, gpointer data)
33 {
34   dyn_connect *connect = (dyn_connect *) data;
35
36   if (!strcmp (gst_pad_get_name (newpad), connect->padname)) {
37     gst_element_set_state (pipeline, GST_STATE_PAUSED);
38     gst_bin_add (GST_BIN (pipeline), connect->bin);
39     gst_pad_connect (newpad, connect->target);
40     gst_element_set_state (pipeline, GST_STATE_PLAYING);
41
42     seekable_pads = g_list_prepend (seekable_pads, newpad);
43     rate_pads = g_list_prepend (rate_pads, newpad);
44   }
45 }
46
47 static void
48 setup_dynamic_connection (GstElement *element, const gchar *padname, GstPad *target, GstElement *bin)
49 {
50   dyn_connect *connect;
51
52   connect = g_new0 (dyn_connect, 1);
53   connect->padname      = g_strdup (padname);
54   connect->target       = target;
55   connect->bin          = bin;
56
57   g_signal_connect (G_OBJECT (element), "new_pad", G_CALLBACK (dynamic_connect), connect);
58 }
59
60 static GstElement*
61 make_flac_pipeline (const gchar *location) 
62 {
63   GstElement *pipeline;
64   GstElement *src, *decoder, *audiosink;
65   GstPad *seekable;
66   
67   pipeline = gst_pipeline_new ("app");
68
69   src = gst_element_factory_make (SOURCE, "src");
70   decoder = gst_element_factory_make ("flacdec", "decoder");
71   audiosink = gst_element_factory_make ("osssink", "sink");
72   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
73
74   g_object_set (G_OBJECT (src), "location", location, NULL);
75
76   gst_bin_add (GST_BIN (pipeline), src);
77   gst_bin_add (GST_BIN (pipeline), decoder);
78   gst_bin_add (GST_BIN (pipeline), audiosink);
79
80   gst_element_connect (src, decoder);
81   gst_element_connect (decoder, audiosink);
82
83   seekable = gst_element_get_pad (decoder, "src");
84   seekable_pads = g_list_prepend (seekable_pads, seekable);
85   rate_pads = g_list_prepend (rate_pads, seekable);
86   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
87
88   return pipeline;
89 }
90
91 static GstElement*
92 make_sid_pipeline (const gchar *location) 
93 {
94   GstElement *pipeline;
95   GstElement *src, *decoder, *audiosink;
96   GstPad *seekable;
97   
98   pipeline = gst_pipeline_new ("app");
99
100   src = gst_element_factory_make (SOURCE, "src");
101   decoder = gst_element_factory_make ("siddec", "decoder");
102   audiosink = gst_element_factory_make ("osssink", "sink");
103   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
104
105   g_object_set (G_OBJECT (src), "location", location, NULL);
106
107   gst_bin_add (GST_BIN (pipeline), src);
108   gst_bin_add (GST_BIN (pipeline), decoder);
109   gst_bin_add (GST_BIN (pipeline), audiosink);
110
111   gst_element_connect (src, decoder);
112   gst_element_connect (decoder, audiosink);
113
114   seekable = gst_element_get_pad (decoder, "src");
115   seekable_pads = g_list_prepend (seekable_pads, seekable);
116   rate_pads = g_list_prepend (rate_pads, seekable);
117   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
118
119   return pipeline;
120 }
121
122 static GstElement*
123 make_parse_pipeline (const gchar *location) 
124 {
125   GstElement *pipeline;
126   GstElement *src, *parser, *fakesink;
127   GstPad *seekable;
128   
129   pipeline = gst_pipeline_new ("app");
130
131   src = gst_element_factory_make (SOURCE, "src");
132   parser = gst_element_factory_make ("mpegparse", "parse");
133   fakesink = gst_element_factory_make ("fakesink", "sink");
134   g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
135
136   g_object_set (G_OBJECT (src), "location", location, NULL);
137
138   gst_bin_add (GST_BIN (pipeline), src);
139   gst_bin_add (GST_BIN (pipeline), parser);
140   gst_bin_add (GST_BIN (pipeline), fakesink);
141
142   gst_element_connect (src, parser);
143   gst_element_connect (parser, fakesink);
144
145   seekable = gst_element_get_pad (parser, "src");
146   seekable_pads = g_list_prepend (seekable_pads, seekable);
147   rate_pads = g_list_prepend (rate_pads, seekable);
148   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (parser, "sink"));
149
150   return pipeline;
151 }
152
153 static GstElement*
154 make_vorbis_pipeline (const gchar *location) 
155 {
156   GstElement *pipeline;
157   GstElement *src, *decoder, *audiosink;
158   GstPad *seekable;
159   
160   pipeline = gst_pipeline_new ("app");
161
162   src = gst_element_factory_make (SOURCE, "src");
163   decoder = gst_element_factory_make ("vorbisfile", "decoder");
164   audiosink = gst_element_factory_make ("osssink", "sink");
165   //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
166
167   g_object_set (G_OBJECT (src), "location", location, NULL);
168
169   gst_bin_add (GST_BIN (pipeline), src);
170   gst_bin_add (GST_BIN (pipeline), decoder);
171   gst_bin_add (GST_BIN (pipeline), audiosink);
172
173   gst_element_connect (src, decoder);
174   gst_element_connect (decoder, audiosink);
175
176   seekable = gst_element_get_pad (decoder, "src");
177   seekable_pads = g_list_prepend (seekable_pads, seekable);
178   rate_pads = g_list_prepend (rate_pads, seekable);
179   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
180
181   return pipeline;
182 }
183
184 static GstElement*
185 make_mp3_pipeline (const gchar *location) 
186 {
187   GstElement *pipeline;
188   GstElement *src, *decoder, *osssink, *queue, *audio_thread;
189   GstPad *seekable;
190   
191   pipeline = gst_pipeline_new ("app");
192
193   src = gst_element_factory_make (SOURCE, "src");
194   decoder = gst_element_factory_make ("mad", "dec");
195   queue = gst_element_factory_make ("queue", "queue");
196   osssink = gst_element_factory_make ("osssink", "sink");
197
198   audio_thread = gst_thread_new ("a_decoder_thread");
199
200   seekable_elements = g_list_prepend (seekable_elements, osssink);
201
202   g_object_set (G_OBJECT (src), "location", location, NULL);
203   g_object_set (G_OBJECT (osssink), "fragment", 0x00180008, NULL);
204
205   gst_bin_add (GST_BIN (pipeline), src);
206   gst_bin_add (GST_BIN (pipeline), decoder);
207   gst_bin_add (GST_BIN (audio_thread), queue);
208   gst_bin_add (GST_BIN (audio_thread), osssink);
209   gst_bin_add (GST_BIN (pipeline), audio_thread);
210
211   gst_element_connect (src, decoder);
212   gst_element_connect (decoder, queue);
213   gst_element_connect (queue, osssink);
214
215   seekable = gst_element_get_pad (queue, "src");
216   seekable_pads = g_list_prepend (seekable_pads, seekable);
217   rate_pads = g_list_prepend (rate_pads, seekable);
218   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (decoder, "sink"));
219
220   return pipeline;
221 }
222
223 static GstElement*
224 make_avi_pipeline (const gchar *location) 
225 {
226   GstElement *pipeline, *audio_bin, *video_bin;
227   GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
228   GstElement *a_queue = NULL, *audio_thread = NULL, *v_queue = NULL, *video_thread = NULL;
229   GstPad *seekable;
230   
231   pipeline = gst_pipeline_new ("app");
232
233   src = gst_element_factory_make (SOURCE, "src");
234   g_object_set (G_OBJECT (src), "location", location, NULL);
235
236   demux = gst_element_factory_make ("avidemux", "demux");
237   seekable_elements = g_list_prepend (seekable_elements, demux);
238
239   gst_bin_add (GST_BIN (pipeline), src);
240   gst_bin_add (GST_BIN (pipeline), demux);
241   gst_element_connect (src, demux);
242
243   audio_bin = gst_bin_new ("a_decoder_bin");
244   a_decoder = gst_element_factory_make ("mad", "a_dec");
245   audio_thread = gst_thread_new ("a_decoder_thread");
246   audiosink = gst_element_factory_make ("osssink", "a_sink");
247   //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
248   a_queue = gst_element_factory_make ("queue", "a_queue");
249   gst_element_connect (a_decoder, a_queue);
250   gst_element_connect (a_queue, audiosink);
251   gst_bin_add (GST_BIN (audio_bin), a_decoder);
252   gst_bin_add (GST_BIN (audio_bin), audio_thread);
253   gst_bin_add (GST_BIN (audio_thread), a_queue);
254   gst_bin_add (GST_BIN (audio_thread), audiosink);
255   gst_element_set_state (audio_bin, GST_STATE_READY);
256
257   setup_dynamic_connection (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin);
258
259   seekable = gst_element_get_pad (a_queue, "src");
260   seekable_pads = g_list_prepend (seekable_pads, seekable);
261   rate_pads = g_list_prepend (rate_pads, seekable);
262   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
263
264   video_bin = gst_bin_new ("v_decoder_bin");
265   //v_decoder = gst_element_factory_make ("identity", "v_dec");
266   v_decoder = gst_element_factory_make ("windec", "v_dec");
267   video_thread = gst_thread_new ("v_decoder_thread");
268   videosink = gst_element_factory_make ("xvideosink", "v_sink");
269   //videosink = gst_element_factory_make ("fakesink", "v_sink");
270   //g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
271   v_queue = gst_element_factory_make ("queue", "v_queue");
272   g_object_set (G_OBJECT (v_queue), "max_level", 10, NULL);
273   gst_element_connect (v_decoder, v_queue);
274   gst_element_connect (v_queue, videosink);
275   gst_bin_add (GST_BIN (video_bin), v_decoder);
276   gst_bin_add (GST_BIN (video_bin), video_thread);
277   gst_bin_add (GST_BIN (video_thread), v_queue);
278   gst_bin_add (GST_BIN (video_thread), videosink);
279
280   gst_element_set_state (video_bin, GST_STATE_READY);
281
282   setup_dynamic_connection (demux, "video_00", gst_element_get_pad (v_decoder, "sink"), video_bin);
283
284   seekable = gst_element_get_pad (v_queue, "src");
285   seekable_pads = g_list_prepend (seekable_pads, seekable);
286   rate_pads = g_list_prepend (rate_pads, seekable);
287   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (v_decoder, "sink"));
288
289   return pipeline;
290 }
291
292 static GstElement*
293 make_mpeg_pipeline (const gchar *location) 
294 {
295   GstElement *pipeline, *audio_bin, *video_bin;
296   GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
297   GstElement *a_queue, *audio_thread, *v_queue, *video_thread;
298   GstPad *seekable;
299   
300   pipeline = gst_pipeline_new ("app");
301
302   src = gst_element_factory_make (SOURCE, "src");
303   g_object_set (G_OBJECT (src), "location", location, NULL);
304
305   demux = gst_element_factory_make ("mpegdemux", "demux");
306   g_object_set (G_OBJECT (demux), "sync", FALSE, NULL);
307
308   seekable_elements = g_list_prepend (seekable_elements, demux);
309
310   gst_bin_add (GST_BIN (pipeline), src);
311   gst_bin_add (GST_BIN (pipeline), demux);
312   gst_element_connect (src, demux);
313
314   audio_bin = gst_bin_new ("a_decoder_bin");
315   a_decoder = gst_element_factory_make ("mad", "a_dec");
316   audio_thread = gst_thread_new ("a_decoder_thread");
317   a_queue = gst_element_factory_make ("queue", "a_queue");
318   audiosink = gst_element_factory_make ("osssink", "a_sink");
319   g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
320   gst_element_connect (a_decoder, a_queue);
321   gst_element_connect (a_queue, audiosink);
322   gst_bin_add (GST_BIN (audio_bin), a_decoder);
323   gst_bin_add (GST_BIN (audio_bin), audio_thread);
324   gst_bin_add (GST_BIN (audio_thread), a_queue);
325   gst_bin_add (GST_BIN (audio_thread), audiosink);
326
327   setup_dynamic_connection (demux, "audio_00", gst_element_get_pad (a_decoder, "sink"), audio_bin);
328
329   seekable = gst_element_get_pad (a_queue, "src");
330   seekable_pads = g_list_prepend (seekable_pads, seekable);
331   rate_pads = g_list_prepend (rate_pads, seekable);
332   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (a_decoder, "sink"));
333
334   video_bin = gst_bin_new ("v_decoder_bin");
335   v_decoder = gst_element_factory_make ("mpeg2dec", "v_dec");
336   video_thread = gst_thread_new ("v_decoder_thread");
337   v_queue = gst_element_factory_make ("queue", "v_queue");
338   videosink = gst_element_factory_make ("xvideosink", "v_sink");
339   gst_element_connect (v_decoder, v_queue);
340   gst_element_connect (v_queue, videosink);
341   gst_bin_add (GST_BIN (video_bin), v_decoder);
342   gst_bin_add (GST_BIN (video_bin), video_thread);
343   gst_bin_add (GST_BIN (video_thread), v_queue);
344   gst_bin_add (GST_BIN (video_thread), videosink);
345
346   setup_dynamic_connection (demux, "video_00", gst_element_get_pad (v_decoder, "sink"), video_bin);
347
348   seekable = gst_element_get_pad (v_queue, "src");
349   seekable_pads = g_list_prepend (seekable_pads, seekable);
350   rate_pads = g_list_prepend (rate_pads, seekable);
351   rate_pads = g_list_prepend (rate_pads, gst_element_get_pad (v_decoder, "sink"));
352
353   return pipeline;
354 }
355
356 static gchar*
357 format_value (GtkScale *scale,
358               gdouble   value)
359 {
360   gint64 real;
361   gint64 seconds;
362   gint64 subseconds;
363
364   real = value * duration / 100;
365   seconds = (gint64) real / GST_SECOND;
366   subseconds = (gint64) real / (GST_SECOND / 100);
367
368   return g_strdup_printf ("%02lld:%02lld:%02lld",
369                           seconds/60, 
370                           seconds%60, 
371                           subseconds%100);
372 }
373
374 typedef struct
375 {
376   const gchar *name;
377   const GstFormat format;
378 } seek_format;
379
380 static seek_format seek_formats[] = 
381 {
382   { "tim",  GST_FORMAT_TIME    },
383   { "byt",  GST_FORMAT_BYTES   },
384   { "unt",  GST_FORMAT_UNITS   },
385   { "buf",  GST_FORMAT_BUFFERS },
386   { "def",  GST_FORMAT_DEFAULT },
387   { NULL, 0 }, 
388 };
389
390 G_GNUC_UNUSED static void
391 query_rates (void)
392 {
393   GList *walk = rate_pads;
394
395   while (walk) {
396     GstPad *pad = GST_PAD (walk->data);
397     gint i = 0;
398
399     g_print ("rate/sec  %8.8s: ", GST_PAD_NAME (pad));
400     while (seek_formats[i].name) {
401       gint64 value;
402       GstFormat format;
403
404       format = seek_formats[i].format;
405
406       if (gst_pad_convert (pad, GST_FORMAT_TIME, GST_SECOND, 
407                            &format, &value)) 
408       {
409         g_print ("%s %13lld | ", seek_formats[i].name, value);
410       }
411       else {
412         g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
413       }
414
415       i++;
416     }
417     g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
418
419     walk = g_list_next (walk);
420   }
421 }
422
423 G_GNUC_UNUSED static void
424 query_durations (GstPad *pad)
425 {
426   gint i = 0;
427
428   g_print ("durations %8.8s: ", GST_PAD_NAME (pad));
429   while (seek_formats[i].name) {
430     gboolean res;
431     gint64 value;
432     GstFormat format;
433
434     format = seek_formats[i].format;
435     res = gst_pad_query (pad, GST_PAD_QUERY_TOTAL, &format, &value);
436     if (res) {
437       g_print ("%s %13lld | ", seek_formats[i].name, value);
438       if (seek_formats[i].format == GST_FORMAT_TIME)
439         duration = value;
440     }
441     else {
442       g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
443     }
444     i++;
445   }
446   g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
447 }
448
449 G_GNUC_UNUSED static void
450 query_positions (GstPad *pad)
451 {
452   gint i = 0;
453
454   g_print ("positions %8.8s: ", GST_PAD_NAME (pad));
455   while (seek_formats[i].name) {
456     gboolean res;
457     gint64 value;
458     GstFormat format;
459
460     format = seek_formats[i].format;
461     res = gst_pad_query (pad, GST_PAD_QUERY_POSITION, &format, &value);
462     if (res) {
463       g_print ("%s %13lld | ", seek_formats[i].name, value);
464       if (seek_formats[i].format == GST_FORMAT_TIME)
465         position = value;
466     }
467     else {
468       g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
469     }
470     i++;
471   }
472   g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
473 }
474
475 static gboolean
476 update_scale (gpointer data) 
477 {
478   GList *walk = seekable_pads;
479   GstClock *clock;
480
481   clock = gst_bin_get_clock (GST_BIN (pipeline));
482
483   g_print ("clock:                  %13llu  (%s)\n", gst_clock_get_time (clock), gst_object_get_name (GST_OBJECT (clock)));
484
485   while (walk) {
486     GstPad *pad = GST_PAD (walk->data);
487
488     query_durations (pad);
489     query_positions (pad);
490
491     walk = g_list_next (walk);
492   }
493   query_rates ();
494
495   if (duration > 0) {
496     gtk_adjustment_set_value (adjustment, position * 100.0 / duration);
497   }
498
499   return TRUE;
500 }
501
502 static gboolean
503 iterate (gpointer data)
504 {
505   gboolean res;
506
507   res = gst_bin_iterate (GST_BIN (data));
508   if (!res) {
509     gtk_timeout_remove (update_id);
510     g_print ("stopping iterations\n");
511   }
512   return res;
513 }
514
515 static gboolean
516 start_seek (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
517 {
518   gst_element_set_state (pipeline, GST_STATE_PAUSED);
519   gtk_timeout_remove (update_id);
520
521   return FALSE;
522 }
523
524 static gboolean
525 stop_seek (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
526 {
527   gint64 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / 100;
528   gboolean res;
529   GstEvent *s_event;
530 #ifdef PAD_SEEK
531   GList *walk = seekable_pads;
532
533   while (walk) {
534     GstPad *seekable = GST_PAD (walk->data);
535
536     g_print ("seek to %lld on pad %s:%s\n", real, GST_DEBUG_PAD_NAME (seekable));
537     s_event = gst_event_new_seek (GST_FORMAT_TIME |
538                                   GST_SEEK_METHOD_SET |
539                                   GST_SEEK_FLAG_FLUSH, real);
540
541     res = gst_pad_send_event (seekable, s_event);
542     gst_event_free (s_event);
543
544     walk = g_list_next (walk);
545   }
546 #else
547   GList *walk = seekable_elements;
548
549   while (walk) {
550     GstElement *seekable = GST_ELEMENT (walk->data);
551
552     g_print ("seek to %lld on element %s\n", real, gst_element_get_name (seekable));
553     s_event = gst_event_new_seek (GST_FORMAT_TIME |
554                                   GST_SEEK_METHOD_SET |
555                                   GST_SEEK_FLAG_FLUSH, real);
556
557     res = gst_element_send_event (seekable, s_event);
558     gst_event_free (s_event);
559
560     walk = g_list_next (walk);
561   }
562 #endif
563
564   gst_element_set_state (pipeline, GST_STATE_PLAYING);
565   gtk_idle_add ((GtkFunction) iterate, pipeline);
566   update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
567
568   return FALSE;
569 }
570
571 static void
572 play_cb (GtkButton * button, gpointer data)
573 {
574   if (gst_element_get_state (pipeline) != GST_STATE_PLAYING) {
575     gst_element_set_state (pipeline, GST_STATE_PLAYING);
576     gtk_idle_add ((GtkFunction) iterate, pipeline);
577     update_id = gtk_timeout_add (UPDATE_INTERVAL, (GtkFunction) update_scale, pipeline);
578   }
579 }
580
581 static void
582 pause_cb (GtkButton * button, gpointer data)
583 {
584   if (gst_element_get_state (pipeline) != GST_STATE_PAUSED) {
585     gst_element_set_state (pipeline, GST_STATE_PAUSED);
586     gtk_timeout_remove (update_id);
587   }
588 }
589
590 static void
591 stop_cb (GtkButton * button, gpointer data)
592 {
593   if (gst_element_get_state (pipeline) != GST_STATE_READY) {
594     gst_element_set_state (pipeline, GST_STATE_READY);
595     gtk_timeout_remove (update_id);
596   }
597 }
598
599 int
600 main (int argc, char **argv)
601 {
602   GtkWidget *window, *hbox, *vbox, 
603             *play_button, *pause_button, *stop_button, 
604             *hscale;
605
606   gst_init (&argc, &argv);
607   gtk_init (&argc, &argv);
608
609   if (argc != 3) {
610     g_print ("usage: %s <type 0=mp3 1=avi 2=mpeg1 3=mpegparse 4=vorbis 5=sid 6=flac> <filename>\n", argv[0]);
611     exit (-1);
612   }
613
614   if (atoi (argv[1]) == 0) 
615     pipeline = make_mp3_pipeline (argv[2]);
616   else if (atoi (argv[1]) == 1) 
617     pipeline = make_avi_pipeline (argv[2]);
618   else if (atoi (argv[1]) == 2) 
619     pipeline = make_mpeg_pipeline (argv[2]);
620   else if (atoi (argv[1]) == 3)
621     pipeline = make_parse_pipeline (argv[2]);
622   else if (atoi (argv[1]) == 4) 
623     pipeline = make_vorbis_pipeline (argv[2]);
624   else if (atoi (argv[1]) == 5) 
625     pipeline = make_sid_pipeline (argv[2]);
626   else if (atoi (argv[1]) == 6) 
627     pipeline = make_flac_pipeline (argv[2]);
628
629   /* initialize gui elements ... */
630   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
631   hbox = gtk_hbox_new (FALSE, 0);
632   vbox = gtk_vbox_new (FALSE, 0);
633   play_button = gtk_button_new_with_label ("play");
634   pause_button = gtk_button_new_with_label ("pause");
635   stop_button = gtk_button_new_with_label ("stop");
636
637   adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, 100.0, 0.1, 1.0, 1.0));
638   hscale = gtk_hscale_new (adjustment);
639   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
640   gtk_range_set_update_policy (GTK_RANGE (hscale), GTK_UPDATE_CONTINUOUS);
641
642   gtk_signal_connect(GTK_OBJECT(hscale),
643                              "button_press_event", G_CALLBACK (start_seek), pipeline);
644   gtk_signal_connect(GTK_OBJECT(hscale),
645                              "button_release_event", G_CALLBACK (stop_seek), pipeline);
646   gtk_signal_connect(GTK_OBJECT(hscale),
647                              "format_value", G_CALLBACK (format_value), pipeline);
648
649   /* do the packing stuff ... */
650   gtk_window_set_default_size (GTK_WINDOW (window), 96, 96);
651   gtk_container_add (GTK_CONTAINER (window), vbox);
652   gtk_container_add (GTK_CONTAINER (vbox), hbox);
653   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
654   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
655   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
656   gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2);
657
658   /* connect things ... */
659   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb), pipeline);
660   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb), pipeline);
661   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb), pipeline);
662   g_signal_connect (G_OBJECT (window), "delete_event", gtk_main_quit, NULL);
663
664   /* show the gui. */
665   gtk_widget_show_all (window);
666
667   gtk_main ();
668
669   //g_mem_chunk_info();
670
671   return 0;
672 }