3 * seek.c: seeking sample application
5 * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
6 * 2006 Stefan Kost <ensonic@users.sf.net>
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.
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.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
27 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
28 * with newer GTK versions (>= 3.3.0) */
29 #define GDK_DISABLE_DEPRECATION_WARNINGS
39 #if defined (GDK_WINDOWING_X11)
41 #elif defined (GDK_WINDOWING_WIN32)
42 #include <gdk/gdkwin32.h>
43 #elif defined (GDK_WINDOWING_QUARTZ)
44 #include <gdk/gdkquartzwindow.h>
47 #include <gst/interfaces/xoverlay.h>
48 #include <gst/interfaces/navigation.h>
49 #include <gst/interfaces/colorbalance.h>
51 GST_DEBUG_CATEGORY_STATIC (seek_debug);
52 #define GST_CAT_DEFAULT (seek_debug)
56 #define SOURCE "filesrc"
58 static gchar *opt_audiosink_str; /* NULL */
59 static gchar *opt_videosink_str; /* NULL */
61 #define FILL_INTERVAL 100
62 //#define UPDATE_INTERVAL 500
63 //#define UPDATE_INTERVAL 100
64 #define UPDATE_INTERVAL 40
66 /* number of milliseconds to play for after a seek */
67 #define SCRUB_TIME 100
69 /* timeout for gst_element_get_state() after a seek */
70 #define SEEK_TIMEOUT 40 * GST_MSECOND
72 #define DEFAULT_VIDEO_HEIGHT 300
74 /* the state to go to when stop is pressed */
75 #define STOP_STATE GST_STATE_READY
79 static GList *seekable_pads = NULL;
80 static GList *rate_pads = NULL;
81 static GList *seekable_elements = NULL;
83 static gboolean accurate_seek = FALSE;
84 static gboolean keyframe_seek = FALSE;
85 static gboolean loop_seek = FALSE;
86 static gboolean flush_seek = TRUE;
87 static gboolean scrub = TRUE;
88 static gboolean play_scrub = FALSE;
89 static gboolean skip_seek = FALSE;
90 static gdouble rate = 1.0;
92 static GstElement *pipeline;
93 static gint pipeline_type;
94 static const gchar *pipeline_spec;
95 static gint64 position = -1;
96 static gint64 duration = -1;
97 static GtkAdjustment *adjustment;
98 static GtkWidget *hscale, *statusbar;
99 static guint status_id = 0;
100 static gboolean stats = FALSE;
101 static gboolean elem_seek = FALSE;
102 static gboolean verbose = FALSE;
104 static gboolean is_live = FALSE;
105 static gboolean buffering = FALSE;
106 static GstBufferingMode mode;
107 static gint64 buffering_left;
108 static GstState state = GST_STATE_NULL;
109 static guint update_id = 0;
110 static guint seek_timeout_id = 0;
111 static gulong changed_id;
112 static guint fill_id = 0;
114 static gint n_video = 0, n_audio = 0, n_text = 0;
115 static gboolean need_streams = TRUE;
116 static GtkWidget *video_combo, *audio_combo, *text_combo, *vis_combo;
117 static GtkWidget *vis_checkbox, *video_checkbox, *audio_checkbox;
118 static GtkWidget *text_checkbox, *mute_checkbox, *volume_spinbutton;
119 static GtkWidget *skip_checkbox, *video_window, *download_checkbox;
120 static GtkWidget *buffer_checkbox, *rate_spinbutton;
122 static GStaticMutex state_mutex = G_STATIC_MUTEX_INIT;
124 static GtkWidget *format_combo, *step_amount_spinbutton, *step_rate_spinbutton;
125 static GtkWidget *shuttle_checkbox, *step_button;
126 static GtkWidget *shuttle_hscale;
127 static GtkAdjustment *shuttle_adjustment;
129 static GtkWidget *contrast_scale, *brightness_scale, *hue_scale,
132 static GstElement *navigation_element = NULL;
133 static GstElement *colorbalance_element = NULL;
137 GstNavigationCommand cmd;
139 } navigation_buttons[14];
141 static GList *paths = NULL, *l = NULL;
143 /* we keep an array of the visualisation entries so that we can easily switch
144 * with the combo box index. */
147 GstElementFactory *factory;
150 static GArray *vis_entries;
152 static void clear_streams (GstElement * pipeline);
153 static void volume_notify_cb (GstElement * pipeline, GParamSpec * arg,
155 static void find_interface_elements (void);
157 /* pipeline construction */
161 const gchar *padname;
168 gst_element_factory_make_or_warn (const gchar * type, const gchar * name)
170 GstElement *element = gst_element_factory_make (type, name);
172 #ifndef GST_DISABLE_PARSE
174 /* Try parsing it as a pipeline description */
175 element = gst_parse_bin_from_description (type, TRUE, NULL);
177 gst_element_set_name (element, name);
183 g_warning ("Failed to create element %s of type %s", name, type);
190 dynamic_link (GstPadTemplate * templ, GstPad * newpad, gpointer data)
193 dyn_link *connect = (dyn_link *) data;
195 padname = gst_pad_get_name (newpad);
197 if (connect->padname == NULL || !strcmp (padname, connect->padname)) {
199 gst_bin_add (GST_BIN (pipeline), connect->bin);
200 gst_pad_link (newpad, connect->target);
202 //seekable_pads = g_list_prepend (seekable_pads, newpad);
203 rate_pads = g_list_prepend (rate_pads, newpad);
209 setup_dynamic_link (GstElement * element, const gchar * padname,
210 GstPad * target, GstElement * bin)
214 connect = g_new0 (dyn_link, 1);
215 connect->padname = g_strdup (padname);
216 connect->target = target;
219 g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (dynamic_link),
224 make_mod_pipeline (const gchar * location)
226 GstElement *pipeline;
227 GstElement *src, *decoder, *audiosink;
230 pipeline = gst_pipeline_new ("app");
232 src = gst_element_factory_make_or_warn (SOURCE, "src");
233 decoder = gst_element_factory_make_or_warn ("modplug", "decoder");
234 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
235 //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
237 g_object_set (G_OBJECT (src), "location", location, NULL);
239 gst_bin_add (GST_BIN (pipeline), src);
240 gst_bin_add (GST_BIN (pipeline), decoder);
241 gst_bin_add (GST_BIN (pipeline), audiosink);
243 gst_element_link (src, decoder);
244 gst_element_link (decoder, audiosink);
246 seekable = gst_element_get_static_pad (decoder, "src");
247 seekable_pads = g_list_prepend (seekable_pads, seekable);
248 rate_pads = g_list_prepend (rate_pads, seekable);
250 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
256 make_dv_pipeline (const gchar * location)
258 GstElement *pipeline;
259 GstElement *src, *demux, *decoder, *audiosink, *videosink;
260 GstElement *a_queue, *v_queue;
263 pipeline = gst_pipeline_new ("app");
265 src = gst_element_factory_make_or_warn (SOURCE, "src");
266 demux = gst_element_factory_make_or_warn ("dvdemux", "demuxer");
267 v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
268 decoder = gst_element_factory_make_or_warn ("ffdec_dvvideo", "decoder");
269 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
270 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
271 audiosink = gst_element_factory_make_or_warn ("alsasink", "a_sink");
273 g_object_set (G_OBJECT (src), "location", location, NULL);
275 gst_bin_add (GST_BIN (pipeline), src);
276 gst_bin_add (GST_BIN (pipeline), demux);
277 gst_bin_add (GST_BIN (pipeline), a_queue);
278 gst_bin_add (GST_BIN (pipeline), audiosink);
279 gst_bin_add (GST_BIN (pipeline), v_queue);
280 gst_bin_add (GST_BIN (pipeline), decoder);
281 gst_bin_add (GST_BIN (pipeline), videosink);
283 gst_element_link (src, demux);
284 gst_element_link (a_queue, audiosink);
285 gst_element_link (v_queue, decoder);
286 gst_element_link (decoder, videosink);
288 setup_dynamic_link (demux, "video", gst_element_get_static_pad (v_queue,
290 setup_dynamic_link (demux, "audio", gst_element_get_static_pad (a_queue,
293 seekable = gst_element_get_static_pad (decoder, "src");
294 seekable_pads = g_list_prepend (seekable_pads, seekable);
295 rate_pads = g_list_prepend (rate_pads, seekable);
301 make_wav_pipeline (const gchar * location)
303 GstElement *pipeline;
304 GstElement *src, *decoder, *audiosink;
306 pipeline = gst_pipeline_new ("app");
308 src = gst_element_factory_make_or_warn (SOURCE, "src");
309 decoder = gst_element_factory_make_or_warn ("wavparse", "decoder");
310 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
312 g_object_set (G_OBJECT (src), "location", location, NULL);
314 gst_bin_add (GST_BIN (pipeline), src);
315 gst_bin_add (GST_BIN (pipeline), decoder);
316 gst_bin_add (GST_BIN (pipeline), audiosink);
318 gst_element_link (src, decoder);
320 setup_dynamic_link (decoder, "src", gst_element_get_static_pad (audiosink,
323 seekable_elements = g_list_prepend (seekable_elements, audiosink);
325 /* force element seeking on this pipeline */
332 make_flac_pipeline (const gchar * location)
334 GstElement *pipeline;
335 GstElement *src, *decoder, *audiosink;
338 pipeline = gst_pipeline_new ("app");
340 src = gst_element_factory_make_or_warn (SOURCE, "src");
341 decoder = gst_element_factory_make_or_warn ("flacdec", "decoder");
342 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
343 g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
345 g_object_set (G_OBJECT (src), "location", location, NULL);
347 gst_bin_add (GST_BIN (pipeline), src);
348 gst_bin_add (GST_BIN (pipeline), decoder);
349 gst_bin_add (GST_BIN (pipeline), audiosink);
351 gst_element_link (src, decoder);
352 gst_element_link (decoder, audiosink);
354 seekable = gst_element_get_static_pad (decoder, "src");
355 seekable_pads = g_list_prepend (seekable_pads, seekable);
356 rate_pads = g_list_prepend (rate_pads, seekable);
358 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
364 make_sid_pipeline (const gchar * location)
366 GstElement *pipeline;
367 GstElement *src, *decoder, *audiosink;
370 pipeline = gst_pipeline_new ("app");
372 src = gst_element_factory_make_or_warn (SOURCE, "src");
373 decoder = gst_element_factory_make_or_warn ("siddec", "decoder");
374 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
375 //g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
377 g_object_set (G_OBJECT (src), "location", location, NULL);
379 gst_bin_add (GST_BIN (pipeline), src);
380 gst_bin_add (GST_BIN (pipeline), decoder);
381 gst_bin_add (GST_BIN (pipeline), audiosink);
383 gst_element_link (src, decoder);
384 gst_element_link (decoder, audiosink);
386 seekable = gst_element_get_static_pad (decoder, "src");
387 seekable_pads = g_list_prepend (seekable_pads, seekable);
388 rate_pads = g_list_prepend (rate_pads, seekable);
390 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
396 make_parse_pipeline (const gchar * location)
398 GstElement *pipeline;
399 GstElement *src, *parser, *fakesink;
402 pipeline = gst_pipeline_new ("app");
404 src = gst_element_factory_make_or_warn (SOURCE, "src");
405 parser = gst_element_factory_make_or_warn ("mpegparse", "parse");
406 fakesink = gst_element_factory_make_or_warn ("fakesink", "sink");
407 g_object_set (G_OBJECT (fakesink), "silent", TRUE, NULL);
408 g_object_set (G_OBJECT (fakesink), "sync", TRUE, NULL);
410 g_object_set (G_OBJECT (src), "location", location, NULL);
412 gst_bin_add (GST_BIN (pipeline), src);
413 gst_bin_add (GST_BIN (pipeline), parser);
414 gst_bin_add (GST_BIN (pipeline), fakesink);
416 gst_element_link (src, parser);
417 gst_element_link (parser, fakesink);
419 seekable = gst_element_get_static_pad (parser, "src");
420 seekable_pads = g_list_prepend (seekable_pads, seekable);
421 rate_pads = g_list_prepend (rate_pads, seekable);
423 g_list_prepend (rate_pads, gst_element_get_static_pad (parser, "sink"));
429 make_vorbis_pipeline (const gchar * location)
431 GstElement *pipeline, *audio_bin;
432 GstElement *src, *demux, *decoder, *convert, *audiosink;
433 GstPad *pad, *seekable;
435 pipeline = gst_pipeline_new ("app");
437 src = gst_element_factory_make_or_warn (SOURCE, "src");
438 demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
439 decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder");
440 convert = gst_element_factory_make_or_warn ("audioconvert", "convert");
441 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
442 g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL);
444 g_object_set (G_OBJECT (src), "location", location, NULL);
446 audio_bin = gst_bin_new ("a_decoder_bin");
448 gst_bin_add (GST_BIN (pipeline), src);
449 gst_bin_add (GST_BIN (pipeline), demux);
450 gst_bin_add (GST_BIN (audio_bin), decoder);
451 gst_bin_add (GST_BIN (audio_bin), convert);
452 gst_bin_add (GST_BIN (audio_bin), audiosink);
453 gst_bin_add (GST_BIN (pipeline), audio_bin);
455 gst_element_link (src, demux);
456 gst_element_link (decoder, convert);
457 gst_element_link (convert, audiosink);
459 pad = gst_element_get_static_pad (decoder, "sink");
460 gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
461 gst_object_unref (pad);
463 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
466 seekable = gst_element_get_static_pad (decoder, "src");
467 seekable_pads = g_list_prepend (seekable_pads, seekable);
468 rate_pads = g_list_prepend (rate_pads, seekable);
470 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
476 make_theora_pipeline (const gchar * location)
478 GstElement *pipeline, *video_bin;
479 GstElement *src, *demux, *decoder, *convert, *videosink;
480 GstPad *pad, *seekable;
482 pipeline = gst_pipeline_new ("app");
484 src = gst_element_factory_make_or_warn (SOURCE, "src");
485 demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
486 decoder = gst_element_factory_make_or_warn ("theoradec", "decoder");
487 convert = gst_element_factory_make_or_warn ("ffmpegcolorspace", "convert");
488 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "sink");
490 g_object_set (G_OBJECT (src), "location", location, NULL);
492 video_bin = gst_bin_new ("v_decoder_bin");
494 gst_bin_add (GST_BIN (pipeline), src);
495 gst_bin_add (GST_BIN (pipeline), demux);
496 gst_bin_add (GST_BIN (video_bin), decoder);
497 gst_bin_add (GST_BIN (video_bin), convert);
498 gst_bin_add (GST_BIN (video_bin), videosink);
499 gst_bin_add (GST_BIN (pipeline), video_bin);
501 gst_element_link (src, demux);
502 gst_element_link (decoder, convert);
503 gst_element_link (convert, videosink);
505 pad = gst_element_get_static_pad (decoder, "sink");
506 gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
507 gst_object_unref (pad);
509 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
512 seekable = gst_element_get_static_pad (decoder, "src");
513 seekable_pads = g_list_prepend (seekable_pads, seekable);
514 rate_pads = g_list_prepend (rate_pads, seekable);
516 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
522 make_vorbis_theora_pipeline (const gchar * location)
524 GstElement *pipeline, *audio_bin, *video_bin;
525 GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
526 GstElement *audiosink, *videosink;
527 GstElement *a_queue, *v_queue, *v_scale;
531 pipeline = gst_pipeline_new ("app");
533 src = gst_element_factory_make_or_warn (SOURCE, "src");
534 g_object_set (G_OBJECT (src), "location", location, NULL);
536 demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
538 gst_bin_add (GST_BIN (pipeline), src);
539 gst_bin_add (GST_BIN (pipeline), demux);
540 gst_element_link (src, demux);
542 audio_bin = gst_bin_new ("a_decoder_bin");
543 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
544 a_decoder = gst_element_factory_make_or_warn ("vorbisdec", "a_dec");
545 a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
546 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
548 gst_bin_add (GST_BIN (pipeline), audio_bin);
550 gst_bin_add (GST_BIN (audio_bin), a_queue);
551 gst_bin_add (GST_BIN (audio_bin), a_decoder);
552 gst_bin_add (GST_BIN (audio_bin), a_convert);
553 gst_bin_add (GST_BIN (audio_bin), audiosink);
555 gst_element_link (a_queue, a_decoder);
556 gst_element_link (a_decoder, a_convert);
557 gst_element_link (a_convert, audiosink);
559 pad = gst_element_get_static_pad (a_queue, "sink");
560 gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
561 gst_object_unref (pad);
563 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
566 video_bin = gst_bin_new ("v_decoder_bin");
567 v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
568 v_decoder = gst_element_factory_make_or_warn ("theoradec", "v_dec");
570 gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
571 v_scale = gst_element_factory_make_or_warn ("videoscale", "v_scale");
572 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
574 gst_bin_add (GST_BIN (pipeline), video_bin);
576 gst_bin_add (GST_BIN (video_bin), v_queue);
577 gst_bin_add (GST_BIN (video_bin), v_decoder);
578 gst_bin_add (GST_BIN (video_bin), v_convert);
579 gst_bin_add (GST_BIN (video_bin), v_scale);
580 gst_bin_add (GST_BIN (video_bin), videosink);
582 gst_element_link_many (v_queue, v_decoder, v_convert, v_scale, videosink,
585 pad = gst_element_get_static_pad (v_queue, "sink");
586 gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
587 gst_object_unref (pad);
589 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
592 seekable = gst_element_get_static_pad (a_decoder, "src");
593 seekable_pads = g_list_prepend (seekable_pads, seekable);
594 rate_pads = g_list_prepend (rate_pads, seekable);
596 g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
603 make_avi_msmpeg4v3_mp3_pipeline (const gchar * location)
605 GstElement *pipeline, *audio_bin, *video_bin;
606 GstElement *src, *demux, *a_decoder, *a_convert, *v_decoder, *v_convert;
607 GstElement *audiosink, *videosink;
608 GstElement *a_queue, *v_queue;
609 GstPad *seekable, *pad;
611 pipeline = gst_pipeline_new ("app");
613 src = gst_element_factory_make_or_warn (SOURCE, "src");
614 g_object_set (G_OBJECT (src), "location", location, NULL);
616 demux = gst_element_factory_make_or_warn ("avidemux", "demux");
618 gst_bin_add (GST_BIN (pipeline), src);
619 gst_bin_add (GST_BIN (pipeline), demux);
620 gst_element_link (src, demux);
622 audio_bin = gst_bin_new ("a_decoder_bin");
623 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
624 a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
625 a_convert = gst_element_factory_make_or_warn ("audioconvert", "a_convert");
626 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
628 gst_bin_add (GST_BIN (audio_bin), a_queue);
629 gst_bin_add (GST_BIN (audio_bin), a_decoder);
630 gst_bin_add (GST_BIN (audio_bin), a_convert);
631 gst_bin_add (GST_BIN (audio_bin), audiosink);
633 gst_element_link (a_queue, a_decoder);
634 gst_element_link (a_decoder, a_convert);
635 gst_element_link (a_convert, audiosink);
637 gst_bin_add (GST_BIN (pipeline), audio_bin);
639 pad = gst_element_get_static_pad (a_queue, "sink");
640 gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
641 gst_object_unref (pad);
643 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (audio_bin,
646 video_bin = gst_bin_new ("v_decoder_bin");
647 v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
648 v_decoder = gst_element_factory_make_or_warn ("ffdec_msmpeg4", "v_dec");
650 gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_convert");
651 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
653 gst_bin_add (GST_BIN (video_bin), v_queue);
654 gst_bin_add (GST_BIN (video_bin), v_decoder);
655 gst_bin_add (GST_BIN (video_bin), v_convert);
656 gst_bin_add (GST_BIN (video_bin), videosink);
658 gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL);
660 gst_bin_add (GST_BIN (pipeline), video_bin);
662 pad = gst_element_get_static_pad (v_queue, "sink");
663 gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
664 gst_object_unref (pad);
666 setup_dynamic_link (demux, NULL, gst_element_get_static_pad (video_bin,
669 seekable = gst_element_get_static_pad (a_decoder, "src");
670 seekable_pads = g_list_prepend (seekable_pads, seekable);
671 rate_pads = g_list_prepend (rate_pads, seekable);
673 g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
680 make_mp3_pipeline (const gchar * location)
682 GstElement *pipeline;
683 GstElement *src, *parser, *decoder, *audiosink, *queue;
686 pipeline = gst_pipeline_new ("app");
688 src = gst_element_factory_make_or_warn (SOURCE, "src");
689 parser = gst_element_factory_make_or_warn ("mp3parse", "parse");
690 decoder = gst_element_factory_make_or_warn ("mad", "dec");
691 queue = gst_element_factory_make_or_warn ("queue", "queue");
692 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "sink");
694 seekable_elements = g_list_prepend (seekable_elements, audiosink);
696 g_object_set (G_OBJECT (src), "location", location, NULL);
697 //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
699 gst_bin_add (GST_BIN (pipeline), src);
700 gst_bin_add (GST_BIN (pipeline), parser);
701 gst_bin_add (GST_BIN (pipeline), decoder);
702 gst_bin_add (GST_BIN (pipeline), queue);
703 gst_bin_add (GST_BIN (pipeline), audiosink);
705 gst_element_link (src, parser);
706 gst_element_link (parser, decoder);
707 gst_element_link (decoder, queue);
708 gst_element_link (queue, audiosink);
710 seekable = gst_element_get_static_pad (queue, "src");
711 seekable_pads = g_list_prepend (seekable_pads, seekable);
712 rate_pads = g_list_prepend (rate_pads, seekable);
714 g_list_prepend (rate_pads, gst_element_get_static_pad (decoder, "sink"));
720 make_avi_pipeline (const gchar * location)
722 GstElement *pipeline, *audio_bin, *video_bin;
723 GstElement *src, *demux, *a_decoder, *v_decoder, *audiosink, *videosink;
724 GstElement *a_queue = NULL, *v_queue = NULL;
727 pipeline = gst_pipeline_new ("app");
729 src = gst_element_factory_make_or_warn (SOURCE, "src");
730 g_object_set (G_OBJECT (src), "location", location, NULL);
732 demux = gst_element_factory_make_or_warn ("avidemux", "demux");
733 seekable_elements = g_list_prepend (seekable_elements, demux);
735 gst_bin_add (GST_BIN (pipeline), src);
736 gst_bin_add (GST_BIN (pipeline), demux);
737 gst_element_link (src, demux);
739 audio_bin = gst_bin_new ("a_decoder_bin");
740 a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
741 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
742 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
743 gst_element_link (a_decoder, a_queue);
744 gst_element_link (a_queue, audiosink);
745 gst_bin_add (GST_BIN (audio_bin), a_decoder);
746 gst_bin_add (GST_BIN (audio_bin), a_queue);
747 gst_bin_add (GST_BIN (audio_bin), audiosink);
748 gst_element_set_state (audio_bin, GST_STATE_PAUSED);
750 setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
753 seekable = gst_element_get_static_pad (a_queue, "src");
754 seekable_pads = g_list_prepend (seekable_pads, seekable);
755 rate_pads = g_list_prepend (rate_pads, seekable);
757 g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
760 video_bin = gst_bin_new ("v_decoder_bin");
761 v_decoder = gst_element_factory_make_or_warn ("ffmpegdecall", "v_dec");
762 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
763 v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
764 gst_element_link (v_decoder, v_queue);
765 gst_element_link (v_queue, videosink);
766 gst_bin_add (GST_BIN (video_bin), v_decoder);
767 gst_bin_add (GST_BIN (video_bin), v_queue);
768 gst_bin_add (GST_BIN (video_bin), videosink);
770 gst_element_set_state (video_bin, GST_STATE_PAUSED);
772 setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
775 seekable = gst_element_get_static_pad (v_queue, "src");
776 seekable_pads = g_list_prepend (seekable_pads, seekable);
777 rate_pads = g_list_prepend (rate_pads, seekable);
779 g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
786 make_mpeg_pipeline (const gchar * location)
788 GstElement *pipeline, *audio_bin, *video_bin;
789 GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
790 GstElement *audiosink, *videosink;
791 GstElement *a_queue, *v_queue;
795 pipeline = gst_pipeline_new ("app");
797 src = gst_element_factory_make_or_warn (SOURCE, "src");
798 g_object_set (G_OBJECT (src), "location", location, NULL);
800 //demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
801 demux = gst_element_factory_make_or_warn ("flupsdemux", "demux");
803 gst_bin_add (GST_BIN (pipeline), src);
804 gst_bin_add (GST_BIN (pipeline), demux);
805 gst_element_link (src, demux);
807 audio_bin = gst_bin_new ("a_decoder_bin");
808 a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
809 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
810 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
811 gst_bin_add (GST_BIN (audio_bin), a_decoder);
812 gst_bin_add (GST_BIN (audio_bin), a_queue);
813 gst_bin_add (GST_BIN (audio_bin), audiosink);
815 gst_element_link (a_decoder, a_queue);
816 gst_element_link (a_queue, audiosink);
818 gst_bin_add (GST_BIN (pipeline), audio_bin);
820 pad = gst_element_get_static_pad (a_decoder, "sink");
821 gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
822 gst_object_unref (pad);
824 setup_dynamic_link (demux, "audio_c0", gst_element_get_static_pad (audio_bin,
827 video_bin = gst_bin_new ("v_decoder_bin");
828 v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
829 v_queue = gst_element_factory_make_or_warn ("queue", "v_queue");
830 v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
831 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
833 gst_bin_add (GST_BIN (video_bin), v_decoder);
834 gst_bin_add (GST_BIN (video_bin), v_queue);
835 gst_bin_add (GST_BIN (video_bin), v_filter);
836 gst_bin_add (GST_BIN (video_bin), videosink);
838 gst_element_link (v_decoder, v_queue);
839 gst_element_link (v_queue, v_filter);
840 gst_element_link (v_filter, videosink);
842 gst_bin_add (GST_BIN (pipeline), video_bin);
844 pad = gst_element_get_static_pad (v_decoder, "sink");
845 gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad));
846 gst_object_unref (pad);
848 setup_dynamic_link (demux, "video_e0", gst_element_get_static_pad (video_bin,
851 seekable = gst_element_get_static_pad (v_filter, "src");
852 seekable_pads = g_list_prepend (seekable_pads, seekable);
853 rate_pads = g_list_prepend (rate_pads, seekable);
855 g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
862 make_mpegnt_pipeline (const gchar * location)
864 GstElement *pipeline, *audio_bin, *video_bin;
865 GstElement *src, *demux, *a_decoder, *v_decoder, *v_filter;
866 GstElement *audiosink, *videosink;
870 pipeline = gst_pipeline_new ("app");
872 src = gst_element_factory_make_or_warn (SOURCE, "src");
873 g_object_set (G_OBJECT (src), "location", location, NULL);
875 demux = gst_element_factory_make_or_warn ("mpegdemux", "demux");
876 //g_object_set (G_OBJECT (demux), "sync", TRUE, NULL);
878 seekable_elements = g_list_prepend (seekable_elements, demux);
880 gst_bin_add (GST_BIN (pipeline), src);
881 gst_bin_add (GST_BIN (pipeline), demux);
882 gst_element_link (src, demux);
884 audio_bin = gst_bin_new ("a_decoder_bin");
885 a_decoder = gst_element_factory_make_or_warn ("mad", "a_dec");
886 a_queue = gst_element_factory_make_or_warn ("queue", "a_queue");
887 audiosink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
888 //g_object_set (G_OBJECT (audiosink), "fragment", 0x00180008, NULL);
889 g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
890 gst_element_link (a_decoder, a_queue);
891 gst_element_link (a_queue, audiosink);
892 gst_bin_add (GST_BIN (audio_bin), a_decoder);
893 gst_bin_add (GST_BIN (audio_bin), a_queue);
894 gst_bin_add (GST_BIN (audio_bin), audiosink);
896 setup_dynamic_link (demux, "audio_00", gst_element_get_static_pad (a_decoder,
899 seekable = gst_element_get_static_pad (a_queue, "src");
900 seekable_pads = g_list_prepend (seekable_pads, seekable);
901 rate_pads = g_list_prepend (rate_pads, seekable);
903 g_list_prepend (rate_pads, gst_element_get_static_pad (a_decoder,
906 video_bin = gst_bin_new ("v_decoder_bin");
907 v_decoder = gst_element_factory_make_or_warn ("mpeg2dec", "v_dec");
908 v_filter = gst_element_factory_make_or_warn ("ffmpegcolorspace", "v_filter");
909 videosink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
910 gst_element_link_many (v_decoder, v_filter, videosink, NULL);
912 gst_bin_add_many (GST_BIN (video_bin), v_decoder, v_filter, videosink, NULL);
914 setup_dynamic_link (demux, "video_00", gst_element_get_static_pad (v_decoder,
917 seekable = gst_element_get_static_pad (v_decoder, "src");
918 seekable_pads = g_list_prepend (seekable_pads, seekable);
919 rate_pads = g_list_prepend (rate_pads, seekable);
921 g_list_prepend (rate_pads, gst_element_get_static_pad (v_decoder,
928 playerbin_set_uri (GstElement * player, const gchar * location)
932 /* Add "file://" prefix for convenience */
933 if (g_str_has_prefix (location, "/") || !gst_uri_is_valid (location)) {
934 uri = gst_filename_to_uri (location, NULL);
935 g_print ("Setting URI: %s\n", uri);
936 g_object_set (G_OBJECT (player), "uri", uri, NULL);
939 g_print ("Setting URI: %s\n", location);
940 g_object_set (G_OBJECT (player), "uri", location, NULL);
945 construct_playerbin (const gchar * name, const gchar * location)
950 player = gst_element_factory_make (name, "player");
953 playerbin_set_uri (player, location);
955 seekable_elements = g_list_prepend (seekable_elements, player);
957 /* force element seeking on this pipeline */
960 avsink = gst_element_factory_make_or_warn (opt_audiosink_str, "a_sink");
962 g_object_set (player, "audio-sink", avsink, NULL);
964 avsink = gst_element_factory_make_or_warn (opt_videosink_str, "v_sink");
966 g_object_set (player, "video-sink", avsink, NULL);
972 make_playerbin_pipeline (const gchar * location)
974 return construct_playerbin ("playbin", location);
978 make_playerbin2_pipeline (const gchar * location)
980 GstElement *pipeline = construct_playerbin ("playbin2", location);
982 /* FIXME: this is not triggered, playbin2 is not forwarding it from the sink */
983 g_signal_connect (pipeline, "notify::volume", G_CALLBACK (volume_notify_cb),
988 #ifndef GST_DISABLE_PARSE
990 make_parselaunch_pipeline (const gchar * description)
992 GstElement *pipeline;
993 GError *error = NULL;
995 pipeline = gst_parse_launch (description, &error);
997 seekable_elements = g_list_prepend (seekable_elements, pipeline);
1008 GstElement *(*func) (const gchar * location);
1012 static Pipeline pipelines[] = {
1013 {"mp3", make_mp3_pipeline},
1014 {"avi", make_avi_pipeline},
1015 {"mpeg1", make_mpeg_pipeline},
1016 {"mpegparse", make_parse_pipeline},
1017 {"vorbis", make_vorbis_pipeline},
1018 {"theora", make_theora_pipeline},
1019 {"ogg/v/t", make_vorbis_theora_pipeline},
1020 {"avi/msmpeg4v3/mp3", make_avi_msmpeg4v3_mp3_pipeline},
1021 {"sid", make_sid_pipeline},
1022 {"flac", make_flac_pipeline},
1023 {"wav", make_wav_pipeline},
1024 {"mod", make_mod_pipeline},
1025 {"dv", make_dv_pipeline},
1026 {"mpeg1nothreads", make_mpegnt_pipeline},
1027 {"playerbin", make_playerbin_pipeline},
1028 #ifndef GST_DISABLE_PARSE
1029 {"parse-launch", make_parselaunch_pipeline},
1031 {"playerbin2", make_playerbin2_pipeline},
1035 #define NUM_TYPES ((sizeof (pipelines) / sizeof (Pipeline)) - 1)
1037 /* ui callbacks and helpers */
1040 format_value (GtkScale * scale, gdouble value)
1046 real = value * duration / N_GRAD;
1047 seconds = (gint64) real / GST_SECOND;
1048 subseconds = (gint64) real / (GST_SECOND / N_GRAD);
1050 return g_strdup_printf ("%02" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT ":%02"
1051 G_GINT64_FORMAT, seconds / 60, seconds % 60, subseconds % 100);
1056 shuttle_format_value (GtkScale * scale, gdouble value)
1058 return g_strdup_printf ("%0.*g", gtk_scale_get_digits (scale), value);
1064 const GstFormat format;
1068 static seek_format seek_formats[] = {
1069 {"tim", GST_FORMAT_TIME},
1070 {"byt", GST_FORMAT_BYTES},
1071 {"buf", GST_FORMAT_BUFFERS},
1072 {"def", GST_FORMAT_DEFAULT},
1076 G_GNUC_UNUSED static void
1079 GList *walk = rate_pads;
1082 GstPad *pad = GST_PAD (walk->data);
1085 g_print ("rate/sec %8.8s: ", GST_PAD_NAME (pad));
1086 while (seek_formats[i].name) {
1090 format = seek_formats[i].format;
1092 if (gst_pad_query_convert (pad, GST_FORMAT_TIME, GST_SECOND, &format,
1094 g_print ("%s %13" G_GINT64_FORMAT " | ", seek_formats[i].name, value);
1096 g_print ("%s %13.13s | ", seek_formats[i].name, "*NA*");
1101 g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1103 walk = g_list_next (walk);
1107 G_GNUC_UNUSED static void
1108 query_positions_elems (void)
1110 GList *walk = seekable_elements;
1113 GstElement *element = GST_ELEMENT (walk->data);
1116 g_print ("positions %8.8s: ", GST_ELEMENT_NAME (element));
1117 while (seek_formats[i].name) {
1118 gint64 position, total;
1121 format = seek_formats[i].format;
1123 if (gst_element_query_position (element, &format, &position) &&
1124 gst_element_query_duration (element, &format, &total)) {
1125 g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1126 seek_formats[i].name, position, total);
1128 g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1133 g_print (" %s\n", GST_ELEMENT_NAME (element));
1135 walk = g_list_next (walk);
1139 G_GNUC_UNUSED static void
1140 query_positions_pads (void)
1142 GList *walk = seekable_pads;
1145 GstPad *pad = GST_PAD (walk->data);
1148 g_print ("positions %8.8s: ", GST_PAD_NAME (pad));
1149 while (seek_formats[i].name) {
1151 gint64 position, total;
1153 format = seek_formats[i].format;
1155 if (gst_pad_query_position (pad, &format, &position) &&
1156 gst_pad_query_duration (pad, &format, &total)) {
1157 g_print ("%s %13" G_GINT64_FORMAT " / %13" G_GINT64_FORMAT " | ",
1158 seek_formats[i].name, position, total);
1160 g_print ("%s %13.13s / %13.13s | ", seek_formats[i].name, "*NA*",
1166 g_print (" %s:%s\n", GST_DEBUG_PAD_NAME (pad));
1168 walk = g_list_next (walk);
1172 static gboolean start_seek (GtkWidget * widget, GdkEventButton * event,
1173 gpointer user_data);
1174 static gboolean stop_seek (GtkWidget * widget, GdkEventButton * event,
1175 gpointer user_data);
1176 static void seek_cb (GtkWidget * widget);
1179 set_scale (gdouble value)
1181 g_signal_handlers_block_by_func (hscale, (void *) start_seek,
1183 g_signal_handlers_block_by_func (hscale, (void *) stop_seek,
1185 g_signal_handlers_block_by_func (hscale, (void *) seek_cb, (void *) pipeline);
1186 gtk_adjustment_set_value (adjustment, value);
1187 g_signal_handlers_unblock_by_func (hscale, (void *) start_seek,
1189 g_signal_handlers_unblock_by_func (hscale, (void *) stop_seek,
1191 g_signal_handlers_unblock_by_func (hscale, (void *) seek_cb,
1193 gtk_widget_queue_draw (hscale);
1197 update_fill (gpointer data)
1200 if (seekable_elements) {
1201 GstElement *element = GST_ELEMENT (seekable_elements->data);
1204 query = gst_query_new_buffering (GST_FORMAT_PERCENT);
1205 if (gst_element_query (element, query)) {
1206 gint64 start, stop, buffering_total;
1211 GstBufferingMode mode;
1212 gint avg_in, avg_out;
1213 gint64 buffering_left;
1215 gst_query_parse_buffering_percent (query, &busy, &percent);
1216 gst_query_parse_buffering_range (query, &format, &start, &stop,
1218 gst_query_parse_buffering_stats (query, &mode, &avg_in, &avg_out,
1221 /* note that we could start the playback when buffering_left < remaining
1223 GST_DEBUG ("buffering total %" G_GINT64_FORMAT " ms, left %"
1224 G_GINT64_FORMAT " ms", buffering_total, buffering_left);
1225 GST_DEBUG ("start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT,
1229 fill = N_GRAD * stop / GST_FORMAT_PERCENT_MAX;
1233 gtk_range_set_fill_level (GTK_RANGE (hscale), fill);
1235 gst_query_unref (query);
1242 update_scale (gpointer data)
1244 GstFormat format = GST_FORMAT_TIME;
1250 if (seekable_elements) {
1251 GstElement *element = GST_ELEMENT (seekable_elements->data);
1253 gst_element_query_position (element, &format, &position);
1254 gst_element_query_duration (element, &format, &duration);
1257 if (seekable_pads) {
1258 GstPad *pad = GST_PAD (seekable_pads->data);
1260 gst_pad_query_position (pad, &format, &position);
1261 gst_pad_query_duration (pad, &format, &duration);
1267 query_positions_elems ();
1269 query_positions_pads ();
1274 if (position >= duration)
1275 duration = position;
1278 set_scale (position * N_GRAD / duration);
1281 /* FIXME: see make_playerbin2_pipeline() and volume_notify_cb() */
1282 if (pipeline_type == 16) {
1283 g_object_notify (G_OBJECT (pipeline), "volume");
1289 static void do_seek (GtkWidget * widget);
1290 static void connect_bus_signals (GstElement * pipeline);
1291 static void set_update_scale (gboolean active);
1292 static void set_update_fill (gboolean active);
1295 end_scrub (GtkWidget * widget)
1297 GST_DEBUG ("end scrub, PAUSE");
1298 gst_element_set_state (pipeline, GST_STATE_PAUSED);
1299 seek_timeout_id = 0;
1305 send_event (GstEvent * event)
1307 gboolean res = FALSE;
1310 GList *walk = seekable_pads;
1313 GstPad *seekable = GST_PAD (walk->data);
1315 GST_DEBUG ("send event on pad %s:%s", GST_DEBUG_PAD_NAME (seekable));
1317 gst_event_ref (event);
1318 res = gst_pad_send_event (seekable, event);
1320 walk = g_list_next (walk);
1323 GList *walk = seekable_elements;
1326 GstElement *seekable = GST_ELEMENT (walk->data);
1328 GST_DEBUG ("send event on element %s", GST_ELEMENT_NAME (seekable));
1330 gst_event_ref (event);
1331 res = gst_element_send_event (seekable, event);
1333 walk = g_list_next (walk);
1336 gst_event_unref (event);
1341 do_seek (GtkWidget * widget)
1344 gboolean res = FALSE;
1348 real = gtk_range_get_value (GTK_RANGE (widget)) * duration / N_GRAD;
1350 GST_DEBUG ("value=%f, real=%" G_GINT64_FORMAT,
1351 gtk_range_get_value (GTK_RANGE (widget)), real);
1355 flags |= GST_SEEK_FLAG_FLUSH;
1357 flags |= GST_SEEK_FLAG_ACCURATE;
1359 flags |= GST_SEEK_FLAG_KEY_UNIT;
1361 flags |= GST_SEEK_FLAG_SEGMENT;
1363 flags |= GST_SEEK_FLAG_SKIP;
1366 s_event = gst_event_new_seek (rate,
1367 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, real, GST_SEEK_TYPE_SET,
1368 GST_CLOCK_TIME_NONE);
1369 GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1370 rate, GST_TIME_ARGS (real), GST_TIME_ARGS (duration));
1372 s_event = gst_event_new_seek (rate,
1373 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1374 GST_SEEK_TYPE_SET, real);
1375 GST_DEBUG ("seek with rate %lf to %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
1376 rate, GST_TIME_ARGS (0), GST_TIME_ARGS (real));
1379 res = send_event (s_event);
1383 gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1385 set_update_scale (TRUE);
1388 g_print ("seek failed\n");
1389 set_update_scale (TRUE);
1394 seek_cb (GtkWidget * widget)
1396 /* If the timer hasn't expired yet, then the pipeline is running */
1397 if (play_scrub && seek_timeout_id != 0) {
1398 GST_DEBUG ("do scrub seek, PAUSED");
1399 gst_element_set_state (pipeline, GST_STATE_PAUSED);
1402 GST_DEBUG ("do seek");
1406 GST_DEBUG ("do scrub seek, PLAYING");
1407 gst_element_set_state (pipeline, GST_STATE_PLAYING);
1409 if (seek_timeout_id == 0) {
1411 g_timeout_add (SCRUB_TIME, (GSourceFunc) end_scrub, widget);
1417 set_update_fill (gboolean active)
1419 GST_DEBUG ("fill scale is %d", active);
1424 g_timeout_add (FILL_INTERVAL, (GSourceFunc) update_fill, pipeline);
1428 g_source_remove (fill_id);
1435 set_update_scale (gboolean active)
1438 GST_DEBUG ("update scale is %d", active);
1441 if (update_id == 0) {
1443 g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, pipeline);
1447 g_source_remove (update_id);
1454 start_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1456 if (event->type != GDK_BUTTON_PRESS)
1459 set_update_scale (FALSE);
1461 if (state == GST_STATE_PLAYING && flush_seek && scrub) {
1462 GST_DEBUG ("start scrub seek, PAUSE");
1463 gst_element_set_state (pipeline, GST_STATE_PAUSED);
1466 if (changed_id == 0 && flush_seek && scrub) {
1468 g_signal_connect (hscale, "value-changed", G_CALLBACK (seek_cb),
1476 stop_seek (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1479 g_signal_handler_disconnect (hscale, changed_id);
1483 if (!flush_seek || !scrub) {
1484 GST_DEBUG ("do final seek");
1488 if (seek_timeout_id != 0) {
1489 g_source_remove (seek_timeout_id);
1490 seek_timeout_id = 0;
1491 /* Still scrubbing, so the pipeline is playing, see if we need PAUSED
1493 if (state == GST_STATE_PAUSED) {
1494 GST_DEBUG ("stop scrub seek, PAUSED");
1495 gst_element_set_state (pipeline, GST_STATE_PAUSED);
1498 if (state == GST_STATE_PLAYING) {
1499 GST_DEBUG ("stop scrub seek, PLAYING");
1500 gst_element_set_state (pipeline, GST_STATE_PLAYING);
1508 play_cb (GtkButton * button, gpointer data)
1510 GstStateChangeReturn ret;
1512 if (state != GST_STATE_PLAYING) {
1513 g_print ("PLAY pipeline\n");
1514 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1516 ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
1518 case GST_STATE_CHANGE_FAILURE:
1520 case GST_STATE_CHANGE_NO_PREROLL:
1526 state = GST_STATE_PLAYING;
1527 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
1534 g_print ("PLAY failed\n");
1535 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Play failed");
1540 pause_cb (GtkButton * button, gpointer data)
1542 g_static_mutex_lock (&state_mutex);
1543 if (state != GST_STATE_PAUSED) {
1544 GstStateChangeReturn ret;
1546 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1547 g_print ("PAUSE pipeline\n");
1548 ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
1550 case GST_STATE_CHANGE_FAILURE:
1552 case GST_STATE_CHANGE_NO_PREROLL:
1559 state = GST_STATE_PAUSED;
1560 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Paused");
1562 g_static_mutex_unlock (&state_mutex);
1568 g_static_mutex_unlock (&state_mutex);
1569 g_print ("PAUSE failed\n");
1570 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Pause failed");
1575 stop_cb (GtkButton * button, gpointer data)
1577 if (state != STOP_STATE) {
1578 GstStateChangeReturn ret;
1581 g_print ("READY pipeline\n");
1582 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
1584 g_static_mutex_lock (&state_mutex);
1585 ret = gst_element_set_state (pipeline, STOP_STATE);
1586 if (ret == GST_STATE_CHANGE_FAILURE)
1590 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
1591 gtk_widget_queue_draw (video_window);
1595 set_update_scale (FALSE);
1597 set_update_fill (FALSE);
1599 if (pipeline_type == 16)
1600 clear_streams (pipeline);
1601 g_static_mutex_unlock (&state_mutex);
1604 /* if one uses parse_launch, play, stop and play again it fails as all the
1605 * pads after the demuxer can't be reconnected
1607 if (!strcmp (pipelines[pipeline_type].name, "parse-launch")) {
1608 gst_element_set_state (pipeline, GST_STATE_NULL);
1609 gst_object_unref (pipeline);
1611 g_list_free (seekable_elements);
1612 seekable_elements = NULL;
1613 g_list_free (seekable_pads);
1614 seekable_pads = NULL;
1615 g_list_free (rate_pads);
1618 pipeline = pipelines[pipeline_type].func (pipeline_spec);
1619 g_assert (pipeline);
1620 gst_element_set_state (pipeline, STOP_STATE);
1621 connect_bus_signals (pipeline);
1624 gtk_widget_set_sensitive (GTK_WIDGET (hscale), TRUE);
1625 for (i = 0; i < G_N_ELEMENTS (navigation_buttons); i++)
1626 gtk_widget_set_sensitive (navigation_buttons[i].button, FALSE);
1632 g_static_mutex_unlock (&state_mutex);
1633 g_print ("STOP failed\n");
1634 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stop failed");
1639 accurate_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1641 accurate_seek = gtk_toggle_button_get_active (button);
1645 key_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1647 keyframe_seek = gtk_toggle_button_get_active (button);
1651 loop_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1653 loop_seek = gtk_toggle_button_get_active (button);
1654 if (state == GST_STATE_PLAYING) {
1660 flush_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1662 flush_seek = gtk_toggle_button_get_active (button);
1666 scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1668 scrub = gtk_toggle_button_get_active (button);
1672 play_scrub_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1674 play_scrub = gtk_toggle_button_get_active (button);
1678 skip_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1680 skip_seek = gtk_toggle_button_get_active (button);
1681 if (state == GST_STATE_PLAYING) {
1687 rate_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
1689 gboolean res = FALSE;
1693 rate = gtk_spin_button_get_value (button);
1695 GST_DEBUG ("rate changed to %lf", rate);
1699 flags |= GST_SEEK_FLAG_FLUSH;
1701 flags |= GST_SEEK_FLAG_SEGMENT;
1703 flags |= GST_SEEK_FLAG_ACCURATE;
1705 flags |= GST_SEEK_FLAG_KEY_UNIT;
1707 flags |= GST_SEEK_FLAG_SKIP;
1710 s_event = gst_event_new_seek (rate,
1711 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
1712 GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1714 s_event = gst_event_new_seek (rate,
1715 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
1716 GST_SEEK_TYPE_SET, position);
1719 res = send_event (s_event);
1723 gst_element_get_state (GST_ELEMENT (pipeline), NULL, NULL, SEEK_TIMEOUT);
1726 g_print ("seek failed\n");
1730 update_flag (GstPipeline * pipeline, gint num, gboolean state)
1734 g_object_get (pipeline, "flags", &flags, NULL);
1736 flags |= (1 << num);
1738 flags &= ~(1 << num);
1739 g_object_set (pipeline, "flags", flags, NULL);
1743 vis_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1747 state = gtk_toggle_button_get_active (button);
1748 update_flag (pipeline, 3, state);
1749 gtk_widget_set_sensitive (vis_combo, state);
1753 audio_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1757 state = gtk_toggle_button_get_active (button);
1758 update_flag (pipeline, 1, state);
1759 gtk_widget_set_sensitive (audio_combo, state);
1763 video_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1767 state = gtk_toggle_button_get_active (button);
1768 update_flag (pipeline, 0, state);
1769 gtk_widget_set_sensitive (video_combo, state);
1773 text_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1777 state = gtk_toggle_button_get_active (button);
1778 update_flag (pipeline, 2, state);
1779 gtk_widget_set_sensitive (text_combo, state);
1783 mute_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1787 mute = gtk_toggle_button_get_active (button);
1788 g_object_set (pipeline, "mute", mute, NULL);
1792 download_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1796 state = gtk_toggle_button_get_active (button);
1797 update_flag (pipeline, 7, state);
1801 buffer_toggle_cb (GtkToggleButton * button, GstPipeline * pipeline)
1805 state = gtk_toggle_button_get_active (button);
1806 update_flag (pipeline, 8, state);
1810 clear_streams (GstElement * pipeline)
1814 /* remove previous info */
1815 for (i = 0; i < n_video; i++)
1816 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (video_combo), 0);
1817 for (i = 0; i < n_audio; i++)
1818 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (audio_combo), 0);
1819 for (i = 0; i < n_text; i++)
1820 gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (text_combo), 0);
1822 n_audio = n_video = n_text = 0;
1823 gtk_widget_set_sensitive (video_combo, FALSE);
1824 gtk_widget_set_sensitive (audio_combo, FALSE);
1825 gtk_widget_set_sensitive (text_combo, FALSE);
1827 need_streams = TRUE;
1831 update_streams (GstPipeline * pipeline)
1835 if (pipeline_type == 16 && need_streams) {
1841 /* remove previous info */
1842 clear_streams (GST_ELEMENT_CAST (pipeline));
1844 /* here we get and update the different streams detected by playbin2 */
1845 g_object_get (pipeline, "n-video", &n_video, NULL);
1846 g_object_get (pipeline, "n-audio", &n_audio, NULL);
1847 g_object_get (pipeline, "n-text", &n_text, NULL);
1849 g_print ("video %d, audio %d, text %d\n", n_video, n_audio, n_text);
1852 for (i = 0; i < n_video; i++) {
1853 g_signal_emit_by_name (pipeline, "get-video-tags", i, &tags);
1855 str = gst_structure_to_string ((GstStructure *) tags);
1856 g_print ("video %d: %s\n", i, str);
1859 /* find good name for the label */
1860 name = g_strdup_printf ("video %d", i + 1);
1861 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (video_combo), name);
1864 state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (video_checkbox));
1865 gtk_widget_set_sensitive (video_combo, state && n_video > 0);
1866 gtk_combo_box_set_active (GTK_COMBO_BOX (video_combo), active_idx);
1869 for (i = 0; i < n_audio; i++) {
1870 g_signal_emit_by_name (pipeline, "get-audio-tags", i, &tags);
1872 str = gst_structure_to_string ((GstStructure *) tags);
1873 g_print ("audio %d: %s\n", i, str);
1876 /* find good name for the label */
1877 name = g_strdup_printf ("audio %d", i + 1);
1878 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (audio_combo), name);
1881 state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (audio_checkbox));
1882 gtk_widget_set_sensitive (audio_combo, state && n_audio > 0);
1883 gtk_combo_box_set_active (GTK_COMBO_BOX (audio_combo), active_idx);
1886 for (i = 0; i < n_text; i++) {
1887 g_signal_emit_by_name (pipeline, "get-text-tags", i, &tags);
1891 const GValue *value;
1893 str = gst_structure_to_string ((GstStructure *) tags);
1894 g_print ("text %d: %s\n", i, str);
1897 /* get the language code if we can */
1898 value = gst_tag_list_get_value_index (tags, GST_TAG_LANGUAGE_CODE, 0);
1899 if (value && G_VALUE_HOLDS_STRING (value)) {
1900 name = g_strdup_printf ("text %s", g_value_get_string (value));
1903 /* find good name for the label if we didn't use a tag */
1905 name = g_strdup_printf ("text %d", i + 1);
1907 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (text_combo), name);
1910 state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_checkbox));
1911 gtk_widget_set_sensitive (text_combo, state && n_text > 0);
1912 gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), active_idx);
1914 need_streams = FALSE;
1919 video_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1923 active = gtk_combo_box_get_active (combo);
1925 g_print ("setting current video track %d\n", active);
1926 g_object_set (pipeline, "current-video", active, NULL);
1930 audio_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1934 active = gtk_combo_box_get_active (combo);
1936 g_print ("setting current audio track %d\n", active);
1937 g_object_set (pipeline, "current-audio", active, NULL);
1941 text_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1945 active = gtk_combo_box_get_active (combo);
1947 g_print ("setting current text track %d\n", active);
1948 g_object_set (pipeline, "current-text", active, NULL);
1952 filter_features (GstPluginFeature * feature, gpointer data)
1954 GstElementFactory *f;
1956 if (!GST_IS_ELEMENT_FACTORY (feature))
1958 f = GST_ELEMENT_FACTORY (feature);
1959 if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
1966 init_visualization_features (void)
1970 vis_entries = g_array_new (FALSE, FALSE, sizeof (VisEntry));
1972 list = gst_registry_feature_filter (gst_registry_get_default (),
1973 filter_features, FALSE, NULL);
1975 for (walk = list; walk; walk = g_list_next (walk)) {
1979 entry.factory = GST_ELEMENT_FACTORY (walk->data);
1980 name = gst_element_factory_get_longname (entry.factory);
1982 g_array_append_val (vis_entries, entry);
1983 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (vis_combo), name);
1985 gtk_combo_box_set_active (GTK_COMBO_BOX (vis_combo), 0);
1989 vis_combo_cb (GtkComboBox * combo, GstPipeline * pipeline)
1993 GstElement *element;
1995 /* get the selected index and get the factory for this index */
1996 index = gtk_combo_box_get_active (GTK_COMBO_BOX (vis_combo));
1997 if (vis_entries->len > 0) {
1998 entry = &g_array_index (vis_entries, VisEntry, index);
2000 /* create an instance of the element from the factory */
2001 element = gst_element_factory_create (entry->factory, NULL);
2005 /* set vis plugin for playbin2 */
2006 g_object_set (pipeline, "vis-plugin", element, NULL);
2011 volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
2015 volume = gtk_spin_button_get_value (button);
2017 g_object_set (pipeline, "volume", volume, NULL);
2021 volume_notify_cb (GstElement * pipeline, GParamSpec * arg, gpointer user_dat)
2023 gdouble cur_volume, new_volume;
2025 g_object_get (pipeline, "volume", &new_volume, NULL);
2026 cur_volume = gtk_spin_button_get_value (GTK_SPIN_BUTTON (volume_spinbutton));
2027 if (fabs (cur_volume - new_volume) > 0.001) {
2028 g_signal_handlers_block_by_func (volume_spinbutton,
2029 volume_spinbutton_changed_cb, pipeline);
2030 gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), new_volume);
2031 g_signal_handlers_unblock_by_func (volume_spinbutton,
2032 volume_spinbutton_changed_cb, pipeline);
2037 shot_cb (GtkButton * button, gpointer data)
2042 /* convert to our desired format (RGB24) */
2043 caps = gst_caps_new_simple ("video/x-raw-rgb",
2044 "bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
2045 /* Note: we don't ask for a specific width/height here, so that
2046 * videoscale can adjust dimensions from a non-1/1 pixel aspect
2047 * ratio to a 1/1 pixel-aspect-ratio */
2048 "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
2049 "endianness", G_TYPE_INT, G_BIG_ENDIAN,
2050 "red_mask", G_TYPE_INT, 0xff0000,
2051 "green_mask", G_TYPE_INT, 0x00ff00,
2052 "blue_mask", G_TYPE_INT, 0x0000ff, NULL);
2054 /* convert the latest frame to the requested format */
2055 g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
2056 gst_caps_unref (caps);
2064 GError *error = NULL;
2066 /* get the snapshot buffer format now. We set the caps on the appsink so
2067 * that it can only be an rgb buffer. The only thing we have not specified
2068 * on the caps is the height, which is dependant on the pixel-aspect-ratio
2069 * of the source material */
2070 caps = GST_BUFFER_CAPS (buffer);
2072 g_warning ("could not get snapshot format\n");
2075 s = gst_caps_get_structure (caps, 0);
2077 /* we need to get the final caps on the buffer to get the size */
2078 res = gst_structure_get_int (s, "width", &width);
2079 res |= gst_structure_get_int (s, "height", &height);
2081 g_warning ("could not get snapshot dimension\n");
2085 /* create pixmap from buffer and save, gstreamer video buffers have a stride
2086 * that is rounded up to the nearest multiple of 4 */
2087 pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
2088 GDK_COLORSPACE_RGB, FALSE, 8, width, height,
2089 GST_ROUND_UP_4 (width * 3), NULL, NULL);
2091 /* save the pixbuf */
2092 gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
2095 gst_buffer_unref (buffer);
2099 /* called when the Step button is pressed */
2101 step_cb (GtkButton * button, gpointer data)
2107 gboolean flush, res;
2110 active = gtk_combo_box_get_active (GTK_COMBO_BOX (format_combo));
2112 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON
2113 (step_amount_spinbutton));
2114 rate = gtk_spin_button_get_value (GTK_SPIN_BUTTON (step_rate_spinbutton));
2119 format = GST_FORMAT_BUFFERS;
2122 format = GST_FORMAT_TIME;
2123 amount *= GST_MSECOND;
2126 format = GST_FORMAT_UNDEFINED;
2130 event = gst_event_new_step (format, amount, rate, flush, FALSE);
2132 res = send_event (event);
2135 g_print ("Sending step event failed\n");
2140 message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2142 const GstStructure *s;
2144 switch (GST_MESSAGE_TYPE (message)) {
2145 case GST_MESSAGE_ERROR:
2146 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2147 GST_DEBUG_GRAPH_SHOW_ALL, "seek.error");
2149 case GST_MESSAGE_WARNING:
2150 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2151 GST_DEBUG_GRAPH_SHOW_ALL, "seek.warning");
2157 s = gst_message_get_structure (message);
2158 g_print ("message from \"%s\" (%s): ",
2159 GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))),
2160 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2164 sstr = gst_structure_to_string (s);
2165 g_print ("%s\n", sstr);
2168 g_print ("no message details\n");
2172 static gboolean shuttling = FALSE;
2173 static gdouble shuttle_rate = 0.0;
2174 static gdouble play_rate = 1.0;
2177 do_shuttle (GstElement * element)
2182 duration = 40 * GST_MSECOND;
2186 gst_element_send_event (element,
2187 gst_event_new_step (GST_FORMAT_TIME, duration, shuttle_rate, FALSE,
2192 msg_sync_step_done (GstBus * bus, GstMessage * message, GstElement * element)
2198 gboolean intermediate;
2202 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
2203 &intermediate, &duration, &eos);
2206 g_print ("stepped till EOS\n");
2210 if (g_static_mutex_trylock (&state_mutex)) {
2212 do_shuttle (element);
2213 g_static_mutex_unlock (&state_mutex);
2215 /* ignore step messages that come while we are doing a state change */
2216 g_print ("state change is busy\n");
2221 shuttle_toggled (GtkToggleButton * button, GstElement * element)
2225 active = gtk_toggle_button_get_active (button);
2227 if (active != shuttling) {
2229 g_print ("shuttling %s\n", shuttling ? "active" : "inactive");
2233 pause_cb (NULL, NULL);
2234 gst_element_get_state (element, NULL, NULL, -1);
2240 shuttle_rate_switch (GstElement * element)
2246 if (state == GST_STATE_PLAYING) {
2247 /* pause when we need to */
2248 pause_cb (NULL, NULL);
2249 gst_element_get_state (element, NULL, NULL, -1);
2252 if (play_rate == 1.0)
2257 g_print ("rate changed to %lf %" GST_TIME_FORMAT "\n", play_rate,
2258 GST_TIME_ARGS (position));
2260 flags = GST_SEEK_FLAG_FLUSH;
2261 flags |= GST_SEEK_FLAG_ACCURATE;
2263 if (play_rate >= 0.0) {
2264 s_event = gst_event_new_seek (play_rate,
2265 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, position,
2266 GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
2268 s_event = gst_event_new_seek (play_rate,
2269 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2270 GST_SEEK_TYPE_SET, position);
2272 res = send_event (s_event);
2274 gst_element_get_state (element, NULL, NULL, SEEK_TIMEOUT);
2276 g_print ("seek failed\n");
2281 shuttle_value_changed (GtkRange * range, GstElement * element)
2285 rate = gtk_adjustment_get_value (shuttle_adjustment);
2288 g_print ("rate 0.0, pause\n");
2289 pause_cb (NULL, NULL);
2290 gst_element_get_state (element, NULL, NULL, -1);
2292 g_print ("rate changed %0.3g\n", rate);
2294 if ((rate < 0.0 && play_rate > 0.0) || (rate > 0.0 && play_rate < 0.0)) {
2295 shuttle_rate_switch (element);
2298 shuttle_rate = ABS (rate);
2299 if (state != GST_STATE_PLAYING) {
2300 do_shuttle (element);
2301 play_cb (NULL, NULL);
2307 colorbalance_value_changed (GtkRange * range, gpointer user_data)
2309 const gchar *label = user_data;
2312 GstColorBalanceChannel *channel = NULL;
2313 const GList *channels, *l;
2315 val = gtk_range_get_value (range);
2317 g_print ("colorbalance %s value changed %lf\n", label, val / 100.);
2319 if (!colorbalance_element) {
2320 find_interface_elements ();
2321 if (!colorbalance_element)
2326 gst_color_balance_list_channels (GST_COLOR_BALANCE
2327 (colorbalance_element));
2328 for (l = channels; l; l = l->next) {
2329 GstColorBalanceChannel *tmp = l->data;
2331 if (g_strrstr (tmp->label, label)) {
2341 (gint) (0.5 + channel->min_value +
2342 (val / 100.0) * ((gdouble) channel->max_value -
2343 (gdouble) channel->min_value));
2344 gst_color_balance_set_value (GST_COLOR_BALANCE (colorbalance_element),
2349 msg_async_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2351 GST_DEBUG ("async done");
2352 /* when we get ASYNC_DONE we can query position, duration and other
2354 update_scale (pipeline);
2356 /* update the available streams */
2357 update_streams (pipeline);
2359 find_interface_elements ();
2363 msg_state_changed (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2365 const GstStructure *s;
2367 s = gst_message_get_structure (message);
2369 /* We only care about state changed on the pipeline */
2370 if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (pipeline)) {
2371 GstState old, new, pending;
2373 gst_message_parse_state_changed (message, &old, &new, &pending);
2375 /* When state of the pipeline changes to paused or playing we start updating scale */
2376 if (new == GST_STATE_PLAYING) {
2377 set_update_scale (TRUE);
2379 set_update_scale (FALSE);
2382 /* dump graph for (some) pipeline state changes */
2386 dump_name = g_strdup_printf ("seek.%s_%s",
2387 gst_element_state_get_name (old), gst_element_state_get_name (new));
2389 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
2390 GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
2398 msg_segment_done (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
2405 GST_DEBUG ("position is %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2406 gst_message_parse_segment_done (message, &format, &position);
2407 GST_DEBUG ("end of segment at %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
2410 /* in the segment-done callback we never flush as this would not make sense
2411 * for seamless playback. */
2413 flags |= GST_SEEK_FLAG_SEGMENT;
2415 flags |= GST_SEEK_FLAG_SKIP;
2417 s_event = gst_event_new_seek (rate,
2418 GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0),
2419 GST_SEEK_TYPE_SET, duration);
2421 GST_DEBUG ("restart loop with rate %lf to 0 / %" GST_TIME_FORMAT,
2422 rate, GST_TIME_ARGS (duration));
2424 res = send_event (s_event);
2426 g_print ("segment seek failed\n");
2429 /* in stream buffering mode we PAUSE the pipeline until we receive a 100%
2432 do_stream_buffering (gint percent)
2436 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2437 bufstr = g_strdup_printf ("Buffering...%d", percent);
2438 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2441 if (percent == 100) {
2442 /* a 100% message means buffering is done */
2444 /* if the desired state is playing, go back */
2445 if (state == GST_STATE_PLAYING) {
2446 /* no state management needed for live pipelines */
2448 fprintf (stderr, "Done buffering, setting pipeline to PLAYING ...\n");
2449 gst_element_set_state (pipeline, GST_STATE_PLAYING);
2451 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), status_id);
2452 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Playing");
2455 /* buffering busy */
2456 if (buffering == FALSE && state == GST_STATE_PLAYING) {
2457 /* we were not buffering but PLAYING, PAUSE the pipeline. */
2459 fprintf (stderr, "Buffering, setting pipeline to PAUSED ...\n");
2460 gst_element_set_state (pipeline, GST_STATE_PAUSED);
2468 do_download_buffering (gint percent)
2470 if (!buffering && percent < 100) {
2475 bufstr = g_strdup_printf ("Downloading...");
2476 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, bufstr);
2479 /* once we get a buffering message, we'll do the fill update */
2480 set_update_fill (TRUE);
2482 if (state == GST_STATE_PLAYING && !is_live) {
2483 fprintf (stderr, "Downloading, setting pipeline to PAUSED ...\n");
2484 gst_element_set_state (pipeline, GST_STATE_PAUSED);
2485 /* user has to manually start the playback */
2486 state = GST_STATE_PAUSED;
2492 msg_buffering (GstBus * bus, GstMessage * message, GstPipeline * data)
2496 gst_message_parse_buffering (message, &percent);
2498 /* get more stats */
2499 gst_message_parse_buffering_stats (message, &mode, NULL, NULL,
2503 case GST_BUFFERING_DOWNLOAD:
2504 do_download_buffering (percent);
2506 case GST_BUFFERING_LIVE:
2508 case GST_BUFFERING_TIMESHIFT:
2509 case GST_BUFFERING_STREAM:
2510 do_stream_buffering (percent);
2516 msg_clock_lost (GstBus * bus, GstMessage * message, GstPipeline * data)
2518 g_print ("clock lost! PAUSE and PLAY to select a new clock\n");
2519 if (state == GST_STATE_PLAYING) {
2520 gst_element_set_state (pipeline, GST_STATE_PAUSED);
2521 gst_element_set_state (pipeline, GST_STATE_PLAYING);
2526 is_valid_color_balance_element (GstElement * element)
2528 GstColorBalance *bal = GST_COLOR_BALANCE (element);
2529 gboolean have_brightness = FALSE;
2530 gboolean have_contrast = FALSE;
2531 gboolean have_hue = FALSE;
2532 gboolean have_saturation = FALSE;
2533 const GList *channels, *l;
2535 channels = gst_color_balance_list_channels (bal);
2536 for (l = channels; l; l = l->next) {
2537 GstColorBalanceChannel *ch = l->data;
2539 if (g_strrstr (ch->label, "BRIGHTNESS"))
2540 have_brightness = TRUE;
2541 else if (g_strrstr (ch->label, "CONTRAST"))
2542 have_contrast = TRUE;
2543 else if (g_strrstr (ch->label, "HUE"))
2545 else if (g_strrstr (ch->label, "SATURATION"))
2546 have_saturation = TRUE;
2549 return have_brightness && have_contrast && have_hue && have_saturation;
2553 find_interface_elements (void)
2555 GstElement *video_sink, *playsink;
2558 gboolean done = FALSE, hardware = FALSE;
2560 g_object_get (pipeline, "video-sink", &video_sink, NULL);
2564 if (navigation_element)
2565 gst_object_unref (navigation_element);
2567 if (colorbalance_element)
2568 gst_object_unref (colorbalance_element);
2569 colorbalance_element = NULL;
2571 if (GST_IS_NAVIGATION (video_sink)) {
2572 navigation_element = gst_object_ref (video_sink);
2573 } else if (GST_IS_BIN (video_sink)) {
2574 navigation_element =
2575 gst_bin_get_by_interface (GST_BIN (video_sink), GST_TYPE_NAVIGATION);
2577 navigation_element = NULL;
2580 gst_object_unref (video_sink);
2582 playsink = gst_bin_get_by_name (GST_BIN (pipeline), "playsink");
2583 g_assert (playsink != NULL);
2585 it = gst_bin_iterate_all_by_interface (GST_BIN (playsink),
2586 GST_TYPE_COLOR_BALANCE);
2588 switch (gst_iterator_next (it, &item)) {
2589 case GST_ITERATOR_OK:{
2590 GstElement *element = GST_ELEMENT (item);
2592 if (is_valid_color_balance_element (element)) {
2593 if (!colorbalance_element) {
2594 colorbalance_element = GST_ELEMENT_CAST (gst_object_ref (element));
2596 (gst_color_balance_get_balance_type (GST_COLOR_BALANCE
2597 (element)) == GST_COLOR_BALANCE_HARDWARE);
2598 } else if (!hardware) {
2600 (gst_color_balance_get_balance_type (GST_COLOR_BALANCE
2601 (element)) == GST_COLOR_BALANCE_HARDWARE);
2604 if (colorbalance_element)
2605 gst_object_unref (colorbalance_element);
2606 colorbalance_element =
2607 GST_ELEMENT_CAST (gst_object_ref (element));
2613 gst_object_unref (element);
2615 if (hardware && colorbalance_element)
2619 case GST_ITERATOR_RESYNC:
2620 gst_iterator_resync (it);
2623 if (colorbalance_element)
2624 gst_object_unref (colorbalance_element);
2625 colorbalance_element = NULL;
2627 case GST_ITERATOR_DONE:
2628 case GST_ITERATOR_ERROR:
2634 gst_iterator_free (it);
2636 gst_object_unref (playsink);
2639 /* called when Navigation command button is pressed */
2641 navigation_cmd_cb (GtkButton * button, gpointer data)
2643 GstNavigationCommand cmd = GPOINTER_TO_INT (data);
2645 if (!navigation_element) {
2646 find_interface_elements ();
2647 if (!navigation_element)
2651 gst_navigation_send_command (GST_NAVIGATION (navigation_element), cmd);
2654 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
2656 static GstElement *xoverlay_element = NULL;
2657 static guintptr embed_xid = 0;
2659 /* We set the xid here in response to the prepare-xwindow-id message via a
2660 * bus sync handler because we don't know the actual videosink used from the
2661 * start (as we don't know the pipeline, or bin elements such as autovideosink
2662 * or gconfvideosink may be used which create the actual videosink only once
2663 * the pipeline is started) */
2664 static GstBusSyncReply
2665 bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data)
2667 if ((GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) &&
2668 gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
2669 GstElement *element = GST_ELEMENT (GST_MESSAGE_SRC (message));
2671 if (xoverlay_element)
2672 gst_object_unref (xoverlay_element);
2673 xoverlay_element = GST_ELEMENT (gst_object_ref (element));
2675 g_print ("got prepare-xwindow-id, setting XID %" G_GUINTPTR_FORMAT "\n",
2678 if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
2679 "force-aspect-ratio")) {
2680 g_object_set (element, "force-aspect-ratio", TRUE, NULL);
2683 /* Should have been initialised from main thread before (can't use
2684 * GDK_WINDOW_XID here with Gtk+ >= 2.18, because the sync handler will
2685 * be called from a streaming thread and GDK_WINDOW_XID maps to more than
2686 * a simple structure lookup with Gtk+ >= 2.18, where 'more' is stuff that
2687 * shouldn't be done from a non-GUI thread without explicit locking). */
2688 g_assert (embed_xid != 0);
2690 gst_x_overlay_set_window_handle (GST_X_OVERLAY (element), embed_xid);
2691 gst_x_overlay_handle_events (GST_X_OVERLAY (element), FALSE);
2693 find_interface_elements ();
2695 return GST_BUS_PASS;
2700 draw_cb (GtkWidget * widget, cairo_t * cr, gpointer data)
2702 if (state < GST_STATE_PAUSED) {
2705 width = gtk_widget_get_allocated_width (widget);
2706 height = gtk_widget_get_allocated_height (widget);
2707 cairo_set_source_rgb (cr, 0, 0, 0);
2708 cairo_rectangle (cr, 0, 0, width, height);
2713 if (xoverlay_element)
2714 gst_x_overlay_expose (GST_X_OVERLAY (xoverlay_element));
2720 realize_cb (GtkWidget * widget, gpointer data)
2722 GdkWindow *window = gtk_widget_get_window (widget);
2724 /* This is here just for pedagogical purposes, GDK_WINDOW_XID will call it
2726 if (!gdk_window_ensure_native (window))
2727 g_error ("Couldn't create native window needed for GstXOverlay!");
2729 #if defined (GDK_WINDOWING_WIN32)
2730 embed_xid = GDK_WINDOW_HWND (window);
2731 g_print ("Window realize: video window HWND = %lu\n", embed_xid);
2732 #elif defined (GDK_WINDOWING_QUARTZ)
2733 embed_xid = gdk_quartz_window_get_nsview (window);
2734 g_print ("Window realize: video window NSView = %p\n", embed_xid);
2735 #elif defined (GDK_WINDOWING_X11)
2736 embed_xid = GDK_WINDOW_XID (window);
2737 g_print ("Window realize: video window XID = %" G_GUINTPTR_FORMAT "\n",
2743 button_press_cb (GtkWidget * widget, GdkEventButton * event, gpointer user_data)
2745 gtk_widget_grab_focus (widget);
2747 if (navigation_element)
2748 gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2749 "mouse-button-press", event->button, event->x, event->y);
2755 button_release_cb (GtkWidget * widget, GdkEventButton * event,
2758 if (navigation_element)
2759 gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2760 "mouse-button-release", event->button, event->x, event->y);
2766 key_press_cb (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
2768 if (navigation_element)
2769 gst_navigation_send_key_event (GST_NAVIGATION (navigation_element),
2770 "key-press", gdk_keyval_name (event->keyval));
2776 key_release_cb (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
2778 if (navigation_element)
2779 gst_navigation_send_key_event (GST_NAVIGATION (navigation_element),
2780 "key-release", gdk_keyval_name (event->keyval));
2786 motion_notify_cb (GtkWidget * widget, GdkEventMotion * event,
2789 if (navigation_element)
2790 gst_navigation_send_mouse_event (GST_NAVIGATION (navigation_element),
2791 "mouse-move", 0, event->x, event->y);
2797 msg_eos (GstBus * bus, GstMessage * message, GstPipeline * data)
2799 message_received (bus, message, data);
2801 /* Set new uri for playerbins and continue playback */
2802 if (l && (pipeline_type == 14 || pipeline_type == 16)) {
2803 stop_cb (NULL, NULL);
2804 l = g_list_next (l);
2806 playerbin_set_uri (GST_ELEMENT (data), l->data);
2807 play_cb (NULL, NULL);
2813 msg_step_done (GstBus * bus, GstMessage * message, GstPipeline * data)
2816 message_received (bus, message, data);
2820 msg (GstBus * bus, GstMessage * message, GstPipeline * data)
2822 GstNavigationMessageType nav_type;
2824 nav_type = gst_navigation_message_get_type (message);
2826 case GST_NAVIGATION_MESSAGE_COMMANDS_CHANGED:{
2830 /* Heuristic to detect if we're dealing with a DVD menu */
2831 query = gst_navigation_query_new_commands ();
2832 res = gst_element_query (GST_ELEMENT (GST_MESSAGE_SRC (message)), query);
2834 for (j = 0; j < G_N_ELEMENTS (navigation_buttons); j++)
2835 gtk_widget_set_sensitive (navigation_buttons[j].button, FALSE);
2838 gboolean is_menu = FALSE;
2841 if (gst_navigation_query_parse_commands_length (query, &n)) {
2842 for (i = 0; i < n; i++) {
2843 GstNavigationCommand cmd;
2845 if (!gst_navigation_query_parse_commands_nth (query, i, &cmd))
2848 is_menu |= (cmd == GST_NAVIGATION_COMMAND_ACTIVATE);
2849 is_menu |= (cmd == GST_NAVIGATION_COMMAND_LEFT);
2850 is_menu |= (cmd == GST_NAVIGATION_COMMAND_RIGHT);
2851 is_menu |= (cmd == GST_NAVIGATION_COMMAND_UP);
2852 is_menu |= (cmd == GST_NAVIGATION_COMMAND_DOWN);
2854 for (j = 0; j < G_N_ELEMENTS (navigation_buttons); j++) {
2855 if (navigation_buttons[j].cmd != cmd)
2858 gtk_widget_set_sensitive (navigation_buttons[j].button, TRUE);
2863 gtk_widget_set_sensitive (GTK_WIDGET (hscale), !is_menu);
2865 g_assert_not_reached ();
2868 gst_query_unref (query);
2877 connect_bus_signals (GstElement * pipeline)
2879 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
2881 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
2882 /* handle prepare-xwindow-id element message synchronously */
2883 gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler,
2887 gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
2888 gst_bus_enable_sync_message_emission (bus);
2890 g_signal_connect (bus, "message::state-changed",
2891 (GCallback) msg_state_changed, pipeline);
2892 g_signal_connect (bus, "message::segment-done", (GCallback) msg_segment_done,
2894 g_signal_connect (bus, "message::async-done", (GCallback) msg_async_done,
2897 g_signal_connect (bus, "message::new-clock", (GCallback) message_received,
2899 g_signal_connect (bus, "message::clock-lost", (GCallback) msg_clock_lost,
2901 g_signal_connect (bus, "message::error", (GCallback) message_received,
2903 g_signal_connect (bus, "message::warning", (GCallback) message_received,
2905 g_signal_connect (bus, "message::eos", (GCallback) msg_eos, pipeline);
2906 g_signal_connect (bus, "message::tag", (GCallback) message_received,
2908 g_signal_connect (bus, "message::element", (GCallback) message_received,
2910 g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
2912 g_signal_connect (bus, "message::buffering", (GCallback) msg_buffering,
2914 // g_signal_connect (bus, "message::step-done", (GCallback) msg_step_done,
2916 g_signal_connect (bus, "message::step-start", (GCallback) msg_step_done,
2918 g_signal_connect (bus, "sync-message::step-done",
2919 (GCallback) msg_sync_step_done, pipeline);
2920 g_signal_connect (bus, "message", (GCallback) msg, pipeline);
2922 gst_object_unref (bus);
2925 /* Return GList of paths described in location string */
2927 handle_wildcards (const gchar * location)
2930 gchar *path = g_path_get_dirname (location);
2931 gchar *pattern = g_path_get_basename (location);
2932 GPatternSpec *pspec = g_pattern_spec_new (pattern);
2933 GDir *dir = g_dir_open (path, 0, NULL);
2936 g_print ("matching %s from %s\n", pattern, path);
2939 g_print ("opening directory %s failed\n", path);
2943 while ((name = g_dir_read_name (dir)) != NULL) {
2944 if (g_pattern_match_string (pspec, name)) {
2945 res = g_list_append (res, g_strjoin ("/", path, name, NULL));
2946 g_print (" found clip %s\n", name);
2952 g_pattern_spec_free (pspec);
2960 delete_event_cb (void)
2962 stop_cb (NULL, NULL);
2967 print_usage (int argc, char **argv)
2971 g_print ("usage: %s <type> <filename>\n", argv[0]);
2972 g_print (" possible types:\n");
2974 for (i = 0; i < NUM_TYPES; i++) {
2975 g_print (" %d = %s\n", i, pipelines[i].name);
2980 main (int argc, char **argv)
2982 GtkWidget *window, *hbox, *vbox, *panel, *expander, *pb2vbox, *boxes,
2983 *flagtable, *boxes2, *step, *navigation, *colorbalance;
2984 GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
2985 GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
2986 GtkWidget *scrub_checkbox, *play_scrub_checkbox;
2987 GtkWidget *rate_label, *volume_label;
2988 GOptionEntry options[] = {
2989 {"audiosink", '\0', 0, G_OPTION_ARG_STRING, &opt_audiosink_str,
2990 "audio sink to use (default: " DEFAULT_AUDIOSINK ")", NULL},
2991 {"stats", 's', 0, G_OPTION_ARG_NONE, &stats,
2992 "Show pad stats", NULL},
2993 {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek,
2994 "Seek on elements instead of pads", NULL},
2995 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
2996 "Verbose properties", NULL},
2997 {"videosink", '\0', 0, G_OPTION_ARG_STRING, &opt_videosink_str,
2998 "video sink to use (default: " DEFAULT_VIDEOSINK ")", NULL},
3001 GOptionContext *ctx;
3004 #if !GLIB_CHECK_VERSION (2, 31, 0)
3005 if (!g_thread_supported ())
3006 g_thread_init (NULL);
3009 ctx = g_option_context_new ("- test seeking in gsteamer");
3010 g_option_context_add_main_entries (ctx, options, NULL);
3011 g_option_context_add_group (ctx, gst_init_get_option_group ());
3012 g_option_context_add_group (ctx, gtk_get_option_group (TRUE));
3014 if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
3015 g_print ("Error initializing: %s\n", err->message);
3019 if (opt_audiosink_str == NULL)
3020 opt_audiosink_str = g_strdup (DEFAULT_AUDIOSINK);
3022 if (opt_videosink_str == NULL)
3023 opt_videosink_str = g_strdup (DEFAULT_VIDEOSINK);
3025 GST_DEBUG_CATEGORY_INIT (seek_debug, "seek", 0, "seek example");
3028 print_usage (argc, argv);
3032 pipeline_type = atoi (argv[1]);
3034 if (pipeline_type < 0 || pipeline_type >= NUM_TYPES) {
3035 print_usage (argc, argv);
3039 pipeline_spec = argv[2];
3041 if (g_path_is_absolute (pipeline_spec) &&
3042 (g_strrstr (pipeline_spec, "*") != NULL ||
3043 g_strrstr (pipeline_spec, "?") != NULL)) {
3044 paths = handle_wildcards (pipeline_spec);
3046 paths = g_list_prepend (paths, g_strdup (pipeline_spec));
3050 g_print ("opening %s failed\n", pipeline_spec);
3056 pipeline = pipelines[pipeline_type].func ((gchar *) l->data);
3057 g_assert (pipeline);
3059 /* initialize gui elements ... */
3060 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3061 video_window = gtk_drawing_area_new ();
3062 g_signal_connect (video_window, "draw", G_CALLBACK (draw_cb), NULL);
3063 g_signal_connect (video_window, "realize", G_CALLBACK (realize_cb), NULL);
3064 g_signal_connect (video_window, "button-press-event",
3065 G_CALLBACK (button_press_cb), NULL);
3066 g_signal_connect (video_window, "button-release-event",
3067 G_CALLBACK (button_release_cb), NULL);
3068 g_signal_connect (video_window, "key-press-event", G_CALLBACK (key_press_cb),
3070 g_signal_connect (video_window, "key-release-event",
3071 G_CALLBACK (key_release_cb), NULL);
3072 g_signal_connect (video_window, "motion-notify-event",
3073 G_CALLBACK (motion_notify_cb), NULL);
3074 gtk_widget_set_can_focus (video_window, TRUE);
3075 gtk_widget_set_double_buffered (video_window, FALSE);
3076 gtk_widget_add_events (video_window,
3077 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3078 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
3080 statusbar = gtk_statusbar_new ();
3081 status_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "seek");
3082 gtk_statusbar_push (GTK_STATUSBAR (statusbar), status_id, "Stopped");
3083 hbox = gtk_hbox_new (FALSE, 0);
3084 vbox = gtk_vbox_new (FALSE, 0);
3085 flagtable = gtk_table_new (4, 2, FALSE);
3086 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
3088 /* media controls */
3089 play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
3090 pause_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PAUSE);
3091 stop_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
3094 accurate_checkbox = gtk_check_button_new_with_label ("Accurate Seek");
3095 key_checkbox = gtk_check_button_new_with_label ("Key-unit Seek");
3096 loop_checkbox = gtk_check_button_new_with_label ("Loop");
3097 flush_checkbox = gtk_check_button_new_with_label ("Flush");
3098 scrub_checkbox = gtk_check_button_new_with_label ("Scrub");
3099 play_scrub_checkbox = gtk_check_button_new_with_label ("Play Scrub");
3100 skip_checkbox = gtk_check_button_new_with_label ("Play Skip");
3101 rate_spinbutton = gtk_spin_button_new_with_range (-100, 100, 0.1);
3102 gtk_spin_button_set_digits (GTK_SPIN_BUTTON (rate_spinbutton), 3);
3103 rate_label = gtk_label_new ("Rate");
3105 gtk_widget_set_tooltip_text (accurate_checkbox,
3106 "accurate position is requested, this might be considerably slower for some formats");
3107 gtk_widget_set_tooltip_text (key_checkbox,
3108 "seek to the nearest keyframe. This might be faster but less accurate");
3109 gtk_widget_set_tooltip_text (loop_checkbox, "loop playback");
3110 gtk_widget_set_tooltip_text (flush_checkbox, "flush pipeline after seeking");
3111 gtk_widget_set_tooltip_text (rate_spinbutton, "define the playback rate, "
3112 "negative value trigger reverse playback");
3113 gtk_widget_set_tooltip_text (scrub_checkbox, "show images while seeking");
3114 gtk_widget_set_tooltip_text (play_scrub_checkbox, "play video while seeking");
3115 gtk_widget_set_tooltip_text (skip_checkbox,
3116 "Skip frames while playing at high frame rates");
3118 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (flush_checkbox), TRUE);
3119 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scrub_checkbox), TRUE);
3121 gtk_spin_button_set_value (GTK_SPIN_BUTTON (rate_spinbutton), rate);
3127 step = gtk_expander_new ("step options");
3128 hbox = gtk_hbox_new (FALSE, 0);
3130 format_combo = gtk_combo_box_text_new ();
3131 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
3133 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format_combo),
3135 gtk_combo_box_set_active (GTK_COMBO_BOX (format_combo), 0);
3136 gtk_box_pack_start (GTK_BOX (hbox), format_combo, FALSE, FALSE, 2);
3138 step_amount_spinbutton = gtk_spin_button_new_with_range (1, 1000, 1);
3139 gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_amount_spinbutton), 0);
3140 gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_amount_spinbutton), 1.0);
3141 gtk_box_pack_start (GTK_BOX (hbox), step_amount_spinbutton, FALSE, FALSE,
3144 step_rate_spinbutton = gtk_spin_button_new_with_range (0.0, 100, 0.1);
3145 gtk_spin_button_set_digits (GTK_SPIN_BUTTON (step_rate_spinbutton), 3);
3146 gtk_spin_button_set_value (GTK_SPIN_BUTTON (step_rate_spinbutton), 1.0);
3147 gtk_box_pack_start (GTK_BOX (hbox), step_rate_spinbutton, FALSE, FALSE, 2);
3149 step_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_FORWARD);
3150 gtk_button_set_label (GTK_BUTTON (step_button), "Step");
3151 gtk_box_pack_start (GTK_BOX (hbox), step_button, FALSE, FALSE, 2);
3153 g_signal_connect (G_OBJECT (step_button), "clicked", G_CALLBACK (step_cb),
3157 shuttle_checkbox = gtk_check_button_new_with_label ("Shuttle");
3158 gtk_box_pack_start (GTK_BOX (hbox), shuttle_checkbox, FALSE, FALSE, 2);
3159 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shuttle_checkbox), FALSE);
3160 g_signal_connect (shuttle_checkbox, "toggled", G_CALLBACK (shuttle_toggled),
3163 shuttle_adjustment =
3164 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, -3.00, 4.0, 0.1, 1.0, 1.0));
3165 shuttle_hscale = gtk_hscale_new (shuttle_adjustment);
3166 gtk_scale_set_digits (GTK_SCALE (shuttle_hscale), 2);
3167 gtk_scale_set_value_pos (GTK_SCALE (shuttle_hscale), GTK_POS_TOP);
3168 g_signal_connect (shuttle_hscale, "value-changed",
3169 G_CALLBACK (shuttle_value_changed), pipeline);
3170 g_signal_connect (shuttle_hscale, "format_value",
3171 G_CALLBACK (shuttle_format_value), pipeline);
3173 gtk_box_pack_start (GTK_BOX (hbox), shuttle_hscale, TRUE, TRUE, 2);
3175 gtk_container_add (GTK_CONTAINER (step), hbox);
3178 /* navigation command expander */
3180 GtkWidget *navigation_button;
3184 navigation = gtk_expander_new ("navigation commands");
3185 grid = gtk_grid_new ();
3186 gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
3187 gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
3188 gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
3189 gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
3191 navigation_button = gtk_button_new_with_label ("Menu 1");
3192 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3193 G_CALLBACK (navigation_cmd_cb),
3194 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU1));
3195 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3196 gtk_widget_set_sensitive (navigation_button, FALSE);
3197 gtk_widget_set_tooltip_text (navigation_button, "DVD Menu");
3198 navigation_buttons[i].button = navigation_button;
3199 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU1;
3201 navigation_button = gtk_button_new_with_label ("Menu 2");
3202 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3203 G_CALLBACK (navigation_cmd_cb),
3204 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU2));
3205 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3206 gtk_widget_set_sensitive (navigation_button, FALSE);
3207 gtk_widget_set_tooltip_text (navigation_button, "DVD Title Menu");
3208 navigation_buttons[i].button = navigation_button;
3209 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU2;
3211 navigation_button = gtk_button_new_with_label ("Menu 3");
3212 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3213 G_CALLBACK (navigation_cmd_cb),
3214 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU3));
3215 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3216 gtk_widget_set_sensitive (navigation_button, FALSE);
3217 gtk_widget_set_tooltip_text (navigation_button, "DVD Root Menu");
3218 navigation_buttons[i].button = navigation_button;
3219 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU3;
3221 navigation_button = gtk_button_new_with_label ("Menu 4");
3222 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3223 G_CALLBACK (navigation_cmd_cb),
3224 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU4));
3225 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3226 gtk_widget_set_sensitive (navigation_button, FALSE);
3227 gtk_widget_set_tooltip_text (navigation_button, "DVD Subpicture Menu");
3228 navigation_buttons[i].button = navigation_button;
3229 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU4;
3231 navigation_button = gtk_button_new_with_label ("Menu 5");
3232 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3233 G_CALLBACK (navigation_cmd_cb),
3234 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU5));
3235 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3236 gtk_widget_set_sensitive (navigation_button, FALSE);
3237 gtk_widget_set_tooltip_text (navigation_button, "DVD Audio Menu");
3238 navigation_buttons[i].button = navigation_button;
3239 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU5;
3241 navigation_button = gtk_button_new_with_label ("Menu 6");
3242 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3243 G_CALLBACK (navigation_cmd_cb),
3244 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU6));
3245 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3246 gtk_widget_set_sensitive (navigation_button, FALSE);
3247 gtk_widget_set_tooltip_text (navigation_button, "DVD Angle Menu");
3248 navigation_buttons[i].button = navigation_button;
3249 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU6;
3251 navigation_button = gtk_button_new_with_label ("Menu 7");
3252 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3253 G_CALLBACK (navigation_cmd_cb),
3254 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_MENU7));
3255 gtk_grid_attach (GTK_GRID (grid), navigation_button, i, 0, 1, 1);
3256 gtk_widget_set_sensitive (navigation_button, FALSE);
3257 gtk_widget_set_tooltip_text (navigation_button, "DVD Chapter Menu");
3258 navigation_buttons[i].button = navigation_button;
3259 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_MENU7;
3261 navigation_button = gtk_button_new_with_label ("Left");
3262 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3263 G_CALLBACK (navigation_cmd_cb),
3264 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_LEFT));
3265 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3266 gtk_widget_set_sensitive (navigation_button, FALSE);
3267 navigation_buttons[i].button = navigation_button;
3268 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_LEFT;
3270 navigation_button = gtk_button_new_with_label ("Right");
3271 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3272 G_CALLBACK (navigation_cmd_cb),
3273 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_RIGHT));
3274 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3275 gtk_widget_set_sensitive (navigation_button, FALSE);
3276 navigation_buttons[i].button = navigation_button;
3277 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_RIGHT;
3279 navigation_button = gtk_button_new_with_label ("Up");
3280 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3281 G_CALLBACK (navigation_cmd_cb),
3282 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_UP));
3283 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3284 gtk_widget_set_sensitive (navigation_button, FALSE);
3285 navigation_buttons[i].button = navigation_button;
3286 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_UP;
3288 navigation_button = gtk_button_new_with_label ("Down");
3289 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3290 G_CALLBACK (navigation_cmd_cb),
3291 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_DOWN));
3292 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3293 gtk_widget_set_sensitive (navigation_button, FALSE);
3294 navigation_buttons[i].button = navigation_button;
3295 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_DOWN;
3297 navigation_button = gtk_button_new_with_label ("Activate");
3298 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3299 G_CALLBACK (navigation_cmd_cb),
3300 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_ACTIVATE));
3301 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3302 gtk_widget_set_sensitive (navigation_button, FALSE);
3303 navigation_buttons[i].button = navigation_button;
3304 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_ACTIVATE;
3306 navigation_button = gtk_button_new_with_label ("Prev. Angle");
3307 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3308 G_CALLBACK (navigation_cmd_cb),
3309 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_PREV_ANGLE));
3310 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3311 gtk_widget_set_sensitive (navigation_button, FALSE);
3312 navigation_buttons[i].button = navigation_button;
3313 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_PREV_ANGLE;
3315 navigation_button = gtk_button_new_with_label ("Next. Angle");
3316 g_signal_connect (G_OBJECT (navigation_button), "clicked",
3317 G_CALLBACK (navigation_cmd_cb),
3318 GINT_TO_POINTER (GST_NAVIGATION_COMMAND_NEXT_ANGLE));
3319 gtk_grid_attach (GTK_GRID (grid), navigation_button, i - 7, 1, 1, 1);
3320 gtk_widget_set_sensitive (navigation_button, FALSE);
3321 navigation_buttons[i].button = navigation_button;
3322 navigation_buttons[i++].cmd = GST_NAVIGATION_COMMAND_NEXT_ANGLE;
3324 gtk_container_add (GTK_CONTAINER (navigation), grid);
3327 /* colorbalance expander */
3329 GtkWidget *vbox, *frame;
3331 colorbalance = gtk_expander_new ("color balance options");
3332 vbox = gtk_vbox_new (FALSE, 0);
3334 /* contrast scale */
3335 frame = gtk_frame_new ("Contrast");
3337 GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3338 contrast_scale = gtk_hscale_new (adjustment);
3339 gtk_scale_set_draw_value (GTK_SCALE (contrast_scale), FALSE);
3340 g_signal_connect (contrast_scale, "value-changed",
3341 G_CALLBACK (colorbalance_value_changed), (gpointer) "CONTRAST");
3342 gtk_container_add (GTK_CONTAINER (frame), contrast_scale);
3343 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3345 /* brightness scale */
3346 frame = gtk_frame_new ("Brightness");
3348 GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3349 brightness_scale = gtk_hscale_new (adjustment);
3350 gtk_scale_set_draw_value (GTK_SCALE (brightness_scale), FALSE);
3351 g_signal_connect (brightness_scale, "value-changed",
3352 G_CALLBACK (colorbalance_value_changed), (gpointer) "BRIGHTNESS");
3353 gtk_container_add (GTK_CONTAINER (frame), brightness_scale);
3354 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3357 frame = gtk_frame_new ("Hue");
3359 GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3360 hue_scale = gtk_hscale_new (adjustment);
3361 gtk_scale_set_draw_value (GTK_SCALE (hue_scale), FALSE);
3362 g_signal_connect (hue_scale, "value-changed",
3363 G_CALLBACK (colorbalance_value_changed), (gpointer) "HUE");
3364 gtk_container_add (GTK_CONTAINER (frame), hue_scale);
3365 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3367 /* saturation scale */
3368 frame = gtk_frame_new ("Saturation");
3370 GTK_ADJUSTMENT (gtk_adjustment_new (50.0, 0.0, 101.0, 1.0, 1.0, 1.0));
3371 saturation_scale = gtk_hscale_new (adjustment);
3372 gtk_scale_set_draw_value (GTK_SCALE (saturation_scale), FALSE);
3373 g_signal_connect (saturation_scale, "value-changed",
3374 G_CALLBACK (colorbalance_value_changed), (gpointer) "SATURATION");
3375 gtk_container_add (GTK_CONTAINER (frame), saturation_scale);
3376 gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 2);
3378 gtk_container_add (GTK_CONTAINER (colorbalance), vbox);
3383 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.00, N_GRAD, 0.1, 1.0, 1.0));
3384 hscale = gtk_hscale_new (adjustment);
3385 gtk_scale_set_digits (GTK_SCALE (hscale), 2);
3386 gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
3387 gtk_range_set_show_fill_level (GTK_RANGE (hscale), TRUE);
3388 gtk_range_set_fill_level (GTK_RANGE (hscale), N_GRAD);
3390 g_signal_connect (hscale, "button_press_event", G_CALLBACK (start_seek),
3392 g_signal_connect (hscale, "button_release_event", G_CALLBACK (stop_seek),
3394 g_signal_connect (hscale, "format_value", G_CALLBACK (format_value),
3397 if (pipeline_type == 16) {
3398 /* the playbin2 panel controls for the video/audio/subtitle tracks */
3399 panel = gtk_hbox_new (FALSE, 0);
3400 video_combo = gtk_combo_box_text_new ();
3401 audio_combo = gtk_combo_box_text_new ();
3402 text_combo = gtk_combo_box_text_new ();
3403 gtk_widget_set_sensitive (video_combo, FALSE);
3404 gtk_widget_set_sensitive (audio_combo, FALSE);
3405 gtk_widget_set_sensitive (text_combo, FALSE);
3406 gtk_box_pack_start (GTK_BOX (panel), video_combo, TRUE, TRUE, 2);
3407 gtk_box_pack_start (GTK_BOX (panel), audio_combo, TRUE, TRUE, 2);
3408 gtk_box_pack_start (GTK_BOX (panel), text_combo, TRUE, TRUE, 2);
3409 g_signal_connect (G_OBJECT (video_combo), "changed",
3410 G_CALLBACK (video_combo_cb), pipeline);
3411 g_signal_connect (G_OBJECT (audio_combo), "changed",
3412 G_CALLBACK (audio_combo_cb), pipeline);
3413 g_signal_connect (G_OBJECT (text_combo), "changed",
3414 G_CALLBACK (text_combo_cb), pipeline);
3415 /* playbin2 panel for flag checkboxes and volume/mute */
3416 boxes = gtk_hbox_new (FALSE, 0);
3417 vis_checkbox = gtk_check_button_new_with_label ("Vis");
3418 video_checkbox = gtk_check_button_new_with_label ("Video");
3419 audio_checkbox = gtk_check_button_new_with_label ("Audio");
3420 text_checkbox = gtk_check_button_new_with_label ("Text");
3421 mute_checkbox = gtk_check_button_new_with_label ("Mute");
3422 download_checkbox = gtk_check_button_new_with_label ("Download");
3423 buffer_checkbox = gtk_check_button_new_with_label ("Buffer");
3424 volume_label = gtk_label_new ("Volume");
3425 volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
3426 gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
3427 gtk_box_pack_start (GTK_BOX (boxes), video_checkbox, TRUE, TRUE, 2);
3428 gtk_box_pack_start (GTK_BOX (boxes), audio_checkbox, TRUE, TRUE, 2);
3429 gtk_box_pack_start (GTK_BOX (boxes), text_checkbox, TRUE, TRUE, 2);
3430 gtk_box_pack_start (GTK_BOX (boxes), vis_checkbox, TRUE, TRUE, 2);
3431 gtk_box_pack_start (GTK_BOX (boxes), mute_checkbox, TRUE, TRUE, 2);
3432 gtk_box_pack_start (GTK_BOX (boxes), download_checkbox, TRUE, TRUE, 2);
3433 gtk_box_pack_start (GTK_BOX (boxes), buffer_checkbox, TRUE, TRUE, 2);
3434 gtk_box_pack_start (GTK_BOX (boxes), volume_label, TRUE, TRUE, 2);
3435 gtk_box_pack_start (GTK_BOX (boxes), volume_spinbutton, TRUE, TRUE, 2);
3436 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vis_checkbox), FALSE);
3437 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (audio_checkbox), TRUE);
3438 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (video_checkbox), TRUE);
3439 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_checkbox), TRUE);
3440 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mute_checkbox), FALSE);
3441 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (download_checkbox), FALSE);
3442 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (buffer_checkbox), FALSE);
3443 g_signal_connect (G_OBJECT (vis_checkbox), "toggled",
3444 G_CALLBACK (vis_toggle_cb), pipeline);
3445 g_signal_connect (G_OBJECT (audio_checkbox), "toggled",
3446 G_CALLBACK (audio_toggle_cb), pipeline);
3447 g_signal_connect (G_OBJECT (video_checkbox), "toggled",
3448 G_CALLBACK (video_toggle_cb), pipeline);
3449 g_signal_connect (G_OBJECT (text_checkbox), "toggled",
3450 G_CALLBACK (text_toggle_cb), pipeline);
3451 g_signal_connect (G_OBJECT (mute_checkbox), "toggled",
3452 G_CALLBACK (mute_toggle_cb), pipeline);
3453 g_signal_connect (G_OBJECT (download_checkbox), "toggled",
3454 G_CALLBACK (download_toggle_cb), pipeline);
3455 g_signal_connect (G_OBJECT (buffer_checkbox), "toggled",
3456 G_CALLBACK (buffer_toggle_cb), pipeline);
3457 g_signal_connect (G_OBJECT (volume_spinbutton), "value-changed",
3458 G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
3459 /* playbin2 panel for snapshot */
3460 boxes2 = gtk_hbox_new (FALSE, 0);
3461 shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
3462 gtk_widget_set_tooltip_text (shot_button,
3463 "save a screenshot .png in the current directory");
3464 g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
3466 vis_combo = gtk_combo_box_text_new ();
3467 g_signal_connect (G_OBJECT (vis_combo), "changed",
3468 G_CALLBACK (vis_combo_cb), pipeline);
3469 gtk_widget_set_sensitive (vis_combo, FALSE);
3470 gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
3471 gtk_box_pack_start (GTK_BOX (boxes2), vis_combo, TRUE, TRUE, 2);
3473 /* fill the vis combo box and the array of factories */
3474 init_visualization_features ();
3476 panel = boxes = boxes2 = NULL;
3479 /* do the packing stuff ... */
3480 gtk_window_set_default_size (GTK_WINDOW (window), 250, 96);
3481 /* FIXME: can we avoid this for audio only? */
3482 gtk_widget_set_size_request (GTK_WIDGET (video_window), -1,
3483 DEFAULT_VIDEO_HEIGHT);
3484 gtk_container_add (GTK_CONTAINER (window), vbox);
3485 gtk_box_pack_start (GTK_BOX (vbox), video_window, TRUE, TRUE, 2);
3486 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 2);
3487 gtk_box_pack_start (GTK_BOX (hbox), play_button, FALSE, FALSE, 2);
3488 gtk_box_pack_start (GTK_BOX (hbox), pause_button, FALSE, FALSE, 2);
3489 gtk_box_pack_start (GTK_BOX (hbox), stop_button, FALSE, FALSE, 2);
3490 gtk_box_pack_start (GTK_BOX (hbox), flagtable, FALSE, FALSE, 2);
3491 gtk_table_attach_defaults (GTK_TABLE (flagtable), accurate_checkbox, 0, 1, 0,
3493 gtk_table_attach_defaults (GTK_TABLE (flagtable), flush_checkbox, 1, 2, 0, 1);
3494 gtk_table_attach_defaults (GTK_TABLE (flagtable), loop_checkbox, 2, 3, 0, 1);
3495 gtk_table_attach_defaults (GTK_TABLE (flagtable), key_checkbox, 0, 1, 1, 2);
3496 gtk_table_attach_defaults (GTK_TABLE (flagtable), scrub_checkbox, 1, 2, 1, 2);
3497 gtk_table_attach_defaults (GTK_TABLE (flagtable), play_scrub_checkbox, 2, 3,
3499 gtk_table_attach_defaults (GTK_TABLE (flagtable), skip_checkbox, 3, 4, 0, 1);
3500 gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 4, 5, 0, 1);
3501 gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 4, 5, 1,
3503 if (panel && boxes && boxes2) {
3504 expander = gtk_expander_new ("playbin2 options");
3505 pb2vbox = gtk_vbox_new (FALSE, 0);
3506 gtk_box_pack_start (GTK_BOX (pb2vbox), panel, FALSE, FALSE, 2);
3507 gtk_box_pack_start (GTK_BOX (pb2vbox), boxes, FALSE, FALSE, 2);
3508 gtk_box_pack_start (GTK_BOX (pb2vbox), boxes2, FALSE, FALSE, 2);
3509 gtk_container_add (GTK_CONTAINER (expander), pb2vbox);
3510 gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 2);
3512 gtk_box_pack_start (GTK_BOX (vbox), step, FALSE, FALSE, 2);
3513 gtk_box_pack_start (GTK_BOX (vbox), navigation, FALSE, FALSE, 2);
3514 gtk_box_pack_start (GTK_BOX (vbox), colorbalance, FALSE, FALSE, 2);
3515 gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, FALSE, 2);
3516 gtk_box_pack_start (GTK_BOX (vbox), hscale, FALSE, FALSE, 2);
3517 gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 2);
3519 /* connect things ... */
3520 g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_cb),
3522 g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_cb),
3524 g_signal_connect (G_OBJECT (stop_button), "clicked", G_CALLBACK (stop_cb),
3526 g_signal_connect (G_OBJECT (accurate_checkbox), "toggled",
3527 G_CALLBACK (accurate_toggle_cb), pipeline);
3528 g_signal_connect (G_OBJECT (key_checkbox), "toggled",
3529 G_CALLBACK (key_toggle_cb), pipeline);
3530 g_signal_connect (G_OBJECT (loop_checkbox), "toggled",
3531 G_CALLBACK (loop_toggle_cb), pipeline);
3532 g_signal_connect (G_OBJECT (flush_checkbox), "toggled",
3533 G_CALLBACK (flush_toggle_cb), pipeline);
3534 g_signal_connect (G_OBJECT (scrub_checkbox), "toggled",
3535 G_CALLBACK (scrub_toggle_cb), pipeline);
3536 g_signal_connect (G_OBJECT (play_scrub_checkbox), "toggled",
3537 G_CALLBACK (play_scrub_toggle_cb), pipeline);
3538 g_signal_connect (G_OBJECT (skip_checkbox), "toggled",
3539 G_CALLBACK (skip_toggle_cb), pipeline);
3540 g_signal_connect (G_OBJECT (rate_spinbutton), "value-changed",
3541 G_CALLBACK (rate_spinbutton_changed_cb), pipeline);
3543 g_signal_connect (G_OBJECT (window), "delete-event", delete_event_cb, NULL);
3546 gtk_widget_show_all (window);
3548 /* realize window now so that the video window gets created and we can
3549 * obtain its XID before the pipeline is started up and the videosink
3550 * asks for the XID of the window to render onto */
3551 gtk_widget_realize (window);
3553 #if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
3554 /* we should have the XID now */
3555 g_assert (embed_xid != 0);
3559 g_signal_connect (pipeline, "deep_notify",
3560 G_CALLBACK (gst_object_default_deep_notify), NULL);
3563 connect_bus_signals (pipeline);
3566 g_print ("NULL pipeline\n");
3567 gst_element_set_state (pipeline, GST_STATE_NULL);
3569 if (xoverlay_element)
3570 gst_object_unref (xoverlay_element);
3571 if (navigation_element)
3572 gst_object_unref (navigation_element);
3574 g_print ("free pipeline\n");
3575 gst_object_unref (pipeline);
3577 g_list_foreach (paths, (GFunc) g_free, NULL);
3578 g_list_free (paths);