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