rtsp: fix connection
[platform/upstream/gstreamer.git] / tests / examples / seek / seek.c
1 /* GStreamer
2  *
3  * seek.c: seeking sample application
4  *
5  * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
6  *               2006 Stefan Kost <ensonic@users.sf.net>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
28  * with newer GTK versions (>= 3.3.0) */
29 #define GDK_DISABLE_DEPRECATION_WARNINGS
30
31 #include <stdlib.h>
32 #include <math.h>
33 #include <glib.h>
34 #include <gtk/gtk.h>
35 #include <gst/gst.h>
36 #include <string.h>
37
38 #include <gdk/gdk.h>
39 #if defined (GDK_WINDOWING_X11)
40 #include <gdk/gdkx.h>
41 #elif defined (GDK_WINDOWING_WIN32)
42 #include <gdk/gdkwin32.h>
43 #endif
44
45 #include <gst/video/videooverlay.h>
46
47 GST_DEBUG_CATEGORY_STATIC (seek_debug);
48 #define GST_CAT_DEFAULT (seek_debug)
49
50 /* configuration */
51
52 #define SOURCE "filesrc"
53
54 static gchar *opt_audiosink_str;        /* NULL */
55 static gchar *opt_videosink_str;        /* NULL */
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 #define N_GRAD 1000.0
74
75 static GList *seekable_elements = NULL;
76
77 static gboolean accurate_seek = FALSE;
78 static gboolean keyframe_seek = FALSE;
79 static gboolean loop_seek = FALSE;
80 static gboolean flush_seek = TRUE;
81 static gboolean scrub = TRUE;
82 static gboolean play_scrub = FALSE;
83 static gboolean skip_seek = FALSE;
84 static gdouble rate = 1.0;
85
86 static GstElement *pipeline;
87 static gint pipeline_type;
88 static const gchar *pipeline_spec;
89 static gint64 position = -1;
90 static gint64 duration = -1;
91 static GtkAdjustment *adjustment;
92 static GtkWidget *hscale, *statusbar;
93 static guint status_id = 0;
94 static gboolean stats = FALSE;
95 static gboolean verbose = FALSE;
96
97 static gboolean is_live = FALSE;
98 static gboolean buffering = FALSE;
99 static GstBufferingMode mode;
100 static gint64 buffering_left;
101 static GstState state = GST_STATE_NULL;
102 static guint update_id = 0;
103 static guint seek_timeout_id = 0;
104 static gulong changed_id;
105 static guint fill_id = 0;
106
107 static gint n_video = 0, n_audio = 0, n_text = 0;
108 static gboolean need_streams = TRUE;
109 static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
110 static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
111 static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
112 static GtkWidget *skip_checkbox, *video_window, *download_checkbox;
113 static GtkWidget *buffer_checkbox, *rate_spinbutton;
114
115 static GMutex state_mutex;
116
117 static GtkWidget *format_combo, *step_amount_spinbutton, *step_rate_spinbutton;
118 static GtkWidget *shuttle_checkbox, *step_button;
119 static GtkWidget *shuttle_hscale;
120 static GtkAdjustment *shuttle_adjustment;
121
122 static GList *paths = NULL, *l = NULL;
123
124 /* we keep an array of the visualisation entries so that we can easily switch
125  * with the combo box index. */
126 typedef struct
127 {
128   GstElementFactory *factory;
129 } VisEntry;
130
131 static GArray *vis_entries;
132
133 static void clear_streams (GstElement * pipeline);
134 static void volume_notify_cb (GstElement * pipeline, GParamSpec * arg,
135     gpointer user_dat);
136
137 /* pipeline construction */
138
139 typedef struct
140 {
141   const gchar *padname;
142   GstPad *target;
143   GstElement *bin;
144 }
145 dyn_link;
146
147 static GstElement *
148 gst_element_factory_make_or_warn (const gchar * type, const gchar * name)
149 {
150   GstElement *element = gst_element_factory_make (type, name);
151
152 #ifndef GST_DISABLE_PARSE
153   if (!element) {
154     /* Try parsing it as a pipeline description */
155     element = gst_parse_bin_from_description (type, TRUE, NULL);
156     if (element) {
157       gst_element_set_name (element, name);
158     }
159   }
160 #endif
161
162   if (!element) {
163     g_warning ("Failed to create element %s of type %s", name, type);
164   }
165
166   return element;
167 }
168
169 static void
170 playerbin_set_uri (GstElement * player, const gchar * location)
171 {
172   gchar *uri;
173
174   /* Add "file://" prefix for convenience */
175   if (g_str_has_prefix (location, "/") || !gst_uri_is_valid (location)) {
176     uri = gst_filename_to_uri (location, NULL);
177     g_print ("Setting URI: %s\n", uri);
178     g_object_set (G_OBJECT (player), "uri", uri, NULL);
179     g_free (uri);
180   } else {
181     g_print ("Setting URI: %s\n", location);
182     g_object_set (G_OBJECT (player), "uri", location, NULL);
183   }
184 }
185
186 static GstElement *
187 construct_playbin (const gchar * name, const gchar * location)
188 {
189   GstElement *player;
190   GstElement *avsink;
191
192   player = gst_element_factory_make (name, "player");
193   g_assert (player);
194
195   playerbin_set_uri (player, location);
196
197   seekable_elements = g_list_prepend (seekable_elements, player);
198
199   avsink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
200   if (avsink)
201     g_object_set (player, "audio-sink", avsink, NULL);
202
203   avsink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
204   if (avsink)
205     g_object_set (player, "video-sink", avsink, NULL);
206
207   return player;
208 }
209
210 static GstElement *
211 make_playbin_pipeline (const gchar * location)
212 {
213   GstElement *pipeline = construct_playbin ("playbin", location);
214
215   /* FIXME: this is not triggered, playbin is not forwarding it from the sink */
216   g_signal_connect (pipeline, "notify::volume", G_CALLBACK (volume_notify_cb),
217       NULL);
218   return pipeline;
219 }
220
221 #ifndef GST_DISABLE_PARSE
222 static GstElement *
223 make_parselaunch_pipeline (const gchar * description)
224 {
225   GstElement *pipeline;
226   GError *error = NULL;
227
228   pipeline = gst_parse_launch (description, &error);
229
230   seekable_elements = g_list_prepend (seekable_elements, pipeline);
231
232   return pipeline;
233 }
234 #endif
235
236 typedef struct
237 {
238   const gchar *name;
239   GstElement *(*func) (const gchar * location);
240 }
241 Pipeline;
242
243 static Pipeline pipelines[] = {
244   {"playbin", make_playbin_pipeline},
245 #ifndef GST_DISABLE_PARSE
246   {"parse-launch", make_parselaunch_pipeline},
247 #endif
248   {NULL, NULL},
249 };
250
251 #define NUM_TYPES       ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
252
253 /* ui callbacks and helpers */
254
255 static gchar *
256 format_value (GtkScale * scale, gdouble value)
257 {
258   gint64 real;
259   gint64 seconds;
260   gint64 subseconds;
261
262   real = value * duration / N_GRAD;
263   seconds = (gint64) real / GST_SECOND;
264   subseconds = (gint64) real / (GST_SECOND / N_GRAD);
265
266   return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
267       G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
268 }
269
270
271 static gchar *
272 shuttle_format_value (GtkScale * scale, gdouble value)
273 {
274   return g_strdup_printf ("%0.*g", gtk_scale_get_digits (scale), value);
275 }
276
277 typedef struct
278 {
279   const gchar *name;
280   const GstFormat format;
281 }
282 seek_format;
283
284 static seek_format seek_formats[] = {
285   {"tim", GST_FORMAT_TIME},
286   {"byt", GST_FORMAT_BYTES},
287   {"buf", GST_FORMAT_BUFFERS},
288   {"def", GST_FORMAT_DEFAULT},
289   {NULL, 0},
290 };
291
292 G_GNUC_UNUSED static void
293 query_positions_elems (void)
294 {
295   GList *walk = seekable_elements;
296
297   while (walk) {
298     GstElement *element = GST_ELEMENT (walk->data);
299     gint i = 0;
300
301     g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
302     while (seek_formats[i].name) {
303       gint64 position, total;
304       GstFormat format;
305
306       format = seek_formats[i].format;
307
308       if (gst_element_query_position (element, format, &position) &&
309           gst_element_query_duration (element, format, &total)) {
310         g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
311             seek_formats[i].name, position, total);
312       } else {
313         g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
314             "*NA*");
315       }
316       i++;
317     }
318     g_print (" %s\n", GST_ELEMENT_NAME (element));
319
320     walk = g_list_next (walk);
321   }
322 }
323
324 static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
325     gpointer user_data);
326 static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
327     gpointer user_data);
328 static void seek_cb (GtkWidget * widget);
329
330 static void
331 set_scale (gdouble value)
332 {
333   g_signal_handlers_block_by_func (hscale, (void *) start_seek,
334       (void *) pipeline);
335   g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
336       (void *) pipeline);
337   g_signal_handlers_block_by_func (hscale, (void *) seek_cb, (void *) pipeline);
338   gtk_adjustment_set_value (adjustment, value);
339   g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
340       (void *) pipeline);
341   g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
342       (void *) pipeline);
343   g_signal_handlers_unblock_by_func (hscale, (void *) seek_cb,
344       (void *) pipeline);
345   gtk_widget_queue_draw (hscale);
346 }
347
348 static gboolean
349 update_fill (gpointer data)
350 {
351   if (seekable_elements) {
352     GstElement *element = GST_ELEMENT (seekable_elements->data);
353     GstQuery *query;
354
355     query = gst_query_new_buffering (GST_FORMAT_PERCENT);
356     if (gst_element_query (element, query)) {
357       gint64 start, stop, buffering_total;
358       GstFormat format;
359       gdouble fill;
360       gboolean busy;
361       gint percent;
362       GstBufferingMode mode;
363       gint avg_in, avg_out;
364       gint64 buffering_left;
365
366       gst_query_parse_buffering_percent (query, &busy, &percent);
367       gst_query_parse_buffering_range (query, &format, &start, &stop,
368           &buffering_total);
369       gst_query_parse_buffering_stats (query, &mode, &avg_in, &avg_out,
370           &buffering_left);
371
372       /* note that we could start the playback when buffering_left < remaining
373        * playback time */
374       GST_DEBUG ("buffering total %" G_GINT64_FORMAT " ms, left %"
375           G_GINT64_FORMAT " ms", buffering_total, buffering_left);
376       GST_DEBUG ("start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT,
377           start, stop);
378
379       if (stop != -1)
380         fill = N_GRAD * stop / GST_FORMAT_PERCENT_MAX;
381       else
382         fill = N_GRAD;
383
384       gtk_range_set_fill_level (GTK_RANGE (hscale), fill);
385     }
386     gst_query_unref (query);
387   }
388   return TRUE;
389 }
390
391 static gboolean
392 update_scale (gpointer data)
393 {
394   if (seekable_elements) {
395     GstElement *element = GST_ELEMENT (seekable_elements->data);
396
397     gst_element_query_position (element, GST_FORMAT_TIME, &position);
398     gst_element_query_duration (element, GST_FORMAT_TIME, &duration);
399   }
400
401   if (stats) {
402     query_positions_elems ();
403   }
404
405   if (position >= duration)
406     duration = position;
407
408   if (duration > 0) {
409     set_scale (position * N_GRAD / duration);
410   }
411
412   /* FIXME: see make_playerbin2_pipeline() and volume_notify_cb() */
413   if (pipeline_type == 16) {
414     g_object_notify (G_OBJECT (pipeline), "volume");
415   }
416
417   return TRUE;
418 }
419
420 static void do_seek (GtkWidget * widget);
421 static void connect_bus_signals (GstElement * pipeline);
422 static void set_update_scale (gboolean active);
423 static void set_update_fill (gboolean active);
424
425 static gboolean
426 end_scrub (GtkWidget * widget)
427 {
428   GST_DEBUG ("end scrub, PAUSE");
429   gst_element_set_state (pipeline, GST_STATE_PAUSED);
430   seek_timeout_id = 0;
431
432   return FALSE;
433 }
434
435 static gboolean
436 send_event (GstEvent * event)
437 {
438   gboolean res = FALSE;
439   GList *walk = seekable_elements;
440
441   while (walk) {
442     GstElement *seekable = GST_ELEMENT (walk->data);
443
444     GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
445
446     gst_event_ref (event);
447     res = gst_element_send_event (seekable, event);
448
449     walk = g_list_next (walk);
450   }
451   gst_event_unref (event);
452   return res;
453 }
454
455 static void
456 do_seek (GtkWidget * widget)
457 {
458   gint64 real;
459   gboolean res = FALSE;
460   GstEvent *s_event;
461   GstSeekFlags flags;
462
463   real = gtk_range_get_value (GTK_RANGE (widget)) * duration / N_GRAD;
464
465   GST_DEBUG ("value=%f, real=%" G_GINT64_FORMAT,
466       gtk_range_get_value (GTK_RANGE (widget)), real);
467
468   flags = 0;
469   if (flush_seek)
470     flags |= GST_SEEK_FLAG_FLUSH;
471   if (accurate_seek)
472     flags |= GST_SEEK_FLAG_ACCURATE;
473   if (keyframe_seek)
474     flags |= GST_SEEK_FLAG_KEY_UNIT;
475   if (loop_seek)
476     flags |= GST_SEEK_FLAG_SEGMENT;
477   if (skip_seek)
478     flags |= GST_SEEK_FLAG_SKIP;
479
480   if (rate >= 0) {
481     s_event = gst_event_new_seek (rate,
482         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET,
483         GST_CLOCK_TIME_NONE);
484     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
485         rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
486   } else {
487     s_event = gst_event_new_seek (rate,
488         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
489         GST_SEEK_TYPE_SET, real);
490     GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
491         rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
492   }
493
494   res = send_event (s_event);
495
496   if (res) {
497     if (flush_seek) {
498       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
499     } else {
500       set_update_scale (TRUE);
501     }
502   } else {
503     g_print ("seek failed\n");
504     set_update_scale (TRUE);
505   }
506 }
507
508 static void
509 seek_cb (GtkWidget * widget)
510 {
511   /* If the timer hasn't expired yet, then the pipeline is running */
512   if (play_scrub && seek_timeout_id != 0) {
513     GST_DEBUG ("do scrub seek, PAUSED");
514     gst_element_set_state (pipeline, GST_STATE_PAUSED);
515   }
516
517   GST_DEBUG ("do seek");
518   do_seek (widget);
519
520   if (play_scrub) {
521     GST_DEBUG ("do scrub seek, PLAYING");
522     gst_element_set_state (pipeline, GST_STATE_PLAYING);
523
524     if (seek_timeout_id == 0) {
525       seek_timeout_id =
526           g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
527     }
528   }
529 }
530
531 static void
532 set_update_fill (gboolean active)
533 {
534   GST_DEBUG ("fill scale is %d", active);
535
536   if (active) {
537     if (fill_id == 0) {
538       fill_id =
539           g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline);
540     }
541   } else {
542     if (fill_id) {
543       g_source_remove (fill_id);
544       fill_id = 0;
545     }
546   }
547 }
548
549 static void
550 set_update_scale (gboolean active)
551 {
552
553   GST_DEBUG ("update scale is %d", active);
554
555   if (active) {
556     if (update_id == 0) {
557       update_id =
558           g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline);
559     }
560   } else {
561     if (update_id) {
562       g_source_remove (update_id);
563       update_id = 0;
564     }
565   }
566 }
567
568 static gboolean
569 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
570 {
571   if (event->type != GDK_BUTTON_PRESS)
572     return FALSE;
573
574   set_update_scale (FALSE);
575
576   if (state == GST_STATE_PLAYING && flush_seek && scrub) {
577     GST_DEBUG ("start scrub seek, PAUSE");
578     gst_element_set_state (pipeline, GST_STATE_PAUSED);
579   }
580
581   if (changed_id == 0 && flush_seek && scrub) {
582     changed_id =
583         g_signal_connect (hscale, "value_changed", G_CALLBACK (seek_cb),
584         pipeline);
585   }
586
587   return FALSE;
588 }
589
590 static gboolean
591 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
592 {
593   if (changed_id) {
594     g_signal_handler_disconnect (hscale, changed_id);
595     changed_id = 0;
596   }
597
598   if (!flush_seek || !scrub) {
599     GST_DEBUG ("do final seek");
600     do_seek (widget);
601   }
602
603   if (seek_timeout_id != 0) {
604     g_source_remove (seek_timeout_id);
605     seek_timeout_id = 0;
606     /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
607      * instead. */
608     if (state == GST_STATE_PAUSED) {
609       GST_DEBUG ("stop scrub seek, PAUSED");
610       gst_element_set_state (pipeline, GST_STATE_PAUSED);
611     }
612   } else {
613     if (state == GST_STATE_PLAYING) {
614       GST_DEBUG ("stop scrub seek, PLAYING");
615       gst_element_set_state (pipeline, GST_STATE_PLAYING);
616     }
617   }
618
619   return FALSE;
620 }
621
622 static void
623 play_cb (GtkButton * button, gpointer data)
624 {
625   GstStateChangeReturn ret;
626
627   if (state != GST_STATE_PLAYING) {
628     g_print ("PLAY pipeline\n");
629     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
630
631     ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
632     switch (ret) {
633       case GST_STATE_CHANGE_FAILURE:
634         goto failed;
635       case GST_STATE_CHANGE_NO_PREROLL:
636         is_live = TRUE;
637         break;
638       default:
639         break;
640     }
641     state = GST_STATE_PLAYING;
642     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
643   }
644
645   return;
646
647 failed:
648   {
649     g_print ("PLAY failed\n");
650     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
651   }
652 }
653
654 static void
655 pause_cb (GtkButton * button, gpointer data)
656 {
657   g_mutex_lock (&state_mutex);
658   if (state != GST_STATE_PAUSED) {
659     GstStateChangeReturn ret;
660
661     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
662     g_print ("PAUSE pipeline\n");
663     ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
664     switch (ret) {
665       case GST_STATE_CHANGE_FAILURE:
666         goto failed;
667       case GST_STATE_CHANGE_NO_PREROLL:
668         is_live = TRUE;
669         break;
670       default:
671         break;
672     }
673
674     state = GST_STATE_PAUSED;
675     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
676   }
677   g_mutex_unlock (&state_mutex);
678
679   return;
680
681 failed:
682   {
683     g_mutex_unlock (&state_mutex);
684     g_print ("PAUSE failed\n");
685     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
686   }
687 }
688
689 static void
690 stop_cb (GtkButton * button, gpointer data)
691 {
692   if (state != STOP_STATE) {
693     GstStateChangeReturn ret;
694
695     g_print ("READY pipeline\n");
696     gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
697
698     g_mutex_lock (&state_mutex);
699     ret = gst_element_set_state (pipeline, STOP_STATE);
700     if (ret == GST_STATE_CHANGE_FAILURE)
701       goto failed;
702
703     state = STOP_STATE;
704     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
705     gtk_widget_queue_draw (video_window);
706
707     is_live = FALSE;
708     buffering = FALSE;
709     set_update_scale (FALSE);
710     set_scale (0.0);
711     set_update_fill (FALSE);
712
713     if (pipeline_type == 16)
714       clear_streams (pipeline);
715     g_mutex_unlock (&state_mutex);
716
717 #if 0
718     /* if one uses parse_launch, play, stop and play again it fails as all the
719      * pads after the demuxer can't be reconnected
720      */
721     if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
722       gst_element_set_state (pipeline, GST_STATE_NULL);
723       gst_object_unref (pipeline);
724
725       g_list_free (seekable_elements);
726       seekable_elements = NULL;
727
728       pipeline = pipelines[pipeline_type].func (pipeline_spec);
729       g_assert (pipeline);
730       gst_element_set_state (pipeline, STOP_STATE);
731       connect_bus_signals (pipeline);
732     }
733 #endif
734   }
735   return;
736
737 failed:
738   {
739     g_mutex_unlock (&state_mutex);
740     g_print ("STOP failed\n");
741     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
742   }
743 }
744
745 static void
746 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
747 {
748   accurate_seek = gtk_toggle_button_get_active (button);
749 }
750
751 static void
752 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
753 {
754   keyframe_seek = gtk_toggle_button_get_active (button);
755 }
756
757 static void
758 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
759 {
760   loop_seek = gtk_toggle_button_get_active (button);
761   if (state == GST_STATE_PLAYING) {
762     do_seek (hscale);
763   }
764 }
765
766 static void
767 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
768 {
769   flush_seek = gtk_toggle_button_get_active (button);
770 }
771
772 static void
773 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
774 {
775   scrub = gtk_toggle_button_get_active (button);
776 }
777
778 static void
779 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
780 {
781   play_scrub = gtk_toggle_button_get_active (button);
782 }
783
784 static void
785 skip_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
786 {
787   skip_seek = gtk_toggle_button_get_active (button);
788   if (state == GST_STATE_PLAYING) {
789     do_seek (hscale);
790   }
791 }
792
793 static void
794 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
795 {
796   gboolean res = FALSE;
797   GstEvent *s_event;
798   GstSeekFlags flags;
799
800   rate = gtk_spin_button_get_value (button);
801
802   GST_DEBUG ("rate changed to %lf", rate);
803
804   flags = 0;
805   if (flush_seek)
806     flags |= GST_SEEK_FLAG_FLUSH;
807   if (loop_seek)
808     flags |= GST_SEEK_FLAG_SEGMENT;
809   if (accurate_seek)
810     flags |= GST_SEEK_FLAG_ACCURATE;
811   if (keyframe_seek)
812     flags |= GST_SEEK_FLAG_KEY_UNIT;
813   if (skip_seek)
814     flags |= GST_SEEK_FLAG_SKIP;
815
816   if (rate >= 0.0) {
817     s_event = gst_event_new_seek (rate,
818         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
819         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
820   } else {
821     s_event = gst_event_new_seek (rate,
822         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
823         GST_SEEK_TYPE_SET, position);
824   }
825
826   res = send_event (s_event);
827
828   if (res) {
829     if (flush_seek) {
830       gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
831     }
832   } else
833     g_print ("seek failed\n");
834 }
835
836 static void
837 update_flag (GstPipeline * pipeline, gint num, gboolean state)
838 {
839   gint flags;
840
841   g_object_get (pipeline, "flags", &flags, NULL);
842   if (state)
843     flags |= (1 << num);
844   else
845     flags &= ~(1 << num);
846   g_object_set (pipeline, "flags", flags, NULL);
847 }
848
849 static void
850 vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
851 {
852   gboolean state;
853
854   state = gtk_toggle_button_get_active (button);
855   update_flag (pipeline, 3, state);
856   gtk_widget_set_sensitive (vis_combo, state);
857 }
858
859 static void
860 audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
861 {
862   gboolean state;
863
864   state = gtk_toggle_button_get_active (button);
865   update_flag (pipeline, 1, state);
866   gtk_widget_set_sensitive (audio_combo, state);
867 }
868
869 static void
870 video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
871 {
872   gboolean state;
873
874   state = gtk_toggle_button_get_active (button);
875   update_flag (pipeline, 0, state);
876   gtk_widget_set_sensitive (video_combo, state);
877 }
878
879 static void
880 text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
881 {
882   gboolean state;
883
884   state = gtk_toggle_button_get_active (button);
885   update_flag (pipeline, 2, state);
886   gtk_widget_set_sensitive (text_combo, state);
887 }
888
889 static void
890 mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
891 {
892   gboolean mute;
893
894   mute = gtk_toggle_button_get_active (button);
895   g_object_set (pipeline, "mute", mute, NULL);
896 }
897
898 static void
899 download_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
900 {
901   gboolean state;
902
903   state = gtk_toggle_button_get_active (button);
904   update_flag (pipeline, 7, state);
905 }
906
907 static void
908 buffer_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
909 {
910   gboolean state;
911
912   state = gtk_toggle_button_get_active (button);
913   update_flag (pipeline, 8, state);
914 }
915
916 static void
917 clear_streams (GstElement * pipeline)
918 {
919   gint i;
920
921   /* remove previous info */
922   for (i = 0; i < n_video; i++)
923     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (video_combo), 0);
924   for (i = 0; i < n_audio; i++)
925     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (audio_combo), 0);
926   for (i = 0; i < n_text; i++)
927     gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (text_combo), 0);
928
929   n_audio = n_video = n_text = 0;
930   gtk_widget_set_sensitive (video_combo, FALSE);
931   gtk_widget_set_sensitive (audio_combo, FALSE);
932   gtk_widget_set_sensitive (text_combo, FALSE);
933
934   need_streams = TRUE;
935 }
936
937 static void
938 update_streams (GstPipeline * pipeline)
939 {
940   gint i;
941
942   if (pipeline_type == 16 && need_streams) {
943     GstTagList *tags;
944     gchar *name, *str;
945     gint active_idx;
946     gboolean state;
947
948     /* remove previous info */
949     clear_streams (GST_ELEMENT_CAST (pipeline));
950
951     /* here we get and update the different streams detected by playbin */
952     g_object_get (pipeline, "n-video", &n_video, NULL);
953     g_object_get (pipeline, "n-audio", &n_audio, NULL);
954     g_object_get (pipeline, "n-text", &n_text, NULL);
955
956     g_print ("video %d, audio %d, text %d\n", n_video, n_audio, n_text);
957
958     active_idx = 0;
959     for (i = 0; i < n_video; i++) {
960       g_signal_emit_by_name (pipeline, "get-video-tags", i, &tags);
961       if (tags) {
962         str = gst_structure_to_string ((GstStructure *) tags);
963         g_print ("video %d: %s\n", i, str);
964         g_free (str);
965       }
966       /* find good name for the label */
967       name = g_strdup_printf ("video %d", i + 1);
968       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (video_combo), name);
969       g_free (name);
970     }
971     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (video_checkbox));
972     gtk_widget_set_sensitive (video_combo, state && n_video > 0);
973     gtk_combo_box_set_active (GTK_COMBO_BOX (video_combo), active_idx);
974
975     active_idx = 0;
976     for (i = 0; i < n_audio; i++) {
977       g_signal_emit_by_name (pipeline, "get-audio-tags", i, &tags);
978       if (tags) {
979         str = gst_structure_to_string ((GstStructure *) tags);
980         g_print ("audio %d: %s\n", i, str);
981         g_free (str);
982       }
983       /* find good name for the label */
984       name = g_strdup_printf ("audio %d", i + 1);
985       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (audio_combo), name);
986       g_free (name);
987     }
988     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (audio_checkbox));
989     gtk_widget_set_sensitive (audio_combo, state && n_audio > 0);
990     gtk_combo_box_set_active (GTK_COMBO_BOX (audio_combo), active_idx);
991
992     active_idx = 0;
993     for (i = 0; i < n_text; i++) {
994       g_signal_emit_by_name (pipeline, "get-text-tags", i, &tags);
995
996       name = NULL;
997       if (tags) {
998         const GValue *value;
999
1000         str = gst_structure_to_string ((GstStructure *) tags);
1001         g_print ("text %d: %s\n", i, str);
1002         g_free (str);
1003
1004         /* get the language code if we can */
1005         value = gst_tag_list_get_value_index (tags, GST_TAG_LANGUAGE_CODE, 0);
1006         if (value && G_VALUE_HOLDS_STRING (value)) {
1007           name = g_strdup_printf ("text %s", g_value_get_string (value));
1008         }
1009       }
1010       /* find good name for the label if we didn't use a tag */
1011       if (name == NULL)
1012         name = g_strdup_printf ("text %d", i + 1);
1013
1014       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (text_combo), name);
1015       g_free (name);
1016     }
1017     state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_checkbox));
1018     gtk_widget_set_sensitive (text_combo, state && n_text > 0);
1019     gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), active_idx);
1020
1021     need_streams = FALSE;
1022   }
1023 }
1024
1025 static void
1026 video_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1027 {
1028   gint active;
1029
1030   active = gtk_combo_box_get_active (combo);
1031
1032   g_print ("setting current video track %d\n", active);
1033   g_object_set (pipeline, "current-video", active, NULL);
1034 }
1035
1036 static void
1037 audio_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1038 {
1039   gint active;
1040
1041   active = gtk_combo_box_get_active (combo);
1042
1043   g_print ("setting current audio track %d\n", active);
1044   g_object_set (pipeline, "current-audio", active, NULL);
1045 }
1046
1047 static void
1048 text_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1049 {
1050   gint active;
1051
1052   active = gtk_combo_box_get_active (combo);
1053
1054   g_print ("setting current text track %d\n", active);
1055   g_object_set (pipeline, "current-text", active, NULL);
1056 }
1057
1058 static gboolean
1059 filter_features (GstPluginFeature * feature, gpointer data)
1060 {
1061   GstElementFactory *f;
1062
1063   if (!GST_IS_ELEMENT_FACTORY (feature))
1064     return FALSE;
1065   f = GST_ELEMENT_FACTORY (feature);
1066   if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
1067     return FALSE;
1068
1069   return TRUE;
1070 }
1071
1072 static void
1073 init_visualization_features (void)
1074 {
1075   GList *list, *walk;
1076
1077   vis_entries = g_array_new (FALSE, FALSE, sizeof (VisEntry));
1078
1079   list = gst_registry_feature_filter (gst_registry_get (),
1080       filter_features, FALSE, NULL);
1081
1082   for (walk = list; walk; walk = g_list_next (walk)) {
1083     VisEntry entry;
1084     const gchar *name;
1085
1086     entry.factory = GST_ELEMENT_FACTORY (walk->data);
1087     name = gst_element_factory_get_longname (entry.factory);
1088
1089     g_array_append_val (vis_entries, entry);
1090     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (vis_combo), name);
1091   }
1092   gtk_combo_box_set_active (GTK_COMBO_BOX (vis_combo), 0);
1093 }
1094
1095 static void
1096 vis_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1097 {
1098   guint index;
1099   VisEntry *entry;
1100   GstElement *element;
1101
1102   /* get the selected index and get the factory for this index */
1103   index = gtk_combo_box_get_active (GTK_COMBO_BOX (vis_combo));
1104   if (vis_entries->len > 0) {
1105     entry = &g_array_index (vis_entries, VisEntry, index);
1106
1107     /* create an instance of the element from the factory */
1108     element = gst_element_factory_create (entry->factory, NULL);
1109     if (!element)
1110       return;
1111
1112     /* set vis plugin for playbin */
1113     g_object_set (pipeline, "vis-plugin", element, NULL);
1114   }
1115 }
1116
1117 static void
1118 volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1119 {
1120   gdouble volume;
1121
1122   volume = gtk_spin_button_get_value (button);
1123
1124   g_object_set (pipeline, "volume", volume, NULL);
1125 }
1126
1127 static void
1128 volume_notify_cb (GstElement * pipeline, GParamSpec * arg, gpointer user_dat)
1129 {
1130   gdouble cur_volume, new_volume;
1131
1132   if (volume_spinbutton == NULL)
1133     return;
1134
1135   g_object_get (pipeline, "volume", &new_volume, NULL);
1136   cur_volume = gtk_spin_button_get_value (GTK_SPIN_BUTTON (volume_spinbutton));
1137   if (fabs (cur_volume - new_volume) > 0.001) {
1138     g_signal_handlers_block_by_func (volume_spinbutton,
1139         volume_spinbutton_changed_cb, pipeline);
1140     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), new_volume);
1141     g_signal_handlers_unblock_by_func (volume_spinbutton,
1142         volume_spinbutton_changed_cb, pipeline);
1143   }
1144 }
1145
1146 static void
1147 shot_cb (GtkButton * button, gpointer data)
1148 {
1149   GstSample *sample = NULL;
1150   GstCaps *caps;
1151
1152   GST_DEBUG ("taking snapshot");
1153
1154   /* convert to our desired format (RGB24) */
1155   caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "RGB",
1156       /* Note: we don't ask for a specific width/height here, so that
1157        * videoscale can adjust dimensions from a non-1/1 pixel aspect
1158        * ratio to a 1/1 pixel-aspect-ratio */
1159       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
1160
1161   /* convert the latest sample to the requested format */
1162   g_signal_emit_by_name (pipeline, "convert-sample", caps, &sample);
1163   gst_caps_unref (caps);
1164
1165   if (sample) {
1166     GstBuffer *buffer;
1167     GstCaps *caps;
1168     GstStructure *s;
1169     gboolean res;
1170     gint width, height;
1171     GdkPixbuf *pixbuf;
1172     GError *error = NULL;
1173     GstMapInfo map;
1174
1175     /* get the snapshot buffer format now. We set the caps on the appsink so
1176      * that it can only be an rgb buffer. The only thing we have not specified
1177      * on the caps is the height, which is dependant on the pixel-aspect-ratio
1178      * of the source material */
1179     caps = gst_sample_get_caps (sample);
1180     if (!caps) {
1181       g_warning ("could not get snapshot format\n");
1182       goto done;
1183     }
1184     s = gst_caps_get_structure (caps, 0);
1185
1186     /* we need to get the final caps on the buffer to get the size */
1187     res = gst_structure_get_int (s, "width", &width);
1188     res |= gst_structure_get_int (s, "height", &height);
1189     if (!res) {
1190       g_warning ("could not get snapshot dimension\n");
1191       goto done;
1192     }
1193
1194     /* create pixmap from buffer and save, gstreamer video buffers have a stride
1195      * that is rounded up to the nearest multiple of 4 */
1196     buffer = gst_sample_get_buffer (sample);
1197     gst_buffer_map (buffer, &map, GST_MAP_READ);
1198     pixbuf = gdk_pixbuf_new_from_data (map.data,
1199         GDK_COLORSPACE_RGB, FALSE, 8, width, height,
1200         GST_ROUND_UP_4 (width * 3), NULL, NULL);
1201
1202     /* save the pixbuf */
1203     gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
1204     gst_buffer_unmap (buffer, &map);
1205
1206   done:
1207     gst_sample_unref (sample);
1208   }
1209 }
1210
1211 /* called when the Step button is pressed */
1212 static void
1213 step_cb (GtkButton * button, gpointer data)
1214 {
1215   GstEvent *event;
1216   GstFormat format;
1217   guint64 amount;
1218   gdouble rate;
1219   gboolean flush, res;
1220   gint active;
1221
1222   active = gtk_combo_box_get_active (GTK_COMBO_BOX (format_combo));
1223   amount =
1224       gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON
1225       (step_amount_spinbutton));
1226   rate = gtk_spin_button_get_value (GTK_SPIN_BUTTON (step_rate_spinbutton));
1227   flush = TRUE;
1228
1229   switch (active) {
1230     case 0:
1231       format = GST_FORMAT_BUFFERS;
1232       break;
1233     case 1:
1234       format = GST_FORMAT_TIME;
1235       amount *= GST_MSECOND;
1236       break;
1237     default:
1238       format = GST_FORMAT_UNDEFINED;
1239       break;
1240   }
1241
1242   event = gst_event_new_step (format, amount, rate, flush, FALSE);
1243
1244   res = send_event (event);
1245
1246   if (!res) {
1247     g_print ("Sending step event failed\n");
1248   }
1249 }
1250
1251 static void
1252 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1253 {
1254   const GstStructure *s;
1255
1256   switch (GST_MESSAGE_TYPE (message)) {
1257     case GST_MESSAGE_ERROR:
1258       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
1259           GST_DEBUG_GRAPH_SHOW_ALL, "seek.error");
1260       break;
1261     case GST_MESSAGE_WARNING:
1262       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
1263           GST_DEBUG_GRAPH_SHOW_ALL, "seek.warning");
1264       break;
1265     default:
1266       break;
1267   }
1268
1269   s = gst_message_get_structure (message);
1270   g_print ("message from \"%s\" (%s): ",
1271       GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
1272       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
1273   if (s) {
1274     gchar *sstr;
1275
1276     sstr = gst_structure_to_string (s);
1277     g_print ("%s\n", sstr);
1278     g_free (sstr);
1279   } else {
1280     g_print ("no message details\n");
1281   }
1282 }
1283
1284 static gboolean shuttling = FALSE;
1285 static gdouble shuttle_rate = 0.0;
1286 static gdouble play_rate = 1.0;
1287
1288 static void
1289 do_shuttle (GstElement * element)
1290 {
1291   guint64 duration;
1292
1293   if (shuttling)
1294     duration = 40 * GST_MSECOND;
1295   else
1296     duration = -1;
1297
1298   gst_element_send_event (element,
1299       gst_event_new_step (GST_FORMAT_TIME, duration, shuttle_rate, FALSE,
1300           FALSE));
1301 }
1302
1303 static void
1304 msg_sync_step_done (GstBus * bus, GstMessage * message, GstElement * element)
1305 {
1306   GstFormat format;
1307   guint64 amount;
1308   gdouble rate;
1309   gboolean flush;
1310   gboolean intermediate;
1311   guint64 duration;
1312   gboolean eos;
1313
1314   gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
1315       &intermediate, &duration, &eos);
1316
1317   if (eos) {
1318     g_print ("stepped till EOS\n");
1319     return;
1320   }
1321
1322   if (g_mutex_trylock (&state_mutex)) {
1323     if (shuttling)
1324       do_shuttle (element);
1325     g_mutex_unlock (&state_mutex);
1326   } else {
1327     /* ignore step messages that come while we are doing a state change */
1328     g_print ("state change is busy\n");
1329   }
1330 }
1331
1332 static void
1333 shuttle_toggled (GtkToggleButton * button, GstElement * element)
1334 {
1335   gboolean active;
1336
1337   active = gtk_toggle_button_get_active (button);
1338
1339   if (active != shuttling) {
1340     shuttling = active;
1341     g_print ("shuttling %s\n", shuttling ? "active" : "inactive");
1342     if (active) {
1343       shuttle_rate = 0.0;
1344       play_rate = 1.0;
1345       pause_cb (NULL, NULL);
1346       gst_element_get_state (element, NULL, NULL, -1);
1347     }
1348   }
1349 }
1350
1351 static void
1352 shuttle_rate_switch (GstElement * element)
1353 {
1354   GstSeekFlags flags;
1355   GstEvent *s_event;
1356   gboolean res;
1357
1358   if (state == GST_STATE_PLAYING) {
1359     /* pause when we need to */
1360     pause_cb (NULL, NULL);
1361     gst_element_get_state (element, NULL, NULL, -1);
1362   }
1363
1364   if (play_rate == 1.0)
1365     play_rate = -1.0;
1366   else
1367     play_rate = 1.0;
1368
1369   g_print ("rate changed to %lf %" GST_TIME_FORMAT "\n", play_rate,
1370       GST_TIME_ARGS (position));
1371
1372   flags = GST_SEEK_FLAG_FLUSH;
1373   flags |= GST_SEEK_FLAG_ACCURATE;
1374
1375   if (play_rate >= 0.0) {
1376     s_event = gst_event_new_seek (play_rate,
1377         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1378         GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1379   } else {
1380     s_event = gst_event_new_seek (play_rate,
1381         GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1382         GST_SEEK_TYPE_SET, position);
1383   }
1384   res = send_event (s_event);
1385   if (res) {
1386     gst_element_get_state (element, NULL, NULL, SEEK_TIMEOUT);
1387   } else {
1388     g_print ("seek failed\n");
1389   }
1390 }
1391
1392 static void
1393 shuttle_value_changed (GtkRange * range, GstElement * element)
1394 {
1395   gdouble rate;
1396
1397   rate = gtk_adjustment_get_value (shuttle_adjustment);
1398
1399   if (rate == 0.0) {
1400     g_print ("rate 0.0, pause\n");
1401     pause_cb (NULL, NULL);
1402     gst_element_get_state (element, NULL, NULL, -1);
1403   } else {
1404     g_print ("rate changed %0.3g\n", rate);
1405
1406     if ((rate < 0.0 && play_rate > 0.0) || (rate > 0.0 && play_rate < 0.0)) {
1407       shuttle_rate_switch (element);
1408     }
1409
1410     shuttle_rate = ABS (rate);
1411     if (state != GST_STATE_PLAYING) {
1412       do_shuttle (element);
1413       play_cb (NULL, NULL);
1414     }
1415   }
1416 }
1417
1418 static void
1419 msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1420 {
1421   GST_DEBUG ("async done");
1422   /* when we get ASYNC_DONE we can query position, duration and other
1423    * properties */
1424   update_scale (pipeline);
1425
1426   /* update the available streams */
1427   update_streams (pipeline);
1428 }
1429
1430 static void
1431 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1432 {
1433   const GstStructure *s;
1434
1435   s = gst_message_get_structure (message);
1436
1437   /* We only care about state changed on the pipeline */
1438   if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
1439     GstState old, new, pending;
1440
1441     gst_message_parse_state_changed (message, &old, &new, &pending);
1442
1443     /* When state of the pipeline changes to paused or playing we start updating scale */
1444     if (new == GST_STATE_PLAYING) {
1445       set_update_scale (TRUE);
1446     } else {
1447       set_update_scale (FALSE);
1448     }
1449
1450     /* dump graph for (some) pipeline state changes */
1451     {
1452       gchar *dump_name;
1453
1454       dump_name = g_strdup_printf ("seek.%s_%s",
1455           gst_element_state_get_name (old), gst_element_state_get_name (new));
1456
1457       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
1458           GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
1459
1460       g_free (dump_name);
1461     }
1462   }
1463 }
1464
1465 static void
1466 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
1467 {
1468   GstEvent *s_event;
1469   GstSeekFlags flags;
1470   gboolean res;
1471   GstFormat format;
1472
1473   GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1474   gst_message_parse_segment_done (message, &format, &position);
1475   GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
1476
1477   flags = 0;
1478   /* in the segment-done callback we never flush as this would not make sense
1479    * for seamless playback. */
1480   if (loop_seek)
1481     flags |= GST_SEEK_FLAG_SEGMENT;
1482   if (skip_seek)
1483     flags |= GST_SEEK_FLAG_SKIP;
1484
1485   s_event = gst_event_new_seek (rate,
1486       GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1487       GST_SEEK_TYPE_SET, duration);
1488
1489   GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
1490       rate, GST_TIME_ARGS (duration));
1491
1492   res = send_event (s_event);
1493   if (!res)
1494     g_print ("segment seek failed\n");
1495 }
1496
1497 /* in stream buffering mode we PAUSE the pipeline until we receive a 100%
1498  * message */
1499 static void
1500 do_stream_buffering (gint percent)
1501 {
1502   gchar *bufstr;
1503
1504   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1505   bufstr = g_strdup_printf ("Buffering...%d", percent);
1506   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
1507   g_free (bufstr);
1508
1509   if (percent == 100) {
1510     /* a 100% message means buffering is done */
1511     buffering = FALSE;
1512     /* if the desired state is playing, go back */
1513     if (state == GST_STATE_PLAYING) {
1514       /* no state management needed for live pipelines */
1515       if (!is_live) {
1516         fprintf (stderr, "Done buffering, setting pipeline to PLAYING ...\n");
1517         gst_element_set_state (pipeline, GST_STATE_PLAYING);
1518       }
1519       gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1520       gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
1521     }
1522   } else {
1523     /* buffering busy */
1524     if (buffering == FALSE && state == GST_STATE_PLAYING) {
1525       /* we were not buffering but PLAYING, PAUSE  the pipeline. */
1526       if (!is_live) {
1527         fprintf (stderr, "Buffering, setting pipeline to PAUSED ...\n");
1528         gst_element_set_state (pipeline, GST_STATE_PAUSED);
1529       }
1530     }
1531     buffering = TRUE;
1532   }
1533 }
1534
1535 static void
1536 do_download_buffering (gint percent)
1537 {
1538   if (!buffering && percent < 100) {
1539     gchar *bufstr;
1540
1541     buffering = TRUE;
1542
1543     bufstr = g_strdup_printf ("Downloading...");
1544     gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
1545     g_free (bufstr);
1546
1547     /* once we get a buffering message, we'll do the fill update */
1548     set_update_fill (TRUE);
1549
1550     if (state == GST_STATE_PLAYING && !is_live) {
1551       fprintf (stderr, "Downloading, setting pipeline to PAUSED ...\n");
1552       gst_element_set_state (pipeline, GST_STATE_PAUSED);
1553       /* user has to manually start the playback */
1554       state = GST_STATE_PAUSED;
1555     }
1556   }
1557 }
1558
1559 static void
1560 msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data)
1561 {
1562   gint percent;
1563
1564   gst_message_parse_buffering (message, &percent);
1565
1566   /* get more stats */
1567   gst_message_parse_buffering_stats (message, &mode, NULL, NULL,
1568       &buffering_left);
1569
1570   switch (mode) {
1571     case GST_BUFFERING_DOWNLOAD:
1572       do_download_buffering (percent);
1573       break;
1574     case GST_BUFFERING_LIVE:
1575       is_live = TRUE;
1576     case GST_BUFFERING_TIMESHIFT:
1577     case GST_BUFFERING_STREAM:
1578       do_stream_buffering (percent);
1579       break;
1580   }
1581 }
1582
1583 static void
1584 msg_clock_lost (GstBus * bus, GstMessage * message, GstPipeline * data)
1585 {
1586   g_print ("clock lost! PAUSE and PLAY to select a new clock\n");
1587   if (state == GST_STATE_PLAYING) {
1588     gst_element_set_state (pipeline, GST_STATE_PAUSED);
1589     gst_element_set_state (pipeline, GST_STATE_PLAYING);
1590   }
1591 }
1592
1593 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32)
1594
1595 static gulong embed_xid = 0;
1596
1597 /* We set the xid here in response to the prepare-window-handle message via a
1598  * bus sync handler because we don't know the actual videosink used from the
1599  * start (as we don't know the pipeline, or bin elements such as autovideosink
1600  * or gconfvideosink may be used which create the actual videosink only once
1601  * the pipeline is started) */
1602 static GstBusSyncReply
1603 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
1604 {
1605   GstElement *element;
1606
1607   if (!gst_is_video_overlay_prepare_window_handle_message (message))
1608     return GST_BUS_PASS;
1609
1610   element = GST_ELEMENT (GST_MESSAGE_SRC (message));
1611
1612   g_print ("got prepare-window-handle, setting XID %lu\n", embed_xid);
1613
1614   if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
1615           "force-aspect-ratio")) {
1616     g_object_set (element, "force-aspect-ratio", TRUE, NULL);
1617   }
1618
1619   /* Should have been initialised from main thread before (can't use
1620    * GDK_WINDOW_XID here with Gtk+ >= 2.18, because the sync handler will
1621    * be called from a streaming thread and GDK_WINDOW_XID maps to more than
1622    * a simple structure lookup with Gtk+ >= 2.18, where 'more' is stuff that
1623    * shouldn't be done from a non-GUI thread without explicit locking).  */
1624   g_assert (embed_xid != 0);
1625
1626   gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (element), embed_xid);
1627   return GST_BUS_PASS;
1628 }
1629 #endif
1630
1631 static gboolean
1632 draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data)
1633 {
1634   if (state < GST_STATE_PAUSED) {
1635     int width, height;
1636
1637     width = gtk_widget_get_allocated_width (widget);
1638     height = gtk_widget_get_allocated_height (widget);
1639     cairo_set_source_rgb (cr, 0, 0, 0);
1640     cairo_rectangle (cr, 0, 0, width, height);
1641     cairo_fill (cr);
1642     return TRUE;
1643   }
1644   return FALSE;
1645 }
1646
1647 static void
1648 realize_cb (GtkWidget * widget, gpointer data)
1649 {
1650   GdkWindow *window = gtk_widget_get_window (widget);
1651
1652   /* This is here just for pedagogical purposes, GDK_WINDOW_XID will call it
1653    * as well */
1654   if (!gdk_window_ensure_native (window))
1655     g_error ("Couldn't create native window needed for GstXOverlay!");
1656
1657 #if defined (GDK_WINDOWING_WIN32)
1658   embed_xid = GDK_WINDOW_HWND (window);
1659   g_print ("Window realize: video window HWND = %lu\n", embed_xid);
1660 #else
1661   embed_xid = GDK_WINDOW_XID (window);
1662   g_print ("Window realize: video window XID = %lu\n", embed_xid);
1663 #endif
1664 }
1665
1666 static void
1667 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * data)
1668 {
1669   message_received (bus, message, data);
1670
1671   /* Set new uri for playerbins and continue playback */
1672   if (l && (pipeline_type == 14 || pipeline_type == 16)) {
1673     stop_cb (NULL, NULL);
1674     l = g_list_next (l);
1675     if (l) {
1676       playerbin_set_uri (GST_ELEMENT (data), l->data);
1677       play_cb (NULL, NULL);
1678     }
1679   }
1680 }
1681
1682 static void
1683 msg_step_done (GstBus * bus, GstMessage * message, GstPipeline * data)
1684 {
1685   if (!shuttling)
1686     message_received (bus, message, data);
1687 }
1688
1689 static void
1690 connect_bus_signals (GstElement * pipeline)
1691 {
1692   GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
1693
1694 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32)
1695   /* handle prepare-window-handle element message synchronously */
1696   gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler,
1697       pipeline);
1698 #endif
1699
1700   gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
1701   gst_bus_enable_sync_message_emission (bus);
1702
1703   g_signal_connect (bus, "message::state-changed",
1704       (GCallback) msg_state_changed, pipeline);
1705   g_signal_connect (bus, "message::segment-done", (GCallback) msg_segment_done,
1706       pipeline);
1707   g_signal_connect (bus, "message::async-done", (GCallback) msg_async_done,
1708       pipeline);
1709
1710   g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
1711       pipeline);
1712   g_signal_connect (bus, "message::clock-lost", (GCallback) msg_clock_lost,
1713       pipeline);
1714   g_signal_connect (bus, "message::error", (GCallback) message_received,
1715       pipeline);
1716   g_signal_connect (bus, "message::warning", (GCallback) message_received,
1717       pipeline);
1718   g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
1719   g_signal_connect (bus, "message::tag", (GCallback) message_received,
1720       pipeline);
1721   g_signal_connect (bus, "message::element", (GCallback) message_received,
1722       pipeline);
1723   g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
1724       pipeline);
1725   g_signal_connect (bus, "message::buffering", (GCallback) msg_buffering,
1726       pipeline);
1727 //  g_signal_connect (bus, "message::step-done", (GCallback) msg_step_done,
1728 //      pipeline);
1729   g_signal_connect (bus, "message::step-start", (GCallback) msg_step_done,
1730       pipeline);
1731   g_signal_connect (bus, "sync-message::step-done",
1732       (GCallback) msg_sync_step_done, pipeline);
1733
1734   gst_object_unref (bus);
1735 }
1736
1737 /* Return GList of paths described in location string */
1738 static GList *
1739 handle_wildcards (const gchar * location)
1740 {
1741   GList *res = NULL;
1742   gchar *path = g_path_get_dirname (location);
1743   gchar *pattern = g_path_get_basename (location);
1744   GPatternSpec *pspec = g_pattern_spec_new (pattern);
1745   GDir *dir = g_dir_open (path, 0, NULL);
1746   const gchar *name;
1747
1748   g_print ("matching %s from %s\n", pattern, path);
1749
1750   if (!dir) {
1751     g_print ("opening directory %s failed\n", path);
1752     goto out;
1753   }
1754
1755   while ((name = g_dir_read_name (dir)) != NULL) {
1756     if (g_pattern_match_string (pspec, name)) {
1757       res = g_list_append (res, g_strjoin ("/", path, name, NULL));
1758       g_print ("  found clip %s\n", name);
1759     }
1760   }
1761
1762   g_dir_close (dir);
1763 out:
1764   g_pattern_spec_free (pspec);
1765   g_free (pattern);
1766   g_free (path);
1767
1768   return res;
1769 }
1770
1771 static void
1772 delete_event_cb (void)
1773 {
1774   stop_cb (NULL, NULL);
1775   gtk_main_quit ();
1776 }
1777
1778 static void
1779 print_usage (int argc, char **argv)
1780 {
1781   gint i;
1782
1783   g_print ("usage: %s <type> <filename>\n", argv[0]);
1784   g_print ("   possible types:\n");
1785
1786   for (i = 0; i < NUM_TYPES; i++) {
1787     g_print ("     %d = %s\n", i, pipelines[i].name);
1788   }
1789 }
1790
1791 int
1792 main (int argc, char **argv)
1793 {
1794   GtkWidget *window, *hbox, *vbox, *panel, *expander, *pb2vbox, *boxes,
1795       *flagtable, *boxes2, *step;
1796   GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
1797   GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
1798   GtkWidget *scrub_checkbox, *play_scrub_checkbox;
1799   GtkWidget *rate_label, *volume_label;
1800   GOptionEntry options[] = {
1801     {"audiosink", '\0', 0, G_OPTION_ARG_STRING, &opt_audiosink_str,
1802         "audio sink to use (default: " DEFAULT_AUDIOSINK ")", NULL},
1803     {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
1804         "Show pad stats", NULL},
1805     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
1806         "Verbose properties", NULL},
1807     {"videosink", '\0', 0, G_OPTION_ARG_STRING, &opt_videosink_str,
1808         "video sink to use (default: " DEFAULT_VIDEOSINK ")", NULL},
1809     {NULL}
1810   };
1811   GOptionContext *ctx;
1812   GError *err = NULL;
1813
1814   ctx = g_option_context_new ("- test seeking in gsteamer");
1815   g_option_context_add_main_entries (ctx, options, NULL);
1816   g_option_context_add_group (ctx, gst_init_get_option_group ());
1817   g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
1818
1819   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1820     g_print ("Error initializing: %s\n", err->message);
1821     exit (1);
1822   }
1823
1824   if (opt_audiosink_str == NULL)
1825     opt_audiosink_str = g_strdup (DEFAULT_AUDIOSINK);
1826
1827   if (opt_videosink_str == NULL)
1828     opt_videosink_str = g_strdup (DEFAULT_VIDEOSINK);
1829
1830   GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
1831
1832   if (argc != 3) {
1833     print_usage (argc, argv);
1834     exit (-1);
1835   }
1836
1837   pipeline_type = atoi (argv[1]);
1838
1839   if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
1840     print_usage (argc, argv);
1841     exit (-1);
1842   }
1843
1844   pipeline_spec = argv[2];
1845
1846   if (g_path_is_absolute (pipeline_spec) &&
1847       (g_strrstr (pipeline_spec, "*") != NULL ||
1848           g_strrstr (pipeline_spec, "?") != NULL)) {
1849     paths = handle_wildcards (pipeline_spec);
1850   } else {
1851     paths = g_list_prepend (paths, g_strdup (pipeline_spec));
1852   }
1853
1854   if (!paths) {
1855     g_print ("opening %s failed\n", pipeline_spec);
1856     exit (-1);
1857   }
1858
1859   l = paths;
1860
1861   pipeline = pipelines[pipeline_type].func ((gchar *) l->data);
1862   g_assert (pipeline);
1863
1864   /* initialize gui elements ... */
1865   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1866   video_window = gtk_drawing_area_new ();
1867   g_signal_connect (video_window, "draw", G_CALLBACK (draw_cb), NULL);
1868   g_signal_connect (video_window, "realize", G_CALLBACK (realize_cb), NULL);
1869   gtk_widget_set_double_buffered (video_window, FALSE);
1870
1871   statusbar = gtk_statusbar_new ();
1872   status_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "seek");
1873   gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
1874   hbox = gtk_hbox_new (FALSE, 0);
1875   vbox = gtk_vbox_new (FALSE, 0);
1876   flagtable = gtk_table_new (4, 2, FALSE);
1877   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
1878
1879   /* media controls */
1880   play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
1881   pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
1882   stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
1883
1884   /* seek flags */
1885   accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
1886   key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
1887   loop_checkbox = gtk_check_button_new_with_label ("Loop");
1888   flush_checkbox = gtk_check_button_new_with_label ("Flush");
1889   scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
1890   play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
1891   skip_checkbox = gtk_check_button_new_with_label ("Play Skip");
1892   rate_spinbutton = gtk_spin_button_new_with_range (-100, 100, 0.1);
1893   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (rate_spinbutton), 3);
1894   rate_label = gtk_label_new ("Rate");
1895
1896   gtk_widget_set_tooltip_text (accurate_checkbox,
1897       "accurate position is requested, this might be considerably slower for some formats");
1898   gtk_widget_set_tooltip_text (key_checkbox,
1899       "seek to the nearest keyframe. This might be faster but less accurate");
1900   gtk_widget_set_tooltip_text (loop_checkbox, "loop playback");
1901   gtk_widget_set_tooltip_text (flush_checkbox, "flush pipeline after seeking");
1902   gtk_widget_set_tooltip_text (rate_spinbutton, "define the playback rate, "
1903       "negative value trigger reverse playback");
1904   gtk_widget_set_tooltip_text (scrub_checkbox, "show images while seeking");
1905   gtk_widget_set_tooltip_text (play_scrub_checkbox, "play video while seeking");
1906   gtk_widget_set_tooltip_text (skip_checkbox,
1907       "Skip frames while playing at high frame rates");
1908
1909   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
1910   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
1911
1912   gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
1913
1914   /* step expander */
1915   {
1916     GtkWidget *hbox;
1917
1918     step = gtk_expander_new ("step options");
1919     hbox = gtk_hbox_new (FALSE, 0);
1920
1921     format_combo = gtk_combo_box_text_new ();
1922     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
1923         "frames");
1924     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
1925         "time (ms)");
1926     gtk_combo_box_set_active (GTK_COMBO_BOX (format_combo), 0);
1927     gtk_box_pack_start (GTK_BOX (hbox), format_combo, FALSE, FALSE, 2);
1928
1929     step_amount_spinbutton = gtk_spin_button_new_with_range (1, 1000, 1);
1930     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_amount_spinbutton), 0);
1931     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_amount_spinbutton), 1.0);
1932     gtk_box_pack_start (GTK_BOX (hbox), step_amount_spinbutton, FALSE, FALSE,
1933         2);
1934
1935     step_rate_spinbutton = gtk_spin_button_new_with_range (0.0, 100, 0.1);
1936     gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_rate_spinbutton), 3);
1937     gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_rate_spinbutton), 1.0);
1938     gtk_box_pack_start (GTK_BOX (hbox), step_rate_spinbutton, FALSE, FALSE, 2);
1939
1940     step_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_FORWARD);
1941     gtk_button_set_label (GTK_BUTTON (step_button), "Step");
1942     gtk_box_pack_start (GTK_BOX (hbox), step_button, FALSE, FALSE, 2);
1943
1944     g_signal_connect (G_OBJECT (step_button), "clicked", G_CALLBACK (step_cb),
1945         pipeline);
1946
1947     /* shuttle scale */
1948     shuttle_checkbox = gtk_check_button_new_with_label ("Shuttle");
1949     gtk_box_pack_start (GTK_BOX (hbox), shuttle_checkbox, FALSE, FALSE, 2);
1950     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shuttle_checkbox), FALSE);
1951     g_signal_connect (shuttle_checkbox, "toggled", G_CALLBACK (shuttle_toggled),
1952         pipeline);
1953
1954     shuttle_adjustment =
1955         GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -3.00, 4.0, 0.1, 1.0, 1.0));
1956     shuttle_hscale = gtk_hscale_new (shuttle_adjustment);
1957     gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2);
1958     gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP);
1959     g_signal_connect (shuttle_hscale, "value_changed",
1960         G_CALLBACK (shuttle_value_changed), pipeline);
1961     g_signal_connect (shuttle_hscale, "format_value",
1962         G_CALLBACK (shuttle_format_value), pipeline);
1963
1964     gtk_box_pack_start (GTK_BOX (hbox), shuttle_hscale, TRUE, TRUE, 2);
1965
1966     gtk_container_add (GTK_CONTAINER (step), hbox);
1967   }
1968
1969   /* seek bar */
1970   adjustment =
1971       GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, N_GRAD, 0.1, 1.0, 1.0));
1972   hscale = gtk_hscale_new (adjustment);
1973   gtk_scale_set_digits (GTK_SCALE (hscale), 2);
1974   gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
1975   gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
1976   gtk_range_set_fill_level (GTK_RANGE (hscale), N_GRAD);
1977
1978   g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek),
1979       pipeline);
1980   g_signal_connect (hscale, "button_release_event", G_CALLBACK (stop_seek),
1981       pipeline);
1982   g_signal_connect (hscale, "format_value", G_CALLBACK (format_value),
1983       pipeline);
1984
1985   if (pipeline_type == 0) {
1986     /* the playbin panel controls for the video/audio/subtitle tracks */
1987     panel = gtk_hbox_new (FALSE, 0);
1988     video_combo = gtk_combo_box_text_new ();
1989     audio_combo = gtk_combo_box_text_new ();
1990     text_combo = gtk_combo_box_text_new ();
1991     gtk_widget_set_sensitive (video_combo, FALSE);
1992     gtk_widget_set_sensitive (audio_combo, FALSE);
1993     gtk_widget_set_sensitive (text_combo, FALSE);
1994     gtk_box_pack_start (GTK_BOX (panel), video_combo, TRUE, TRUE, 2);
1995     gtk_box_pack_start (GTK_BOX (panel), audio_combo, TRUE, TRUE, 2);
1996     gtk_box_pack_start (GTK_BOX (panel), text_combo, TRUE, TRUE, 2);
1997     g_signal_connect (G_OBJECT (video_combo), "changed",
1998         G_CALLBACK (video_combo_cb), pipeline);
1999     g_signal_connect (G_OBJECT (audio_combo), "changed",
2000         G_CALLBACK (audio_combo_cb), pipeline);
2001     g_signal_connect (G_OBJECT (text_combo), "changed",
2002         G_CALLBACK (text_combo_cb), pipeline);
2003     /* playbin panel for flag checkboxes and volume/mute */
2004     boxes = gtk_hbox_new (FALSE, 0);
2005     vis_checkbox = gtk_check_button_new_with_label ("Vis");
2006     video_checkbox = gtk_check_button_new_with_label ("Video");
2007     audio_checkbox = gtk_check_button_new_with_label ("Audio");
2008     text_checkbox = gtk_check_button_new_with_label ("Text");
2009     mute_checkbox = gtk_check_button_new_with_label ("Mute");
2010     download_checkbox = gtk_check_button_new_with_label ("Download");
2011     buffer_checkbox = gtk_check_button_new_with_label ("Buffer");
2012     volume_label = gtk_label_new ("Volume");
2013     volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
2014     gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
2015     gtk_box_pack_start (GTK_BOX (boxes), video_checkbox, TRUE, TRUE, 2);
2016     gtk_box_pack_start (GTK_BOX (boxes), audio_checkbox, TRUE, TRUE, 2);
2017     gtk_box_pack_start (GTK_BOX (boxes), text_checkbox, TRUE, TRUE, 2);
2018     gtk_box_pack_start (GTK_BOX (boxes), vis_checkbox, TRUE, TRUE, 2);
2019     gtk_box_pack_start (GTK_BOX (boxes), mute_checkbox, TRUE, TRUE, 2);
2020     gtk_box_pack_start (GTK_BOX (boxes), download_checkbox, TRUE, TRUE, 2);
2021     gtk_box_pack_start (GTK_BOX (boxes), buffer_checkbox, TRUE, TRUE, 2);
2022     gtk_box_pack_start (GTK_BOX (boxes), volume_label, TRUE, TRUE, 2);
2023     gtk_box_pack_start (GTK_BOX (boxes), volume_spinbutton, TRUE, TRUE, 2);
2024     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vis_checkbox), FALSE);
2025     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audio_checkbox), TRUE);
2026     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (video_checkbox), TRUE);
2027     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_checkbox), TRUE);
2028     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mute_checkbox), FALSE);
2029     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (download_checkbox), FALSE);
2030     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (buffer_checkbox), FALSE);
2031     g_signal_connect (G_OBJECT (vis_checkbox), "toggled",
2032         G_CALLBACK (vis_toggle_cb), pipeline);
2033     g_signal_connect (G_OBJECT (audio_checkbox), "toggled",
2034         G_CALLBACK (audio_toggle_cb), pipeline);
2035     g_signal_connect (G_OBJECT (video_checkbox), "toggled",
2036         G_CALLBACK (video_toggle_cb), pipeline);
2037     g_signal_connect (G_OBJECT (text_checkbox), "toggled",
2038         G_CALLBACK (text_toggle_cb), pipeline);
2039     g_signal_connect (G_OBJECT (mute_checkbox), "toggled",
2040         G_CALLBACK (mute_toggle_cb), pipeline);
2041     g_signal_connect (G_OBJECT (download_checkbox), "toggled",
2042         G_CALLBACK (download_toggle_cb), pipeline);
2043     g_signal_connect (G_OBJECT (buffer_checkbox), "toggled",
2044         G_CALLBACK (buffer_toggle_cb), pipeline);
2045     g_signal_connect (G_OBJECT (volume_spinbutton), "value_changed",
2046         G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
2047     /* playbin panel for snapshot */
2048     boxes2 = gtk_hbox_new (FALSE, 0);
2049     shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
2050     gtk_widget_set_tooltip_text (shot_button,
2051         "save a screenshot .png in the current directory");
2052     g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
2053         pipeline);
2054     vis_combo = gtk_combo_box_text_new ();
2055     g_signal_connect (G_OBJECT (vis_combo), "changed",
2056         G_CALLBACK (vis_combo_cb), pipeline);
2057     gtk_widget_set_sensitive (vis_combo, FALSE);
2058     gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
2059     gtk_box_pack_start (GTK_BOX (boxes2), vis_combo, TRUE, TRUE, 2);
2060
2061     /* fill the vis combo box and the array of factories */
2062     init_visualization_features ();
2063   } else {
2064     panel = boxes = boxes2 = NULL;
2065   }
2066
2067   /* do the packing stuff ... */
2068   gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
2069   /* FIXME: can we avoid this for audio only? */
2070   gtk_widget_set_size_request (GTK_WIDGET (video_window), -1,
2071       DEFAULT_VIDEO_HEIGHT);
2072   gtk_container_add (GTK_CONTAINER (window), vbox);
2073   gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 2);
2074   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
2075   gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
2076   gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
2077   gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
2078   gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
2079   gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
2080       1);
2081   gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
2082   gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
2083   gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
2084   gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
2085   gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
2086       1, 2);
2087   gtk_table_attach_defaults (GTK_TABLE (flagtable), skip_checkbox, 3, 4, 0, 1);
2088   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 4, 5, 0, 1);
2089   gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 4, 5, 1,
2090       2);
2091   if (panel && boxes && boxes2) {
2092     expander = gtk_expander_new ("playbin options");
2093     pb2vbox = gtk_vbox_new (FALSE, 0);
2094     gtk_box_pack_start (GTK_BOX (pb2vbox), panel, FALSE, FALSE, 2);
2095     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes, FALSE, FALSE, 2);
2096     gtk_box_pack_start (GTK_BOX (pb2vbox), boxes2, FALSE, FALSE, 2);
2097     gtk_container_add (GTK_CONTAINER (expander), pb2vbox);
2098     gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 2);
2099   }
2100   gtk_box_pack_start (GTK_BOX (vbox), step, FALSE, FALSE, 2);
2101   gtk_box_pack_start (GTK_BOX (vbox), hscale, FALSE, FALSE, 2);
2102   gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 2);
2103
2104   /* connect things ... */
2105   g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
2106       pipeline);
2107   g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
2108       pipeline);
2109   g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
2110       pipeline);
2111   g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
2112       G_CALLBACK (accurate_toggle_cb), pipeline);
2113   g_signal_connect (G_OBJECT (key_checkbox), "toggled",
2114       G_CALLBACK (key_toggle_cb), pipeline);
2115   g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
2116       G_CALLBACK (loop_toggle_cb), pipeline);
2117   g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
2118       G_CALLBACK (flush_toggle_cb), pipeline);
2119   g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
2120       G_CALLBACK (scrub_toggle_cb), pipeline);
2121   g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
2122       G_CALLBACK (play_scrub_toggle_cb), pipeline);
2123   g_signal_connect (G_OBJECT (skip_checkbox), "toggled",
2124       G_CALLBACK (skip_toggle_cb), pipeline);
2125   g_signal_connect (G_OBJECT (rate_spinbutton), "value_changed",
2126       G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
2127
2128   g_signal_connect (G_OBJECT (window), "delete-event", delete_event_cb, NULL);
2129
2130   /* show the gui. */
2131   gtk_widget_show_all (window);
2132
2133   /* realize window now so that the video window gets created and we can
2134    * obtain its XID before the pipeline is started up and the videosink
2135    * asks for the XID of the window to render onto */
2136   gtk_widget_realize (window);
2137
2138 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32)
2139   /* we should have the XID now */
2140   g_assert (embed_xid != 0);
2141 #endif
2142
2143   if (verbose) {
2144     g_signal_connect (pipeline, "deep_notify",
2145         G_CALLBACK (gst_object_default_deep_notify), NULL);
2146   }
2147
2148   connect_bus_signals (pipeline);
2149   gtk_main ();
2150
2151   g_print ("NULL pipeline\n");
2152   gst_element_set_state (pipeline, GST_STATE_NULL);
2153
2154   g_print ("free pipeline\n");
2155   gst_object_unref (pipeline);
2156
2157   g_list_foreach (paths, (GFunc) g_free, NULL);
2158   g_list_free (paths);
2159
2160   return 0;
2161 }