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