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