2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
31 #include <gst/audio/streamvolume.h>
32 #include <gst/video/colorbalance.h>
33 #include <gst/video/videooverlay.h>
34 #include <gst/video/navigation.h>
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
44 #define VOLUME_MAX_DOUBLE 10.0
46 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
47 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
53 * GstPlaySinkSendEventMode:
54 * @MODE_DEFAULT: default GstBin's send_event handling
55 * @MODE_FIRST: send event only to the first sink that return true
57 * Send event handling to use
63 } GstPlaySinkSendEventMode;
66 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
68 gst_play_sink_send_event_mode_get_type (void)
70 static GType gtype = 0;
73 static const GEnumValue values[] = {
74 {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
76 {MODE_FIRST, "Sends the event to sinks until the first one handles it",
81 gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
86 /* holds the common data fields for the audio and video pipelines. We keep them
87 * in a structure to more easily have all the info available. */
90 GstPlaySink *playsink;
103 GstElement *volume; /* element with the volume property */
104 gboolean sink_volume; /* if the volume was provided by the sink */
105 GstElement *mute; /* element with the mute property */
107 GstElement *ts_offset;
113 GstPad *sinkpad, *srcpad;
115 GstElement *deinterlace;
116 } GstPlayVideoDeinterlaceChain;
126 GstElement *ts_offset;
135 GstElement *resample;
136 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
137 GstPad *vissinkpad; /* visualisation sinkpad, */
139 GstPad *vissrcpad; /* visualisation srcpad, */
140 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
149 GstElement *identity;
151 GstPad *videosinkpad;
153 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
155 GstElement *sink; /* custom sink to receive subtitle buffers */
158 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
159 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
160 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
161 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
162 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
164 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
165 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
166 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
169 #define PENDING_FLAG_SET(playsink, flagtype) \
170 ((playsink->pending_blocked_pads) |= (1 << flagtype))
171 #define PENDING_FLAG_UNSET(playsink, flagtype) \
172 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
173 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
174 ((playsink->pending_blocked_pads) & (1 << flagtype))
175 #define PENDING_VIDEO_BLOCK(playsink) \
176 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
177 #define PENDING_AUDIO_BLOCK(playsink) \
178 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
179 #define PENDING_TEXT_BLOCK(playsink) \
180 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
188 gboolean async_pending;
189 gboolean need_async_start;
193 GstStreamSynchronizer *stream_synchronizer;
196 GstPlayAudioChain *audiochain;
197 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
198 GstPlayVideoChain *videochain;
199 GstPlayVisChain *vischain;
200 GstPlayTextChain *textchain;
204 gboolean audio_pad_raw;
205 gboolean audio_pad_blocked;
206 GstPad *audio_srcpad_stream_synchronizer;
207 GstPad *audio_sinkpad_stream_synchronizer;
208 gulong audio_block_id;
210 GstElement *audio_tee;
211 GstPad *audio_tee_sink;
212 GstPad *audio_tee_asrc;
213 GstPad *audio_tee_vissrc;
216 gboolean video_pad_raw;
217 gboolean video_pad_blocked;
218 GstPad *video_srcpad_stream_synchronizer;
219 GstPad *video_sinkpad_stream_synchronizer;
220 gulong video_block_id;
223 gboolean text_pad_blocked;
224 GstPad *text_srcpad_stream_synchronizer;
225 GstPad *text_sinkpad_stream_synchronizer;
226 gulong text_block_id;
228 guint32 pending_blocked_pads;
231 GstElement *audio_sink;
232 GstElement *video_sink;
233 GstElement *visualisation;
234 GstElement *text_sink;
237 gchar *font_desc; /* font description */
238 gchar *subtitle_encoding; /* subtitle encoding */
239 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
241 gboolean volume_changed; /* volume/mute changed while no audiochain */
242 gboolean mute_changed; /* ... has been created yet */
244 GstPlaySinkSendEventMode send_event_mode;
246 /* videooverlay proxy interface */
247 GstVideoOverlay *overlay_element; /* protected with LOCK */
248 gboolean overlay_handle_set;
249 guintptr overlay_handle;
250 gboolean overlay_render_rectangle_set;
251 gint overlay_x, overlay_y, overlay_width, overlay_height;
252 gboolean overlay_handle_events_set;
253 gboolean overlay_handle_events;
255 /* colorbalance proxy interface */
256 GstColorBalance *colorbalance_element;
257 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
258 gint colorbalance_values[4];
260 /* sending audio/video flushes break stream changes when the pipeline
261 * is paused and played again in 0.10 */
263 GstSegment video_segment;
264 gboolean video_custom_flush_finished;
265 gboolean video_ignore_wrong_state;
266 gboolean video_pending_flush;
268 GstSegment audio_segment;
269 gboolean audio_custom_flush_finished;
270 gboolean audio_ignore_wrong_state;
271 gboolean audio_pending_flush;
274 GstSegment text_segment;
275 gboolean text_custom_flush_finished;
276 gboolean text_ignore_wrong_state;
277 gboolean text_pending_flush;
280 struct _GstPlaySinkClass
282 GstBinClass parent_class;
284 gboolean (*reconfigure) (GstPlaySink * playsink);
286 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
290 static GstStaticPadTemplate audiotemplate =
291 GST_STATIC_PAD_TEMPLATE ("audio_sink",
294 GST_STATIC_CAPS_ANY);
295 static GstStaticPadTemplate videotemplate =
296 GST_STATIC_PAD_TEMPLATE ("video_sink",
299 GST_STATIC_CAPS_ANY);
300 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
303 GST_STATIC_CAPS_ANY);
305 /* FIXME 0.11: Remove */
306 static GstStaticPadTemplate audiorawtemplate =
307 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
310 GST_STATIC_CAPS_ANY);
311 static GstStaticPadTemplate videorawtemplate =
312 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
315 GST_STATIC_CAPS_ANY);
326 PROP_SUBTITLE_ENCODING,
333 PROP_SEND_EVENT_MODE,
343 static void gst_play_sink_dispose (GObject * object);
344 static void gst_play_sink_finalize (GObject * object);
345 static void gst_play_sink_set_property (GObject * object, guint prop_id,
346 const GValue * value, GParamSpec * spec);
347 static void gst_play_sink_get_property (GObject * object, guint prop_id,
348 GValue * value, GParamSpec * spec);
350 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
351 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
352 static void gst_play_sink_release_request_pad (GstElement * element,
354 static gboolean gst_play_sink_send_event (GstElement * element,
356 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
357 GstStateChange transition);
359 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
361 /* sending audio/video flushes break stream changes when the pipeline
362 * is paused and played again in 0.10 */
364 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
365 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
367 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
368 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
371 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
373 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
374 GstObject * parent, GstBuffer * buffer);
376 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
377 GstPlaySink * playsink);
378 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
379 GstPlaySink * playsink);
381 static void update_av_offset (GstPlaySink * playsink);
383 static GQuark _playsink_reset_segment_event_marker_id = 0;
385 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
387 static void gst_play_sink_overlay_init (gpointer g_iface,
388 gpointer g_iface_data);
389 static void gst_play_sink_navigation_init (gpointer g_iface,
390 gpointer g_iface_data);
391 static void gst_play_sink_colorbalance_init (gpointer g_iface,
392 gpointer g_iface_data);
395 _do_init (GType type)
397 static const GInterfaceInfo svol_info = {
400 static const GInterfaceInfo ov_info = {
401 gst_play_sink_overlay_init,
404 static const GInterfaceInfo nav_info = {
405 gst_play_sink_navigation_init,
408 static const GInterfaceInfo col_info = {
409 gst_play_sink_colorbalance_init,
413 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
414 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
415 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
416 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
419 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
420 _do_init (g_define_type_id));
423 gst_play_sink_class_init (GstPlaySinkClass * klass)
425 GObjectClass *gobject_klass;
426 GstElementClass *gstelement_klass;
427 GstBinClass *gstbin_klass;
429 gobject_klass = (GObjectClass *) klass;
430 gstelement_klass = (GstElementClass *) klass;
431 gstbin_klass = (GstBinClass *) klass;
433 gobject_klass->dispose = gst_play_sink_dispose;
434 gobject_klass->finalize = gst_play_sink_finalize;
435 gobject_klass->set_property = gst_play_sink_set_property;
436 gobject_klass->get_property = gst_play_sink_get_property;
442 * Control the behaviour of playsink.
444 g_object_class_install_property (gobject_klass, PROP_FLAGS,
445 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
446 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
447 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 * GstPlaySink:volume:
452 * Get or set the current audio stream volume. 1.0 means 100%,
453 * 0.0 means mute. This uses a linear volume scale.
456 g_object_class_install_property (gobject_klass, PROP_VOLUME,
457 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
458 0.0, VOLUME_MAX_DOUBLE, 1.0,
459 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
460 g_object_class_install_property (gobject_klass, PROP_MUTE,
461 g_param_spec_boolean ("mute", "Mute",
462 "Mute the audio channel without changing the volume", FALSE,
463 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
465 g_param_spec_string ("subtitle-font-desc",
466 "Subtitle font description",
467 "Pango font description of font "
468 "to be used for subtitle rendering", NULL,
469 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
470 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
471 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
472 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
473 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
474 "be checked for an encoding to use. If that is not set either, "
475 "ISO-8859-15 will be assumed.", NULL,
476 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
477 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
478 g_param_spec_object ("vis-plugin", "Vis plugin",
479 "the visualization element to use (NULL = default)",
480 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482 * GstPlaySink:sample:
484 * Get the currently rendered or prerolled sample in the video sink.
485 * The #GstCaps in the sample will describe the format of the buffer.
487 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
488 g_param_spec_boxed ("sample", "Sample",
489 "The last sample (NULL = no video available)",
490 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
492 * GstPlaySink:av-offset:
494 * Control the synchronisation offset between the audio and video streams.
495 * Positive values make the audio ahead of the video and negative values make
496 * the audio go behind the video.
500 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
501 g_param_spec_int64 ("av-offset", "AV Offset",
502 "The synchronisation offset between audio and video in nanoseconds",
503 G_MININT64, G_MAXINT64, 0,
504 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
507 * GstPlaySink:video-sink:
509 * Set the used video sink element. NULL will use the default sink. playsink
510 * must be in %GST_STATE_NULL
514 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
515 g_param_spec_object ("video-sink", "Video Sink",
516 "the video output element to use (NULL = default sink)",
517 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
519 * GstPlaySink:audio-sink:
521 * Set the used audio sink element. NULL will use the default sink. playsink
522 * must be in %GST_STATE_NULL
526 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
527 g_param_spec_object ("audio-sink", "Audio Sink",
528 "the audio output element to use (NULL = default sink)",
529 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
532 * GstPlaySink:text-sink:
534 * Set the used text sink element. NULL will use the default sink. playsink
535 * must be in %GST_STATE_NULL
539 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
540 g_param_spec_object ("text-sink", "Text sink",
541 "the text output element to use (NULL = default subtitleoverlay)",
542 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
545 * GstPlaySink::send-event-mode:
547 * Sets the handling method used for events received from send_event
548 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
549 * handling (push the event to all internal sinks).
553 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
554 g_param_spec_enum ("send-event-mode", "Send event mode",
555 "How to send events received in send_event function",
556 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
557 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
559 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
560 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
561 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
564 * GstPlaySink::convert-sample
565 * @playsink: a #GstPlaySink
566 * @caps: the target format of the sample
568 * Action signal to retrieve the currently playing video sample in the format
569 * specified by @caps.
570 * If @caps is %NULL, no conversion will be performed and this function is
571 * equivalent to the #GstPlaySink::sample property.
573 * Returns: a #GstSample of the current video sample converted to #caps.
574 * The caps in the sample will describe the final layout of the buffer data.
575 * %NULL is returned when no current sample can be retrieved or when the
578 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
579 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
580 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
581 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
583 gst_element_class_add_pad_template (gstelement_klass,
584 gst_static_pad_template_get (&audiorawtemplate));
585 gst_element_class_add_pad_template (gstelement_klass,
586 gst_static_pad_template_get (&audiotemplate));
587 gst_element_class_add_pad_template (gstelement_klass,
588 gst_static_pad_template_get (&videorawtemplate));
589 gst_element_class_add_pad_template (gstelement_klass,
590 gst_static_pad_template_get (&videotemplate));
591 gst_element_class_add_pad_template (gstelement_klass,
592 gst_static_pad_template_get (&texttemplate));
593 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
595 "Convenience sink for multiple streams",
596 "Wim Taymans <wim.taymans@gmail.com>");
598 gstelement_klass->change_state =
599 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
600 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
601 gstelement_klass->request_new_pad =
602 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
603 gstelement_klass->release_pad =
604 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
606 gstbin_klass->handle_message =
607 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
609 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
610 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
612 _playsink_reset_segment_event_marker_id =
613 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
617 gst_play_sink_init (GstPlaySink * playsink)
619 GstColorBalanceChannel *channel;
622 playsink->video_sink = NULL;
623 playsink->audio_sink = NULL;
624 playsink->visualisation = NULL;
625 playsink->text_sink = NULL;
626 playsink->volume = 1.0;
627 playsink->font_desc = NULL;
628 playsink->subtitle_encoding = NULL;
629 playsink->flags = DEFAULT_FLAGS;
630 playsink->send_event_mode = MODE_DEFAULT;
632 playsink->stream_synchronizer =
633 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
634 gst_bin_add (GST_BIN_CAST (playsink),
635 GST_ELEMENT_CAST (playsink->stream_synchronizer));
637 g_rec_mutex_init (&playsink->lock);
638 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
641 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
643 channel->label = g_strdup ("CONTRAST");
644 channel->min_value = -1000;
645 channel->max_value = 1000;
646 playsink->colorbalance_channels =
647 g_list_append (playsink->colorbalance_channels, channel);
648 playsink->colorbalance_values[0] = 0;
651 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
653 channel->label = g_strdup ("BRIGHTNESS");
654 channel->min_value = -1000;
655 channel->max_value = 1000;
656 playsink->colorbalance_channels =
657 g_list_append (playsink->colorbalance_channels, channel);
658 playsink->colorbalance_values[1] = 0;
661 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
663 channel->label = g_strdup ("HUE");
664 channel->min_value = -1000;
665 channel->max_value = 1000;
666 playsink->colorbalance_channels =
667 g_list_append (playsink->colorbalance_channels, channel);
668 playsink->colorbalance_values[2] = 0;
671 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
673 channel->label = g_strdup ("SATURATION");
674 channel->min_value = -1000;
675 channel->max_value = 1000;
676 playsink->colorbalance_channels =
677 g_list_append (playsink->colorbalance_channels, channel);
678 playsink->colorbalance_values[3] = 0;
682 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
686 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
689 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
695 free_chain (GstPlayChain * chain)
699 gst_object_unref (chain->bin);
705 gst_play_sink_dispose (GObject * object)
707 GstPlaySink *playsink;
709 playsink = GST_PLAY_SINK (object);
711 if (playsink->audio_sink != NULL) {
712 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
713 gst_object_unref (playsink->audio_sink);
714 playsink->audio_sink = NULL;
716 if (playsink->video_sink != NULL) {
717 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
718 gst_object_unref (playsink->video_sink);
719 playsink->video_sink = NULL;
721 if (playsink->visualisation != NULL) {
722 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
723 gst_object_unref (playsink->visualisation);
724 playsink->visualisation = NULL;
726 if (playsink->text_sink != NULL) {
727 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
728 gst_object_unref (playsink->text_sink);
729 playsink->text_sink = NULL;
732 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
733 playsink->videodeinterlacechain = NULL;
734 free_chain ((GstPlayChain *) playsink->videochain);
735 playsink->videochain = NULL;
736 free_chain ((GstPlayChain *) playsink->audiochain);
737 playsink->audiochain = NULL;
738 free_chain ((GstPlayChain *) playsink->vischain);
739 playsink->vischain = NULL;
740 free_chain ((GstPlayChain *) playsink->textchain);
741 playsink->textchain = NULL;
743 if (playsink->audio_tee_sink) {
744 gst_object_unref (playsink->audio_tee_sink);
745 playsink->audio_tee_sink = NULL;
748 if (playsink->audio_tee_vissrc) {
749 gst_element_release_request_pad (playsink->audio_tee,
750 playsink->audio_tee_vissrc);
751 gst_object_unref (playsink->audio_tee_vissrc);
752 playsink->audio_tee_vissrc = NULL;
755 if (playsink->audio_tee_asrc) {
756 gst_element_release_request_pad (playsink->audio_tee,
757 playsink->audio_tee_asrc);
758 gst_object_unref (playsink->audio_tee_asrc);
759 playsink->audio_tee_asrc = NULL;
762 g_free (playsink->font_desc);
763 playsink->font_desc = NULL;
765 g_free (playsink->subtitle_encoding);
766 playsink->subtitle_encoding = NULL;
768 playsink->stream_synchronizer = NULL;
770 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
772 g_list_free (playsink->colorbalance_channels);
773 playsink->colorbalance_channels = NULL;
775 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
779 gst_play_sink_finalize (GObject * object)
781 GstPlaySink *playsink;
783 playsink = GST_PLAY_SINK (object);
785 g_rec_mutex_clear (&playsink->lock);
787 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
791 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
794 GstElement **elem = NULL, *old = NULL;
796 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
798 GST_PLAY_SINK_LOCK (playsink);
800 case GST_PLAY_SINK_TYPE_AUDIO:
801 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
802 elem = &playsink->audio_sink;
804 case GST_PLAY_SINK_TYPE_VIDEO:
805 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
806 elem = &playsink->video_sink;
808 case GST_PLAY_SINK_TYPE_TEXT:
809 elem = &playsink->text_sink;
817 gst_object_ref (sink);
820 GST_PLAY_SINK_UNLOCK (playsink);
824 gst_element_set_state (old, GST_STATE_NULL);
825 gst_object_unref (old);
830 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
832 GstElement *result = NULL;
833 GstElement *elem = NULL, *chainp = NULL;
835 GST_PLAY_SINK_LOCK (playsink);
837 case GST_PLAY_SINK_TYPE_AUDIO:
838 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
840 GstPlayAudioChain *chain;
841 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
842 chainp = chain->sink;
843 elem = playsink->audio_sink;
846 case GST_PLAY_SINK_TYPE_VIDEO:
847 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
849 GstPlayVideoChain *chain;
850 if ((chain = (GstPlayVideoChain *) playsink->videochain))
851 chainp = chain->sink;
852 elem = playsink->video_sink;
855 case GST_PLAY_SINK_TYPE_TEXT:
857 GstPlayTextChain *chain;
858 if ((chain = (GstPlayTextChain *) playsink->textchain))
859 chainp = chain->sink;
860 elem = playsink->text_sink;
867 /* we have an active chain with a sink, get the sink */
868 result = gst_object_ref (chainp);
870 /* nothing found, return last configured sink */
871 if (result == NULL && elem)
872 result = gst_object_ref (elem);
873 GST_PLAY_SINK_UNLOCK (playsink);
878 static GstPadProbeReturn
879 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
882 GstPlaySink *playsink;
883 GstPlayVisChain *chain;
885 playsink = GST_PLAY_SINK (user_data);
887 GST_PLAY_SINK_LOCK (playsink);
888 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
889 /* now try to change the plugin in the running vis chain */
890 if (!(chain = (GstPlayVisChain *) playsink->vischain))
893 /* unlink the old plugin and unghost the pad */
894 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
895 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
897 /* set the old plugin to NULL and remove */
898 gst_element_set_state (chain->vis, GST_STATE_NULL);
899 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
901 /* add new plugin and set state to playing */
902 chain->vis = playsink->visualisation;
903 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
904 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
907 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
908 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
911 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
912 GST_PAD_LINK_CHECK_NOTHING);
913 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
917 GST_PLAY_SINK_UNLOCK (playsink);
919 /* remove the probe and unblock the pad */
920 return GST_PAD_PROBE_REMOVE;
924 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
926 GstPlayVisChain *chain;
928 /* setting NULL means creating the default vis plugin */
930 vis = gst_element_factory_make ("goom", "vis");
932 /* simply return if we don't have a vis plugin here */
936 GST_PLAY_SINK_LOCK (playsink);
937 /* first store the new vis */
938 if (playsink->visualisation)
939 gst_object_unref (playsink->visualisation);
941 gst_object_ref_sink (vis);
942 playsink->visualisation = vis;
944 /* now try to change the plugin in the running vis chain, if we have no chain,
945 * we don't bother, any future vis chain will be created with the new vis
947 if (!(chain = (GstPlayVisChain *) playsink->vischain))
950 /* block the pad, the next time the callback is called we can change the
951 * visualisation. It's possible that this never happens or that the pad was
952 * already blocked. If the callback never happens, we don't have new data so
953 * we don't need the new vis plugin. If the pad was already blocked, the
954 * function returns FALSE but the previous pad block will do the right thing
956 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
957 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
958 gst_play_sink_vis_blocked, playsink, NULL);
960 GST_PLAY_SINK_UNLOCK (playsink);
966 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
968 GstElement *result = NULL;
969 GstPlayVisChain *chain;
971 GST_PLAY_SINK_LOCK (playsink);
972 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
973 /* we have an active chain, get the sink */
975 result = gst_object_ref (chain->vis);
977 /* nothing found, return last configured sink */
978 if (result == NULL && playsink->visualisation)
979 result = gst_object_ref (playsink->visualisation);
980 GST_PLAY_SINK_UNLOCK (playsink);
986 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
988 GstPlayAudioChain *chain;
990 GST_PLAY_SINK_LOCK (playsink);
991 playsink->volume = volume;
992 chain = (GstPlayAudioChain *) playsink->audiochain;
993 if (chain && chain->volume) {
994 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
995 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
996 chain->mute, volume, playsink->mute);
997 /* if there is a mute element or we are not muted, set the volume */
998 if (chain->mute || !playsink->mute)
999 g_object_set (chain->volume, "volume", volume, NULL);
1001 GST_LOG_OBJECT (playsink, "no volume element");
1002 playsink->volume_changed = TRUE;
1004 GST_PLAY_SINK_UNLOCK (playsink);
1008 gst_play_sink_get_volume (GstPlaySink * playsink)
1011 GstPlayAudioChain *chain;
1013 GST_PLAY_SINK_LOCK (playsink);
1014 chain = (GstPlayAudioChain *) playsink->audiochain;
1015 result = playsink->volume;
1016 if (chain && chain->volume) {
1017 if (chain->mute || !playsink->mute) {
1018 g_object_get (chain->volume, "volume", &result, NULL);
1019 playsink->volume = result;
1022 GST_PLAY_SINK_UNLOCK (playsink);
1028 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1030 GstPlayAudioChain *chain;
1032 GST_PLAY_SINK_LOCK (playsink);
1033 playsink->mute = mute;
1034 chain = (GstPlayAudioChain *) playsink->audiochain;
1037 g_object_set (chain->mute, "mute", mute, NULL);
1038 } else if (chain->volume) {
1040 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1042 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1046 playsink->mute_changed = TRUE;
1048 GST_PLAY_SINK_UNLOCK (playsink);
1052 gst_play_sink_get_mute (GstPlaySink * playsink)
1055 GstPlayAudioChain *chain;
1057 GST_PLAY_SINK_LOCK (playsink);
1058 chain = (GstPlayAudioChain *) playsink->audiochain;
1059 if (chain && chain->mute) {
1060 g_object_get (chain->mute, "mute", &result, NULL);
1061 playsink->mute = result;
1063 result = playsink->mute;
1065 GST_PLAY_SINK_UNLOCK (playsink);
1071 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1075 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1076 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1080 add_chain (GstPlayChain * chain, gboolean add)
1082 if (chain->added == add)
1086 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1088 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1089 /* we don't want to lose our sink status */
1090 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1099 activate_chain (GstPlayChain * chain, gboolean activate)
1103 if (chain->activated == activate)
1106 GST_OBJECT_LOCK (chain->playsink);
1107 state = GST_STATE_TARGET (chain->playsink);
1108 GST_OBJECT_UNLOCK (chain->playsink);
1111 gst_element_set_state (chain->bin, state);
1113 gst_element_set_state (chain->bin, GST_STATE_NULL);
1115 chain->activated = activate;
1121 element_is_sink (GstElement * element)
1125 GST_OBJECT_LOCK (element);
1126 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1127 GST_OBJECT_UNLOCK (element);
1129 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1134 element_has_property (GstElement * element, const gchar * pname, GType type)
1138 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1140 if (pspec == NULL) {
1141 GST_DEBUG_OBJECT (element, "no %s property", pname);
1145 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1146 g_type_is_a (pspec->value_type, type)) {
1147 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1148 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1152 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1153 "and we expected it to be of type %s", pname,
1154 g_type_name (pspec->value_type), g_type_name (type));
1161 const gchar *prop_name;
1164 } FindPropertyHelper;
1167 find_property (const GValue * item, FindPropertyHelper * helper)
1169 GstElement *element = g_value_get_object (item);
1170 if (helper->need_sink && !element_is_sink (element)) {
1174 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1178 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1179 (helper->need_sink) ? "sink" : "element");
1180 return 0; /* keep it */
1183 /* FIXME: why not move these functions into core? */
1184 /* find a sink in the hierarchy with a property named @name. This function does
1185 * not increase the refcount of the returned object and thus remains valid as
1186 * long as the bin is valid. */
1188 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1189 const gchar * name, GType expected_type)
1191 GstElement *result = NULL;
1194 if (element_has_property (obj, name, expected_type)) {
1196 } else if (GST_IS_BIN (obj)) {
1198 GValue item = { 0, };
1199 FindPropertyHelper helper = { name, expected_type, TRUE };
1201 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1202 found = gst_iterator_find_custom (it,
1203 (GCompareFunc) find_property, &item, &helper);
1204 gst_iterator_free (it);
1206 result = g_value_get_object (&item);
1207 /* we don't need the extra ref */
1208 g_value_unset (&item);
1214 /* find an object in the hierarchy with a property named @name */
1216 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1217 const gchar * name, GType expected_type)
1219 GstElement *result = NULL;
1222 if (GST_IS_BIN (obj)) {
1224 GValue item = { 0, };
1225 FindPropertyHelper helper = { name, expected_type, FALSE };
1227 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1228 found = gst_iterator_find_custom (it,
1229 (GCompareFunc) find_property, &item, &helper);
1230 gst_iterator_free (it);
1232 result = g_value_dup_object (&item);
1233 g_value_unset (&item);
1236 if (element_has_property (obj, name, expected_type)) {
1238 gst_object_ref (obj);
1245 do_async_start (GstPlaySink * playsink)
1247 GstMessage *message;
1249 if (!playsink->need_async_start) {
1250 GST_INFO_OBJECT (playsink, "no async_start needed");
1254 playsink->async_pending = TRUE;
1256 GST_INFO_OBJECT (playsink, "Sending async_start message");
1257 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1258 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1259 (playsink), message);
1263 do_async_done (GstPlaySink * playsink)
1265 GstMessage *message;
1267 if (playsink->async_pending) {
1268 GST_INFO_OBJECT (playsink, "Sending async_done message");
1269 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1270 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1271 (playsink), message);
1273 playsink->async_pending = FALSE;
1276 playsink->need_async_start = FALSE;
1279 /* try to change the state of an element. This function returns the element when
1280 * the state change could be performed. When this function returns NULL an error
1281 * occured and the element is unreffed if @unref is TRUE. */
1283 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1285 GstStateChangeReturn ret;
1288 ret = gst_element_set_state (element, GST_STATE_READY);
1289 if (ret == GST_STATE_CHANGE_FAILURE) {
1290 GST_DEBUG_OBJECT (playsink, "failed state change..");
1291 gst_element_set_state (element, GST_STATE_NULL);
1293 gst_object_unref (element);
1300 /* make the element (bin) that contains the elements needed to perform
1301 * video display. Only used for *raw* video streams.
1303 * +------------------------------------------------------------+
1305 * | +-------+ +----------+ +----------+ +---------+ |
1306 * | | queue | |colorspace| |videoscale| |videosink| |
1307 * | +-sink src-sink src-sink src-sink | |
1308 * | | +-------+ +----------+ +----------+ +---------+ |
1310 * +------------------------------------------------------------+
1313 static GstPlayVideoDeinterlaceChain *
1314 gen_video_deinterlace_chain (GstPlaySink * playsink)
1316 GstPlayVideoDeinterlaceChain *chain;
1319 GstElement *head = NULL, *prev = NULL;
1321 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1322 chain->chain.playsink = playsink;
1324 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1326 /* create a bin to hold objects, as we create them we add them to this bin so
1327 * that when something goes wrong we only need to unref the bin */
1328 chain->chain.bin = gst_bin_new ("vdbin");
1329 bin = GST_BIN_CAST (chain->chain.bin);
1330 gst_object_ref_sink (bin);
1332 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1333 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1334 if (chain->conv == NULL) {
1335 post_missing_element_message (playsink, COLORSPACE);
1336 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1337 (_("Missing element '%s' - check your GStreamer installation."),
1338 COLORSPACE), ("video rendering might fail"));
1340 gst_bin_add (bin, chain->conv);
1345 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1346 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1347 if (chain->deinterlace == NULL) {
1348 post_missing_element_message (playsink, "deinterlace");
1349 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1350 (_("Missing element '%s' - check your GStreamer installation."),
1351 "deinterlace"), ("deinterlacing won't work"));
1353 gst_bin_add (bin, chain->deinterlace);
1355 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1356 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1359 head = chain->deinterlace;
1361 prev = chain->deinterlace;
1365 pad = gst_element_get_static_pad (head, "sink");
1366 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1367 gst_object_unref (pad);
1369 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1373 pad = gst_element_get_static_pad (prev, "src");
1374 chain->srcpad = gst_ghost_pad_new ("src", pad);
1375 gst_object_unref (pad);
1377 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1380 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1381 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1387 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1388 (NULL), ("Failed to configure the video deinterlace chain."));
1389 free_chain ((GstPlayChain *) chain);
1395 is_valid_color_balance_element (GstColorBalance * bal)
1397 gboolean have_brightness = FALSE;
1398 gboolean have_contrast = FALSE;
1399 gboolean have_hue = FALSE;
1400 gboolean have_saturation = FALSE;
1401 const GList *channels, *l;
1403 channels = gst_color_balance_list_channels (bal);
1404 for (l = channels; l; l = l->next) {
1405 GstColorBalanceChannel *ch = l->data;
1407 if (g_strrstr (ch->label, "BRIGHTNESS"))
1408 have_brightness = TRUE;
1409 else if (g_strrstr (ch->label, "CONTRAST"))
1410 have_contrast = TRUE;
1411 else if (g_strrstr (ch->label, "HUE"))
1413 else if (g_strrstr (ch->label, "SATURATION"))
1414 have_saturation = TRUE;
1417 return have_brightness && have_contrast && have_hue && have_saturation;
1421 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1424 GstColorBalance *cb, **cb_out = user_data;
1426 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1427 valid = is_valid_color_balance_element (cb);
1430 && gst_color_balance_get_balance_type (*cb_out) ==
1431 GST_COLOR_BALANCE_SOFTWARE) {
1432 gst_object_unref (*cb_out);
1433 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1434 } else if (!*cb_out) {
1435 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1440 static GstColorBalance *
1441 find_color_balance_element (GstElement * element)
1444 GstColorBalance *cb = NULL;
1446 if (GST_IS_COLOR_BALANCE (element)
1447 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1448 return GST_COLOR_BALANCE (gst_object_ref (element));
1449 else if (!GST_IS_BIN (element))
1452 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1453 GST_TYPE_COLOR_BALANCE);
1454 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1455 &cb) == GST_ITERATOR_RESYNC)
1456 gst_iterator_resync (it);
1457 gst_iterator_free (it);
1463 colorbalance_value_changed_cb (GstColorBalance * balance,
1464 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1469 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1470 GstColorBalanceChannel *proxy = l->data;
1472 if (g_strrstr (channel->label, proxy->label)) {
1475 /* Convert to [0, 1] range */
1478 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1479 (gdouble) channel->min_value);
1480 /* Convert to proxy range */
1482 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1483 (gdouble) proxy->min_value);
1484 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1486 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1487 playsink->colorbalance_values[i]);
1494 update_colorbalance (GstPlaySink * playsink)
1496 GstColorBalance *balance = NULL;
1500 GST_OBJECT_LOCK (playsink);
1501 if (playsink->colorbalance_element) {
1503 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1505 GST_OBJECT_UNLOCK (playsink);
1509 g_signal_handlers_block_by_func (balance,
1510 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1512 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1513 GstColorBalanceChannel *proxy = l->data;
1514 GstColorBalanceChannel *channel = NULL;
1515 const GList *channels, *k;
1517 channels = gst_color_balance_list_channels (balance);
1518 for (k = channels; k; k = k->next) {
1519 GstColorBalanceChannel *tmp = k->data;
1521 if (g_strrstr (tmp->label, proxy->label)) {
1529 gst_color_balance_set_value (balance, channel,
1530 playsink->colorbalance_values[i]);
1533 g_signal_handlers_unblock_by_func (balance,
1534 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1536 gst_object_unref (balance);
1539 /* make the element (bin) that contains the elements needed to perform
1542 * +------------------------------------------------------------+
1544 * | +-------+ +----------+ +----------+ +---------+ |
1545 * | | queue | |colorspace| |videoscale| |videosink| |
1546 * | +-sink src-sink src-sink src-sink | |
1547 * | | +-------+ +----------+ +----------+ +---------+ |
1549 * +------------------------------------------------------------+
1552 static GstPlayVideoChain *
1553 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1555 GstPlayVideoChain *chain;
1558 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1560 chain = g_new0 (GstPlayVideoChain, 1);
1561 chain->chain.playsink = playsink;
1562 chain->chain.raw = raw;
1564 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1566 if (playsink->video_sink) {
1567 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1568 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1570 /* only try fallback if no specific sink was chosen */
1571 if (chain->sink == NULL) {
1572 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1573 elem = gst_element_factory_make ("autovideosink", "videosink");
1574 chain->sink = try_element (playsink, elem, TRUE);
1576 if (chain->sink == NULL) {
1577 /* if default sink from config.h is different then try it too */
1578 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1579 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1580 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1581 chain->sink = try_element (playsink, elem, TRUE);
1585 playsink->video_sink = gst_object_ref (chain->sink);
1587 if (chain->sink == NULL)
1591 /* if we can disable async behaviour of the sink, we can avoid adding a
1592 * queue for the audio chain. */
1594 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1597 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1598 async, GST_ELEMENT_NAME (elem));
1599 g_object_set (elem, "async", async, NULL);
1600 chain->async = async;
1602 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1603 chain->async = TRUE;
1606 /* Make sure the aspect ratio is kept */
1608 gst_play_sink_find_property_sinks (playsink, chain->sink,
1609 "force-aspect-ratio", G_TYPE_BOOLEAN);
1611 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1613 /* find ts-offset element */
1614 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1615 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1618 /* create a bin to hold objects, as we create them we add them to this bin so
1619 * that when something goes wrong we only need to unref the bin */
1620 chain->chain.bin = gst_bin_new ("vbin");
1621 bin = GST_BIN_CAST (chain->chain.bin);
1622 gst_object_ref_sink (bin);
1623 gst_bin_add (bin, chain->sink);
1625 /* Get the VideoOverlay element */
1627 GstVideoOverlay *overlay = NULL;
1629 GST_OBJECT_LOCK (playsink);
1630 if (playsink->overlay_element)
1631 gst_object_unref (playsink->overlay_element);
1632 playsink->overlay_element =
1633 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1634 GST_TYPE_VIDEO_OVERLAY));
1635 if (playsink->overlay_element)
1636 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1637 GST_OBJECT_UNLOCK (playsink);
1640 if (playsink->overlay_handle_set)
1641 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1642 if (playsink->overlay_handle_events_set)
1643 gst_video_overlay_handle_events (overlay,
1644 playsink->overlay_handle_events);
1645 if (playsink->overlay_render_rectangle_set)
1646 gst_video_overlay_set_render_rectangle (overlay,
1647 playsink->overlay_x, playsink->overlay_y,
1648 playsink->overlay_width, playsink->overlay_height);
1649 gst_object_unref (overlay);
1653 /* decouple decoder from sink, this improves playback quite a lot since the
1654 * decoder can continue while the sink blocks for synchronisation. We don't
1655 * need a lot of buffers as this consumes a lot of memory and we don't want
1656 * too little because else we would be context switching too quickly. */
1657 chain->queue = gst_element_factory_make ("queue", "vqueue");
1658 if (chain->queue == NULL) {
1659 post_missing_element_message (playsink, "queue");
1660 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1661 (_("Missing element '%s' - check your GStreamer installation."),
1662 "queue"), ("video rendering might be suboptimal"));
1666 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1667 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1668 gst_bin_add (bin, chain->queue);
1669 head = prev = chain->queue;
1672 GST_OBJECT_LOCK (playsink);
1673 if (playsink->colorbalance_element) {
1674 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1675 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1676 gst_object_unref (playsink->colorbalance_element);
1678 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1679 GST_OBJECT_UNLOCK (playsink);
1681 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1682 || (!playsink->colorbalance_element
1683 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1684 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1685 gboolean use_balance = !playsink->colorbalance_element
1686 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1688 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1690 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1691 "use-converters", use_converters, "use-balance", use_balance, NULL);
1693 GST_OBJECT_LOCK (playsink);
1694 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1695 playsink->colorbalance_element =
1696 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1697 (chain->conv)->balance));
1698 GST_OBJECT_UNLOCK (playsink);
1700 gst_bin_add (bin, chain->conv);
1702 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1703 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1711 update_colorbalance (playsink);
1714 GST_DEBUG_OBJECT (playsink, "linking to sink");
1715 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1716 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1720 pad = gst_element_get_static_pad (head, "sink");
1721 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1723 /* sending audio/video flushes break stream changes when the pipeline
1724 * is paused and played again in 0.10 */
1726 gst_pad_set_event_function (chain->sinkpad,
1727 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1728 gst_pad_set_chain_function (chain->sinkpad,
1729 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1732 gst_object_unref (pad);
1733 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1740 if (!elem && !playsink->video_sink) {
1741 post_missing_element_message (playsink, "autovideosink");
1742 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1743 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1744 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1745 (_("Both autovideosink and %s elements are missing."),
1746 DEFAULT_VIDEOSINK), (NULL));
1748 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1749 (_("The autovideosink element is missing.")), (NULL));
1752 if (playsink->video_sink) {
1753 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1754 (_("Configured videosink %s is not working."),
1755 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1756 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1757 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1758 (_("Both autovideosink and %s elements are not working."),
1759 DEFAULT_VIDEOSINK), (NULL));
1761 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1762 (_("The autovideosink element is not working.")), (NULL));
1765 free_chain ((GstPlayChain *) chain);
1771 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1772 (NULL), ("Failed to configure the video sink."));
1773 /* checking sink made it READY */
1774 gst_element_set_state (chain->sink, GST_STATE_NULL);
1775 /* Remove chain from the bin to allow reuse later */
1776 gst_bin_remove (bin, chain->sink);
1777 free_chain ((GstPlayChain *) chain);
1783 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1786 GstPlayVideoChain *chain;
1787 GstStateChangeReturn ret;
1789 chain = playsink->videochain;
1791 chain->chain.raw = raw;
1793 /* if the chain was active we don't do anything */
1794 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1797 /* try to set the sink element to READY again */
1798 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1799 if (ret == GST_STATE_CHANGE_FAILURE)
1802 /* Get the VideoOverlay element */
1804 GstVideoOverlay *overlay = NULL;
1806 GST_OBJECT_LOCK (playsink);
1807 if (playsink->overlay_element)
1808 gst_object_unref (playsink->overlay_element);
1809 playsink->overlay_element =
1810 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1811 GST_TYPE_VIDEO_OVERLAY));
1812 if (playsink->overlay_element)
1813 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1814 GST_OBJECT_UNLOCK (playsink);
1817 if (playsink->overlay_handle_set)
1818 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1819 if (playsink->overlay_handle_events_set)
1820 gst_video_overlay_handle_events (overlay,
1821 playsink->overlay_handle_events);
1822 if (playsink->overlay_render_rectangle_set)
1823 gst_video_overlay_set_render_rectangle (overlay,
1824 playsink->overlay_x, playsink->overlay_y,
1825 playsink->overlay_width, playsink->overlay_height);
1826 gst_object_unref (overlay);
1830 /* find ts-offset element */
1831 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1832 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1835 /* if we can disable async behaviour of the sink, we can avoid adding a
1836 * queue for the audio chain. */
1838 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1841 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1842 async, GST_ELEMENT_NAME (elem));
1843 g_object_set (elem, "async", async, NULL);
1844 chain->async = async;
1846 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1847 chain->async = TRUE;
1850 /* Make sure the aspect ratio is kept */
1852 gst_play_sink_find_property_sinks (playsink, chain->sink,
1853 "force-aspect-ratio", G_TYPE_BOOLEAN);
1855 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1857 GST_OBJECT_LOCK (playsink);
1858 if (playsink->colorbalance_element) {
1859 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1860 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1861 gst_object_unref (playsink->colorbalance_element);
1863 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1864 GST_OBJECT_UNLOCK (playsink);
1867 gboolean use_balance = !playsink->colorbalance_element
1868 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1870 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1872 GST_OBJECT_LOCK (playsink);
1873 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1874 playsink->colorbalance_element =
1875 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1876 (chain->conv)->balance));
1877 GST_OBJECT_UNLOCK (playsink);
1880 update_colorbalance (playsink);
1886 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1890 GstStructure *structure;
1891 event = gst_event_new_segment (segment);
1892 structure = gst_event_writable_structure (event);
1893 gst_structure_id_set (structure,
1894 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1899 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1900 const gchar * sink_type,
1901 gboolean * sink_ignore_wrong_state,
1902 gboolean * sink_custom_flush_finished,
1903 gboolean * sink_pending_flush, GstSegment * sink_segment)
1905 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1907 const GstStructure *structure = gst_event_get_structure (event);
1909 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1910 gchar *custom_flush;
1911 gchar *custom_flush_finish;
1913 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1914 custom_flush_finish =
1915 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1916 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1917 GST_DEBUG_OBJECT (pad,
1918 "Custom %s flush event received, marking to flush %s", sink_type,
1920 GST_PLAY_SINK_LOCK (playsink);
1921 *sink_ignore_wrong_state = TRUE;
1922 *sink_custom_flush_finished = FALSE;
1923 GST_PLAY_SINK_UNLOCK (playsink);
1924 } else if (strcmp (gst_structure_get_name (structure),
1925 custom_flush_finish) == 0) {
1926 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1928 GST_PLAY_SINK_LOCK (playsink);
1929 *sink_pending_flush = TRUE;
1930 *sink_custom_flush_finished = TRUE;
1931 GST_PLAY_SINK_UNLOCK (playsink);
1934 g_free (custom_flush);
1935 g_free (custom_flush_finish);
1936 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1937 GST_PLAY_SINK_LOCK (playsink);
1938 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1940 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1941 GST_PLAY_SINK_UNLOCK (playsink);
1944 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1945 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1947 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1948 const GstSegment *segment;
1950 gst_event_parse_segment (event, &segment);
1951 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1953 GST_PLAY_SINK_LOCK (playsink);
1954 if (sink_segment->format != segment->format) {
1955 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1957 gst_format_get_name (sink_segment->format),
1958 gst_format_get_name (segment->format));
1959 gst_segment_init (sink_segment, segment->format);
1962 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1963 sink_type, sink_segment);
1964 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1965 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1966 sink_type, sink_segment);
1967 GST_PLAY_SINK_UNLOCK (playsink);
1970 gst_event_unref (event);
1971 gst_object_unref (playsink);
1975 static GstFlowReturn
1976 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1977 const gchar * sink_type,
1978 gboolean * sink_ignore_wrong_state,
1979 gboolean * sink_custom_flush_finished,
1980 gboolean * sink_pending_flush, GstSegment * sink_segment)
1982 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1983 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
1986 GST_PLAY_SINK_LOCK (playsink);
1988 if (*sink_pending_flush) {
1989 GstSegment segment_copy;
1991 GstStructure *structure;
1993 *sink_pending_flush = FALSE;
1995 /* sink_segment will be updated in flush */
1996 segment_copy = *sink_segment;
1998 GST_PLAY_SINK_UNLOCK (playsink);
2000 /* make the bin drop all cached data.
2001 * This event will be dropped on the src pad, if any. */
2002 event = gst_event_new_flush_start ();
2003 structure = gst_event_writable_structure (event);
2004 gst_structure_id_set (structure,
2005 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2007 GST_DEBUG_OBJECT (pad,
2008 "Pushing %s flush-start event with reset segment marker set: %"
2009 GST_PTR_FORMAT, sink_type, event);
2010 gst_pad_send_event (pad, event);
2012 /* make queue drop all cached data.
2013 * This event will be dropped on the src pad. */
2014 event = gst_event_new_flush_stop (TRUE);
2015 structure = gst_event_writable_structure (event);
2016 gst_structure_id_set (structure,
2017 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2019 GST_DEBUG_OBJECT (pad,
2020 "Pushing %s flush-stop event with reset segment marker set: %"
2021 GST_PTR_FORMAT, sink_type, event);
2022 gst_pad_send_event (pad, event);
2024 /* Re-sync queue segment info after flush-stop.
2025 * This event will be dropped on the src pad. */
2026 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2029 _generate_update_newsegment_event (pad, sink_segment, &event1);
2030 GST_DEBUG_OBJECT (playsink,
2031 "Pushing segment event with reset "
2032 "segment marker set: %" GST_PTR_FORMAT, event1);
2033 gst_pad_send_event (pad, event1);
2036 GST_PLAY_SINK_UNLOCK (playsink);
2039 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2041 GST_PLAY_SINK_LOCK (playsink);
2042 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2043 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2045 if (*sink_custom_flush_finished) {
2046 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2047 "wrong state for %s", sink_type);
2048 *sink_ignore_wrong_state = FALSE;
2053 GST_PLAY_SINK_UNLOCK (playsink);
2055 gst_object_unref (playsink);
2056 gst_object_unref (tbin);
2060 /* sending audio/video flushes break stream changes when the pipeline
2061 * is paused and played again in 0.10 */
2064 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2066 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2067 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2070 ret = gst_play_sink_sink_event (pad, event, "video",
2071 &playsink->video_ignore_wrong_state,
2072 &playsink->video_custom_flush_finished,
2073 &playsink->video_pending_flush, &playsink->video_segment);
2075 gst_object_unref (playsink);
2076 gst_object_unref (tbin);
2080 static GstFlowReturn
2081 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2083 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2084 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2087 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2088 &playsink->video_ignore_wrong_state,
2089 &playsink->video_custom_flush_finished,
2090 &playsink->video_pending_flush, &playsink->video_segment);
2092 gst_object_unref (playsink);
2093 gst_object_unref (tbin);
2098 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2100 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2101 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2104 ret = gst_play_sink_sink_event (pad, event, "audio",
2105 &playsink->audio_ignore_wrong_state,
2106 &playsink->audio_custom_flush_finished,
2107 &playsink->audio_pending_flush, &playsink->audio_segment);
2109 gst_object_unref (playsink);
2110 gst_object_unref (tbin);
2114 static GstFlowReturn
2115 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2117 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2118 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2121 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2122 &playsink->audio_ignore_wrong_state,
2123 &playsink->audio_custom_flush_finished,
2124 &playsink->audio_pending_flush, &playsink->audio_segment);
2126 gst_object_unref (playsink);
2127 gst_object_unref (tbin);
2133 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2136 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2139 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2140 &playsink->text_ignore_wrong_state,
2141 &playsink->text_custom_flush_finished,
2142 &playsink->text_pending_flush, &playsink->text_segment);
2144 gst_object_unref (playsink);
2149 static GstFlowReturn
2150 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2154 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2156 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2157 &playsink->text_ignore_wrong_state,
2158 &playsink->text_custom_flush_finished,
2159 &playsink->text_pending_flush, &playsink->text_segment);
2161 gst_object_unref (playsink);
2166 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2170 const GstStructure *structure;
2172 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2174 structure = gst_event_get_structure (event);
2177 gst_structure_id_has_field (structure,
2178 _playsink_reset_segment_event_marker_id)) {
2179 /* the events marked with a reset segment marker
2180 * are sent internally to reset the queue and
2181 * must be dropped here */
2182 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2183 "segment marker set: %" GST_PTR_FORMAT, event);
2188 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2191 gst_event_unref (event);
2195 /* make an element for playback of video with subtitles embedded.
2196 * Only used for *raw* video streams.
2198 * +--------------------------------------------+
2200 * | +--------+ +-----------------+ |
2201 * | | queue | | subtitleoverlay | |
2202 * video--src sink---video_sink | |
2203 * | +--------+ | src--src
2204 * text------------------text_sink | |
2205 * | +-----------------+ |
2206 * +--------------------------------------------+
2209 static GstPlayTextChain *
2210 gen_text_chain (GstPlaySink * playsink)
2212 GstPlayTextChain *chain;
2215 GstPad *videosinkpad, *textsinkpad, *srcpad;
2217 chain = g_new0 (GstPlayTextChain, 1);
2218 chain->chain.playsink = playsink;
2220 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2222 chain->chain.bin = gst_bin_new ("tbin");
2223 bin = GST_BIN_CAST (chain->chain.bin);
2224 gst_object_ref_sink (bin);
2226 videosinkpad = textsinkpad = srcpad = NULL;
2228 /* first try to hook the text pad to the custom sink */
2229 if (playsink->text_sink) {
2230 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2231 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2234 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2237 /* make sure the sparse subtitles don't participate in the preroll */
2238 g_object_set (elem, "async", FALSE, NULL);
2239 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2240 gst_bin_add (bin, chain->sink);
2241 /* NOTE streamsynchronizer needs streams decoupled */
2242 /* make a little queue */
2243 chain->queue = gst_element_factory_make ("queue", "subqueue");
2244 if (chain->queue == NULL) {
2245 post_missing_element_message (playsink, "queue");
2246 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2247 (_("Missing element '%s' - check your GStreamer installation."),
2248 "queue"), ("rendering might be suboptimal"));
2250 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2251 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2252 "silent", TRUE, NULL);
2253 gst_bin_add (bin, chain->queue);
2255 /* we have a custom sink, this will be our textsinkpad */
2256 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2257 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2258 /* we're all fine now and we can add the sink to the chain */
2259 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2260 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2262 GST_WARNING_OBJECT (playsink,
2263 "can't find a sink pad on custom text sink");
2264 gst_bin_remove (bin, chain->sink);
2265 gst_bin_remove (bin, chain->queue);
2267 chain->queue = NULL;
2269 /* try to set sync to true but it's no biggie when we can't */
2270 if (chain->sink && (elem =
2271 gst_play_sink_find_property_sinks (playsink, chain->sink,
2272 "sync", G_TYPE_BOOLEAN)))
2273 g_object_set (elem, "sync", TRUE, NULL);
2276 gst_bin_remove (bin, chain->sink);
2278 GST_WARNING_OBJECT (playsink,
2279 "can't find async property in custom text sink");
2282 if (textsinkpad == NULL) {
2283 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2284 (_("Custom text sink element is not usable.")),
2285 ("fallback to default subtitleoverlay"));
2289 if (textsinkpad == NULL) {
2290 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2291 /* make a little queue */
2292 chain->queue = gst_element_factory_make ("queue", "vqueue");
2293 if (chain->queue == NULL) {
2294 post_missing_element_message (playsink, "queue");
2295 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2296 (_("Missing element '%s' - check your GStreamer installation."),
2297 "queue"), ("video rendering might be suboptimal"));
2299 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2300 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2301 "silent", TRUE, NULL);
2302 gst_bin_add (bin, chain->queue);
2303 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2307 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2308 if (chain->overlay == NULL) {
2309 post_missing_element_message (playsink, "subtitleoverlay");
2310 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2311 (_("Missing element '%s' - check your GStreamer installation."),
2312 "subtitleoverlay"), ("subtitle rendering disabled"));
2314 GstElement *element;
2316 gst_bin_add (bin, chain->overlay);
2318 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2319 if (playsink->font_desc) {
2320 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2321 playsink->font_desc, NULL);
2323 if (playsink->subtitle_encoding) {
2324 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2325 playsink->subtitle_encoding, NULL);
2328 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2329 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2331 /* make another little queue to decouple streams */
2332 element = gst_element_factory_make ("queue", "subqueue");
2333 if (element == NULL) {
2334 post_missing_element_message (playsink, "queue");
2335 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2336 (_("Missing element '%s' - check your GStreamer installation."),
2337 "queue"), ("rendering might be suboptimal"));
2339 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2340 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2341 "silent", TRUE, NULL);
2342 gst_bin_add (bin, element);
2343 if (gst_element_link_pads_full (element, "src", chain->overlay,
2344 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2345 textsinkpad = gst_element_get_static_pad (element, "sink");
2346 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2348 gst_bin_remove (bin, chain->sink);
2349 gst_bin_remove (bin, chain->overlay);
2351 chain->overlay = NULL;
2352 gst_object_unref (videosinkpad);
2353 videosinkpad = NULL;
2360 if (videosinkpad == NULL) {
2361 /* if we still don't have a videosink, we don't have an overlay. the only
2362 * thing we can do is insert an identity and ghost the src
2364 chain->identity = gst_element_factory_make ("identity", "tidentity");
2365 if (chain->identity == NULL) {
2366 post_missing_element_message (playsink, "identity");
2367 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2368 (_("Missing element '%s' - check your GStreamer installation."),
2369 "identity"), (NULL));
2371 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2372 g_object_set (chain->identity, "silent", TRUE, NULL);
2373 gst_bin_add (bin, chain->identity);
2374 srcpad = gst_element_get_static_pad (chain->identity, "src");
2375 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2379 /* expose the ghostpads */
2381 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2382 gst_object_unref (videosinkpad);
2383 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2386 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2387 gst_object_unref (textsinkpad);
2389 gst_pad_set_event_function (chain->textsinkpad,
2390 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2391 gst_pad_set_chain_function (chain->textsinkpad,
2392 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2394 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2397 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2398 gst_object_unref (srcpad);
2400 gst_pad_set_event_function (chain->srcpad,
2401 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2403 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2410 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2414 g_object_get (object, "volume", &vol, NULL);
2415 playsink->volume = vol;
2417 g_object_notify (G_OBJECT (playsink), "volume");
2421 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2425 g_object_get (object, "mute", &mute, NULL);
2426 playsink->mute = mute;
2428 g_object_notify (G_OBJECT (playsink), "mute");
2431 /* make the chain that contains the elements needed to perform
2434 * We add a tee as the first element so that we can link the visualisation chain
2435 * to it when requested.
2437 * +-------------------------------------------------------------+
2439 * | +---------+ +----------+ +---------+ +---------+ |
2440 * | |audioconv| |audioscale| | volume | |audiosink| |
2441 * | +-srck src-sink src-sink src-sink | |
2442 * | | +---------+ +----------+ +---------+ +---------+ |
2444 * +-------------------------------------------------------------+
2446 static GstPlayAudioChain *
2447 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2449 GstPlayAudioChain *chain;
2451 gboolean have_volume;
2453 GstElement *head, *prev, *elem = NULL;
2455 chain = g_new0 (GstPlayAudioChain, 1);
2456 chain->chain.playsink = playsink;
2457 chain->chain.raw = raw;
2459 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2461 if (playsink->audio_sink) {
2462 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2463 playsink->audio_sink);
2464 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2466 /* only try fallback if no specific sink was chosen */
2467 if (chain->sink == NULL) {
2468 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2469 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2470 chain->sink = try_element (playsink, elem, TRUE);
2472 if (chain->sink == NULL) {
2473 /* if default sink from config.h is different then try it too */
2474 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2475 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2476 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2477 chain->sink = try_element (playsink, elem, TRUE);
2481 playsink->audio_sink = gst_object_ref (chain->sink);
2483 if (chain->sink == NULL)
2486 chain->chain.bin = gst_bin_new ("abin");
2487 bin = GST_BIN_CAST (chain->chain.bin);
2488 gst_object_ref_sink (bin);
2489 gst_bin_add (bin, chain->sink);
2491 /* we have to add a queue when we need to decouple for the video sink in
2492 * visualisations and for streamsynchronizer */
2493 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2494 chain->queue = gst_element_factory_make ("queue", "aqueue");
2495 if (chain->queue == NULL) {
2496 post_missing_element_message (playsink, "queue");
2497 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2498 (_("Missing element '%s' - check your GStreamer installation."),
2499 "queue"), ("audio playback and visualizations might not work"));
2503 g_object_set (chain->queue, "silent", TRUE, NULL);
2504 gst_bin_add (bin, chain->queue);
2505 prev = head = chain->queue;
2508 /* find ts-offset element */
2509 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2510 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2513 /* check if the sink, or something within the sink, has the volume property.
2514 * If it does we don't need to add a volume element. */
2516 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2519 chain->volume = elem;
2521 g_signal_connect (chain->volume, "notify::volume",
2522 G_CALLBACK (notify_volume_cb), playsink);
2524 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2526 chain->sink_volume = TRUE;
2527 /* if the sink also has a mute property we can use this as well. We'll only
2528 * use the mute property if there is a volume property. We can simulate the
2529 * mute with the volume otherwise. */
2531 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2534 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2535 g_signal_connect (chain->mute, "notify::mute",
2536 G_CALLBACK (notify_mute_cb), playsink);
2538 /* use the sink to control the volume and mute */
2539 if (playsink->volume_changed) {
2540 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2541 playsink->volume_changed = FALSE;
2543 if (playsink->mute_changed) {
2545 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2548 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2550 playsink->mute_changed = FALSE;
2553 /* no volume, we need to add a volume element when we can */
2554 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2555 have_volume = FALSE;
2556 chain->sink_volume = FALSE;
2559 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2560 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2561 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2562 gboolean use_volume =
2563 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2564 GST_DEBUG_OBJECT (playsink,
2565 "creating audioconvert with use-converters %d, use-volume %d",
2566 use_converters, use_volume);
2568 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2569 "use-converters", use_converters, "use-volume", use_volume, NULL);
2570 gst_bin_add (bin, chain->conv);
2572 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2573 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2580 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2581 GstPlaySinkAudioConvert *conv =
2582 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2585 chain->volume = conv->volume;
2588 g_signal_connect (chain->volume, "notify::volume",
2589 G_CALLBACK (notify_volume_cb), playsink);
2591 /* volume also has the mute property */
2592 chain->mute = chain->volume;
2593 g_signal_connect (chain->mute, "notify::mute",
2594 G_CALLBACK (notify_mute_cb), playsink);
2596 /* configure with the latest volume and mute */
2597 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2599 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2605 /* we only have to link to the previous element if we have something in
2606 * front of the sink */
2607 GST_DEBUG_OBJECT (playsink, "linking to sink");
2608 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2609 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2613 /* post a warning if we have no way to configure the volume */
2615 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2616 (_("No volume control found")), ("Volume/mute is not available"));
2619 /* and ghost the sinkpad of the headmost element */
2620 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2621 pad = gst_element_get_static_pad (head, "sink");
2622 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2624 /* sending audio/video flushes break stream changes when the pipeline
2625 * is paused and played again in 0.10 */
2627 gst_pad_set_event_function (chain->sinkpad,
2628 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2629 gst_pad_set_chain_function (chain->sinkpad,
2630 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2633 gst_object_unref (pad);
2634 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2641 if (!elem && !playsink->audio_sink) {
2642 post_missing_element_message (playsink, "autoaudiosink");
2643 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2644 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2645 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2646 (_("Both autoaudiosink and %s elements are missing."),
2647 DEFAULT_AUDIOSINK), (NULL));
2649 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2650 (_("The autoaudiosink element is missing.")), (NULL));
2653 if (playsink->audio_sink) {
2654 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2655 (_("Configured audiosink %s is not working."),
2656 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2657 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2658 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2659 (_("Both autoaudiosink and %s elements are not working."),
2660 DEFAULT_AUDIOSINK), (NULL));
2662 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2663 (_("The autoaudiosink element is not working.")), (NULL));
2666 free_chain ((GstPlayChain *) chain);
2671 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2672 (NULL), ("Failed to configure the audio sink."));
2673 /* checking sink made it READY */
2674 gst_element_set_state (chain->sink, GST_STATE_NULL);
2675 /* Remove chain from the bin to allow reuse later */
2676 gst_bin_remove (bin, chain->sink);
2677 free_chain ((GstPlayChain *) chain);
2683 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2686 GstPlayAudioChain *chain;
2687 GstStateChangeReturn ret;
2688 GstPlaySinkAudioConvert *conv;
2690 chain = playsink->audiochain;
2691 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2693 chain->chain.raw = raw;
2695 /* if the chain was active we don't do anything */
2696 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2699 /* try to set the sink element to READY again */
2700 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2701 if (ret == GST_STATE_CHANGE_FAILURE)
2704 /* find ts-offset element */
2705 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2706 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2709 /* check if the sink, or something within the sink, has the volume property.
2710 * If it does we don't need to add a volume element. */
2712 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2715 chain->volume = elem;
2717 if (playsink->volume_changed) {
2718 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2720 /* use the sink to control the volume */
2721 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2722 playsink->volume_changed = FALSE;
2725 g_signal_connect (chain->volume, "notify::volume",
2726 G_CALLBACK (notify_volume_cb), playsink);
2727 /* if the sink also has a mute property we can use this as well. We'll only
2728 * use the mute property if there is a volume property. We can simulate the
2729 * mute with the volume otherwise. */
2731 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2734 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2735 g_signal_connect (chain->mute, "notify::mute",
2736 G_CALLBACK (notify_mute_cb), playsink);
2739 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2741 /* no volume, we need to add a volume element when we can */
2742 g_object_set (chain->conv, "use-volume",
2743 !!(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2744 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2746 /* Disconnect signals */
2747 disconnect_chain (chain, playsink);
2749 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2750 chain->volume = conv->volume;
2751 chain->mute = chain->volume;
2753 g_signal_connect (chain->volume, "notify::volume",
2754 G_CALLBACK (notify_volume_cb), playsink);
2756 g_signal_connect (chain->mute, "notify::mute",
2757 G_CALLBACK (notify_mute_cb), playsink);
2759 /* configure with the latest volume and mute */
2760 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2761 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2764 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2770 * +-------------------------------------------------------------------+
2772 * | +----------+ +------------+ +----------+ +-------+ |
2773 * | | visqueue | | audioconv | | audiores | | vis | |
2774 * | +-sink src-sink + samp src-sink src-sink src-+ |
2775 * | | +----------+ +------------+ +----------+ +-------+ | |
2777 * +-------------------------------------------------------------------+
2780 static GstPlayVisChain *
2781 gen_vis_chain (GstPlaySink * playsink)
2783 GstPlayVisChain *chain;
2789 chain = g_new0 (GstPlayVisChain, 1);
2790 chain->chain.playsink = playsink;
2792 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2794 chain->chain.bin = gst_bin_new ("visbin");
2795 bin = GST_BIN_CAST (chain->chain.bin);
2796 gst_object_ref_sink (bin);
2798 /* we're queuing raw audio here, we can remove this queue when we can disable
2799 * async behaviour in the video sink. */
2800 chain->queue = gst_element_factory_make ("queue", "visqueue");
2801 if (chain->queue == NULL)
2803 g_object_set (chain->queue, "silent", TRUE, NULL);
2804 gst_bin_add (bin, chain->queue);
2806 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2807 if (chain->conv == NULL)
2808 goto no_audioconvert;
2809 gst_bin_add (bin, chain->conv);
2811 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2812 if (chain->resample == NULL)
2813 goto no_audioresample;
2814 gst_bin_add (bin, chain->resample);
2816 /* this pad will be used for blocking the dataflow and switching the vis
2818 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2820 if (playsink->visualisation) {
2821 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2822 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2824 if (chain->vis == NULL) {
2825 GST_DEBUG_OBJECT (playsink, "trying goom");
2826 elem = gst_element_factory_make ("goom", "vis");
2827 chain->vis = try_element (playsink, elem, TRUE);
2829 if (chain->vis == NULL)
2832 gst_bin_add (bin, chain->vis);
2834 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2835 GST_PAD_LINK_CHECK_NOTHING);
2837 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2838 GST_PAD_LINK_CHECK_NOTHING);
2840 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2841 GST_PAD_LINK_CHECK_NOTHING);
2845 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2846 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2848 pad = gst_element_get_static_pad (chain->queue, "sink");
2849 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2850 gst_object_unref (pad);
2851 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2853 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2854 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2861 post_missing_element_message (playsink, "queue");
2862 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2863 (_("Missing element '%s' - check your GStreamer installation."),
2865 free_chain ((GstPlayChain *) chain);
2870 post_missing_element_message (playsink, "audioconvert");
2871 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2872 (_("Missing element '%s' - check your GStreamer installation."),
2873 "audioconvert"), ("possibly a liboil version mismatch?"));
2874 free_chain ((GstPlayChain *) chain);
2879 post_missing_element_message (playsink, "audioresample");
2880 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2881 (_("Missing element '%s' - check your GStreamer installation."),
2882 "audioresample"), (NULL));
2883 free_chain ((GstPlayChain *) chain);
2888 post_missing_element_message (playsink, "goom");
2889 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2890 (_("Missing element '%s' - check your GStreamer installation."),
2892 free_chain ((GstPlayChain *) chain);
2897 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2898 (NULL), ("Failed to configure the visualisation element."));
2899 /* element made it to READY */
2900 gst_element_set_state (chain->vis, GST_STATE_NULL);
2901 free_chain ((GstPlayChain *) chain);
2906 /* this function is called when all the request pads are requested and when we
2907 * have to construct the final pipeline. Based on the flags we construct the
2908 * final output pipelines.
2911 gst_play_sink_reconfigure (GstPlaySink * playsink)
2914 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2916 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2918 /* assume we need nothing */
2919 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2921 GST_PLAY_SINK_LOCK (playsink);
2922 GST_OBJECT_LOCK (playsink);
2923 /* get flags, there are protected with the object lock */
2924 flags = playsink->flags;
2925 GST_OBJECT_UNLOCK (playsink);
2927 /* figure out which components we need */
2928 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2929 /* we have subtitles and we are requested to show it */
2933 GST_OBJECT_LOCK (playsink);
2934 if (playsink->overlay_element)
2935 gst_object_unref (playsink->overlay_element);
2936 playsink->overlay_element = NULL;
2938 if (playsink->colorbalance_element) {
2939 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2940 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2941 gst_object_unref (playsink->colorbalance_element);
2943 playsink->colorbalance_element = NULL;
2944 GST_OBJECT_UNLOCK (playsink);
2946 if (((flags & GST_PLAY_FLAG_VIDEO)
2947 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2948 /* we have video and we are requested to show it */
2951 /* we only deinterlace if native video is not requested and
2952 * we have raw video */
2953 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2954 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2955 need_deinterlace = TRUE;
2958 if (playsink->audio_pad) {
2959 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2962 if (playsink->audio_pad_raw) {
2963 /* only can do vis with raw uncompressed audio */
2964 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2965 /* also add video when we add visualisation */
2972 /* we have a text_pad and we need text rendering, in this case we need a
2973 * video_pad to combine the video with the text or visualizations */
2974 if (need_text && !need_video && !playsink->text_sink) {
2975 if (playsink->video_pad) {
2977 } else if (need_audio) {
2978 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2979 (_("Can't play a text file without video or visualizations.")),
2980 ("Have text pad but no video pad or visualizations"));
2983 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2984 (_("Can't play a text file without video or visualizations.")),
2985 ("Have text pad but no video pad or visualizations"));
2986 GST_PLAY_SINK_UNLOCK (playsink);
2991 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2992 need_video, need_vis, need_text);
2994 /* set up video pipeline */
2996 gboolean raw, async;
2998 /* we need a raw sink when we do vis or when we have a raw pad */
2999 raw = need_vis ? TRUE : playsink->video_pad_raw;
3000 /* we try to set the sink async=FALSE when we need vis, this way we can
3001 * avoid a queue in the audio chain. */
3004 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3005 playsink->video_pad_raw);
3007 if (playsink->videochain) {
3008 /* try to reactivate the chain */
3009 if (!setup_video_chain (playsink, raw, async)) {
3010 if (playsink->video_sinkpad_stream_synchronizer) {
3011 gst_element_release_request_pad (GST_ELEMENT_CAST
3012 (playsink->stream_synchronizer),
3013 playsink->video_sinkpad_stream_synchronizer);
3014 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3015 playsink->video_sinkpad_stream_synchronizer = NULL;
3016 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3017 playsink->video_srcpad_stream_synchronizer = NULL;
3020 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3022 /* Remove the sink from the bin to keep its state
3023 * and unparent it to allow reuse */
3024 if (playsink->videochain->sink)
3025 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3026 playsink->videochain->sink);
3028 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3029 free_chain ((GstPlayChain *) playsink->videochain);
3030 playsink->videochain = NULL;
3034 if (!playsink->videochain)
3035 playsink->videochain = gen_video_chain (playsink, raw, async);
3036 if (!playsink->videochain)
3039 if (!playsink->video_sinkpad_stream_synchronizer) {
3040 GValue item = { 0, };
3043 playsink->video_sinkpad_stream_synchronizer =
3044 gst_element_get_request_pad (GST_ELEMENT_CAST
3045 (playsink->stream_synchronizer), "sink_%u");
3046 it = gst_pad_iterate_internal_links
3047 (playsink->video_sinkpad_stream_synchronizer);
3049 gst_iterator_next (it, &item);
3050 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3051 g_value_unset (&item);
3052 g_assert (playsink->video_srcpad_stream_synchronizer);
3053 gst_iterator_free (it);
3056 if (playsink->video_pad)
3057 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3058 playsink->video_sinkpad_stream_synchronizer);
3060 if (need_deinterlace) {
3061 if (!playsink->videodeinterlacechain)
3062 playsink->videodeinterlacechain =
3063 gen_video_deinterlace_chain (playsink);
3064 if (!playsink->videodeinterlacechain)
3067 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3069 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3071 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3072 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3074 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3075 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3077 if (playsink->videodeinterlacechain) {
3078 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3079 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3084 GST_DEBUG_OBJECT (playsink, "adding video chain");
3085 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3086 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3087 /* if we are not part of vis or subtitles, set the ghostpad target */
3088 if (!need_vis && !need_text && (!playsink->textchain
3089 || !playsink->text_pad)) {
3090 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3091 if (need_deinterlace)
3092 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3093 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3095 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3096 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3099 GST_DEBUG_OBJECT (playsink, "no video needed");
3100 if (playsink->videochain) {
3101 GST_DEBUG_OBJECT (playsink, "removing video chain");
3102 if (playsink->vischain) {
3105 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3107 /* also had visualisation, release the tee srcpad before we then
3108 * unlink the video from it */
3109 if (playsink->audio_tee_vissrc) {
3110 gst_element_release_request_pad (playsink->audio_tee,
3111 playsink->audio_tee_vissrc);
3112 gst_object_unref (playsink->audio_tee_vissrc);
3113 playsink->audio_tee_vissrc = NULL;
3116 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3117 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3120 if (playsink->video_sinkpad_stream_synchronizer) {
3121 gst_element_release_request_pad (GST_ELEMENT_CAST
3122 (playsink->stream_synchronizer),
3123 playsink->video_sinkpad_stream_synchronizer);
3124 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3125 playsink->video_sinkpad_stream_synchronizer = NULL;
3126 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3127 playsink->video_srcpad_stream_synchronizer = NULL;
3130 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3131 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3132 if (playsink->videochain->ts_offset)
3133 gst_object_unref (playsink->videochain->ts_offset);
3134 playsink->videochain->ts_offset = NULL;
3137 if (playsink->videodeinterlacechain) {
3138 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3139 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3142 if (playsink->video_pad)
3143 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3149 GST_DEBUG_OBJECT (playsink, "adding audio");
3151 /* get a raw sink if we are asked for a raw pad */
3152 raw = playsink->audio_pad_raw;
3154 if (playsink->audiochain) {
3155 /* try to reactivate the chain */
3156 if (!setup_audio_chain (playsink, raw)) {
3157 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3158 if (playsink->audio_tee_asrc) {
3159 gst_element_release_request_pad (playsink->audio_tee,
3160 playsink->audio_tee_asrc);
3161 gst_object_unref (playsink->audio_tee_asrc);
3162 playsink->audio_tee_asrc = NULL;
3165 if (playsink->audio_sinkpad_stream_synchronizer) {
3166 gst_element_release_request_pad (GST_ELEMENT_CAST
3167 (playsink->stream_synchronizer),
3168 playsink->audio_sinkpad_stream_synchronizer);
3169 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3170 playsink->audio_sinkpad_stream_synchronizer = NULL;
3171 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3172 playsink->audio_srcpad_stream_synchronizer = NULL;
3175 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3177 /* Remove the sink from the bin to keep its state
3178 * and unparent it to allow reuse */
3179 if (playsink->audiochain->sink)
3180 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3181 playsink->audiochain->sink);
3183 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3184 disconnect_chain (playsink->audiochain, playsink);
3185 playsink->audiochain->volume = NULL;
3186 playsink->audiochain->mute = NULL;
3187 if (playsink->audiochain->ts_offset)
3188 gst_object_unref (playsink->audiochain->ts_offset);
3189 playsink->audiochain->ts_offset = NULL;
3190 free_chain ((GstPlayChain *) playsink->audiochain);
3191 playsink->audiochain = NULL;
3192 playsink->volume_changed = playsink->mute_changed = FALSE;
3196 if (!playsink->audiochain) {
3197 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3198 playsink->audiochain = gen_audio_chain (playsink, raw);
3201 if (!playsink->audio_sinkpad_stream_synchronizer) {
3202 GValue item = { 0, };
3205 playsink->audio_sinkpad_stream_synchronizer =
3206 gst_element_get_request_pad (GST_ELEMENT_CAST
3207 (playsink->stream_synchronizer), "sink_%u");
3208 it = gst_pad_iterate_internal_links
3209 (playsink->audio_sinkpad_stream_synchronizer);
3211 gst_iterator_next (it, &item);
3212 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3213 g_value_unset (&item);
3214 g_assert (playsink->audio_srcpad_stream_synchronizer);
3215 gst_iterator_free (it);
3218 if (playsink->audiochain) {
3219 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3220 if (playsink->audio_tee_asrc == NULL) {
3221 playsink->audio_tee_asrc =
3222 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3224 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3225 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3226 gst_pad_link_full (playsink->audio_tee_asrc,
3227 playsink->audio_sinkpad_stream_synchronizer,
3228 GST_PAD_LINK_CHECK_NOTHING);
3229 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3230 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3233 GST_DEBUG_OBJECT (playsink, "no audio needed");
3234 /* we have no audio or we are requested to not play audio */
3235 if (playsink->audiochain) {
3236 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3237 /* release the audio pad */
3238 if (playsink->audio_tee_asrc) {
3239 gst_element_release_request_pad (playsink->audio_tee,
3240 playsink->audio_tee_asrc);
3241 gst_object_unref (playsink->audio_tee_asrc);
3242 playsink->audio_tee_asrc = NULL;
3245 if (playsink->audio_sinkpad_stream_synchronizer) {
3246 gst_element_release_request_pad (GST_ELEMENT_CAST
3247 (playsink->stream_synchronizer),
3248 playsink->audio_sinkpad_stream_synchronizer);
3249 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3250 playsink->audio_sinkpad_stream_synchronizer = NULL;
3251 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3252 playsink->audio_srcpad_stream_synchronizer = NULL;
3255 if (playsink->audiochain->sink_volume) {
3256 disconnect_chain (playsink->audiochain, playsink);
3257 playsink->audiochain->volume = NULL;
3258 playsink->audiochain->mute = NULL;
3259 if (playsink->audiochain->ts_offset)
3260 gst_object_unref (playsink->audiochain->ts_offset);
3261 playsink->audiochain->ts_offset = NULL;
3263 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3264 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3271 if (!playsink->vischain)
3272 playsink->vischain = gen_vis_chain (playsink);
3274 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3276 if (playsink->vischain) {
3277 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3279 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3280 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3281 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3282 if (playsink->audio_tee_vissrc == NULL) {
3283 playsink->audio_tee_vissrc =
3284 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3286 gst_pad_link_full (playsink->audio_tee_vissrc,
3287 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3288 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3289 GST_PAD_LINK_CHECK_NOTHING);
3290 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3291 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3292 gst_object_unref (srcpad);
3295 GST_DEBUG_OBJECT (playsink, "no vis needed");
3296 if (playsink->vischain) {
3297 if (playsink->audio_tee_vissrc) {
3298 gst_element_release_request_pad (playsink->audio_tee,
3299 playsink->audio_tee_vissrc);
3300 gst_object_unref (playsink->audio_tee_vissrc);
3301 playsink->audio_tee_vissrc = NULL;
3303 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3304 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3305 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3310 GST_DEBUG_OBJECT (playsink, "adding text");
3311 if (!playsink->textchain) {
3312 GST_DEBUG_OBJECT (playsink, "creating text chain");
3313 playsink->textchain = gen_text_chain (playsink);
3315 if (playsink->textchain) {
3318 GST_DEBUG_OBJECT (playsink, "adding text chain");
3319 if (playsink->textchain->overlay)
3320 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3322 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3324 if (!playsink->text_sinkpad_stream_synchronizer) {
3325 GValue item = { 0, };
3327 playsink->text_sinkpad_stream_synchronizer =
3328 gst_element_get_request_pad (GST_ELEMENT_CAST
3329 (playsink->stream_synchronizer), "sink_%u");
3330 it = gst_pad_iterate_internal_links
3331 (playsink->text_sinkpad_stream_synchronizer);
3333 gst_iterator_next (it, &item);
3334 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3335 g_value_unset (&item);
3336 g_assert (playsink->text_srcpad_stream_synchronizer);
3337 gst_iterator_free (it);
3339 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3340 playsink->text_sinkpad_stream_synchronizer);
3341 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3342 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3345 if (need_vis || need_video) {
3350 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3351 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3352 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3353 GST_PAD_LINK_CHECK_NOTHING);
3354 gst_object_unref (srcpad);
3356 if (need_deinterlace)
3357 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3358 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3360 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3361 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3363 gst_pad_link_full (playsink->textchain->srcpad,
3364 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3367 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3370 GST_DEBUG_OBJECT (playsink, "no text needed");
3371 /* we have no subtitles/text or we are requested to not show them */
3373 if (playsink->text_sinkpad_stream_synchronizer) {
3374 gst_element_release_request_pad (GST_ELEMENT_CAST
3375 (playsink->stream_synchronizer),
3376 playsink->text_sinkpad_stream_synchronizer);
3377 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3378 playsink->text_sinkpad_stream_synchronizer = NULL;
3379 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3380 playsink->text_srcpad_stream_synchronizer = NULL;
3383 if (playsink->textchain) {
3384 if (playsink->text_pad == NULL) {
3385 /* no text pad, remove the chain entirely */
3386 GST_DEBUG_OBJECT (playsink, "removing text chain");
3387 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3388 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3390 /* we have a chain and a textpad, turn the subtitles off */
3391 GST_DEBUG_OBJECT (playsink, "turning off the text");
3392 if (playsink->textchain->overlay)
3393 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3397 if (!need_video && playsink->video_pad) {
3398 if (playsink->video_sinkpad_stream_synchronizer) {
3399 gst_element_release_request_pad (GST_ELEMENT_CAST
3400 (playsink->stream_synchronizer),
3401 playsink->video_sinkpad_stream_synchronizer);
3402 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3403 playsink->video_sinkpad_stream_synchronizer = NULL;
3404 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3405 playsink->video_srcpad_stream_synchronizer = NULL;
3408 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3411 if (playsink->text_pad && !playsink->textchain)
3412 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3414 update_av_offset (playsink);
3415 do_async_done (playsink);
3416 GST_PLAY_SINK_UNLOCK (playsink);
3423 /* gen_ chain already posted error */
3424 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3425 GST_PLAY_SINK_UNLOCK (playsink);
3431 * gst_play_sink_set_flags:
3432 * @playsink: a #GstPlaySink
3433 * @flags: #GstPlayFlags
3435 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3436 * when constructing the sink pipelins.
3438 * Returns: TRUE if the flags could be configured.
3441 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3443 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3445 GST_OBJECT_LOCK (playsink);
3446 playsink->flags = flags;
3447 GST_OBJECT_UNLOCK (playsink);
3453 * gst_play_sink_get_flags:
3454 * @playsink: a #GstPlaySink
3456 * Get the flags of @playsink. That flags control the behaviour of the sink when
3457 * it constructs the sink pipelines.
3459 * Returns: the currently configured #GstPlayFlags.
3462 gst_play_sink_get_flags (GstPlaySink * playsink)
3466 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3468 GST_OBJECT_LOCK (playsink);
3469 res = playsink->flags;
3470 GST_OBJECT_UNLOCK (playsink);
3476 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3478 GstPlayTextChain *chain;
3480 GST_PLAY_SINK_LOCK (playsink);
3481 chain = (GstPlayTextChain *) playsink->textchain;
3482 g_free (playsink->font_desc);
3483 playsink->font_desc = g_strdup (desc);
3484 if (chain && chain->overlay) {
3485 g_object_set (chain->overlay, "font-desc", desc, NULL);
3487 GST_PLAY_SINK_UNLOCK (playsink);
3491 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3493 gchar *result = NULL;
3494 GstPlayTextChain *chain;
3496 GST_PLAY_SINK_LOCK (playsink);
3497 chain = (GstPlayTextChain *) playsink->textchain;
3498 if (chain && chain->overlay) {
3499 g_object_get (chain->overlay, "font-desc", &result, NULL);
3500 playsink->font_desc = g_strdup (result);
3502 result = g_strdup (playsink->font_desc);
3504 GST_PLAY_SINK_UNLOCK (playsink);
3510 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3511 const gchar * encoding)
3513 GstPlayTextChain *chain;
3515 GST_PLAY_SINK_LOCK (playsink);
3516 chain = (GstPlayTextChain *) playsink->textchain;
3517 g_free (playsink->subtitle_encoding);
3518 playsink->subtitle_encoding = g_strdup (encoding);
3519 if (chain && chain->overlay) {
3520 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3522 GST_PLAY_SINK_UNLOCK (playsink);
3526 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3528 gchar *result = NULL;
3529 GstPlayTextChain *chain;
3531 GST_PLAY_SINK_LOCK (playsink);
3532 chain = (GstPlayTextChain *) playsink->textchain;
3533 if (chain && chain->overlay) {
3534 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3535 playsink->subtitle_encoding = g_strdup (result);
3537 result = g_strdup (playsink->subtitle_encoding);
3539 GST_PLAY_SINK_UNLOCK (playsink);
3545 update_av_offset (GstPlaySink * playsink)
3548 GstPlayAudioChain *achain;
3549 GstPlayVideoChain *vchain;
3551 av_offset = playsink->av_offset;
3552 achain = (GstPlayAudioChain *) playsink->audiochain;
3553 vchain = (GstPlayVideoChain *) playsink->videochain;
3555 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3556 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3557 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3559 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3564 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3566 GST_PLAY_SINK_LOCK (playsink);
3567 playsink->av_offset = av_offset;
3568 update_av_offset (playsink);
3569 GST_PLAY_SINK_UNLOCK (playsink);
3573 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3577 GST_PLAY_SINK_LOCK (playsink);
3578 result = playsink->av_offset;
3579 GST_PLAY_SINK_UNLOCK (playsink);
3585 * gst_play_sink_get_last_sample:
3586 * @playsink: a #GstPlaySink
3588 * Get the last displayed sample from @playsink. This sample is in the native
3589 * format of the sink element, the caps in the result sample contain the format
3590 * of the frame data.
3592 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3596 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3598 GstSample *result = NULL;
3599 GstPlayVideoChain *chain;
3601 GST_PLAY_SINK_LOCK (playsink);
3602 GST_DEBUG_OBJECT (playsink, "taking last sample");
3603 /* get the video chain if we can */
3604 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3605 GST_DEBUG_OBJECT (playsink, "found video chain");
3606 /* see if the chain is active */
3607 if (chain->chain.activated && chain->sink) {
3610 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3612 /* find and get the last-buffer property now */
3614 gst_play_sink_find_property (playsink, chain->sink,
3615 "last-sample", GST_TYPE_SAMPLE))) {
3616 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3617 g_object_get (elem, "last-sample", &result, NULL);
3618 gst_object_unref (elem);
3622 GST_PLAY_SINK_UNLOCK (playsink);
3628 * gst_play_sink_convert_sample:
3629 * @playsink: a #GstPlaySink
3632 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3633 * be in the native format of the sink element and the caps on the buffer
3634 * describe the format of the frame. If @caps is not %NULL, the video
3635 * frame will be converted to the format of the caps.
3637 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3638 * available or when the conversion failed.
3641 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3646 result = gst_play_sink_get_last_sample (playsink);
3647 if (result != NULL && caps != NULL) {
3650 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3651 if (temp == NULL && err)
3654 gst_sample_unref (result);
3662 /* I'm really uncertain whether we should make playsink post an error
3663 * on the bus or not. It's not like it's a critical issue regarding
3664 * playsink behaviour. */
3665 GST_ERROR ("Error converting frame: %s", err->message);
3666 gst_sample_unref (result);
3673 is_raw_structure (GstStructure * s)
3677 name = gst_structure_get_name (s);
3679 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3685 is_raw_pad (GstPad * pad)
3687 GstPad *peer = gst_pad_get_peer (pad);
3689 gboolean raw = TRUE;
3694 caps = gst_pad_get_current_caps (peer);
3698 caps = gst_pad_query_caps (peer, NULL);
3700 n = gst_caps_get_size (caps);
3701 for (i = 0; i < n; i++) {
3702 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3706 } else if (raw != r) {
3707 GST_ERROR_OBJECT (pad,
3708 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3714 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3716 gst_caps_unref (caps);
3717 gst_object_unref (peer);
3722 static GstPadProbeReturn
3723 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3724 gpointer user_data);
3727 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3729 if (playsink->video_pad) {
3731 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3732 (playsink->video_pad)));
3733 if (blocked && playsink->video_block_id == 0) {
3734 playsink->video_block_id =
3735 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3736 sinkpad_blocked_cb, playsink, NULL);
3737 } else if (!blocked && playsink->video_block_id) {
3738 gst_pad_remove_probe (opad, playsink->video_block_id);
3739 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3740 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3741 playsink->video_block_id = 0;
3742 playsink->video_pad_blocked = FALSE;
3744 gst_object_unref (opad);
3749 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3751 if (playsink->audio_pad) {
3753 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3754 (playsink->audio_pad)));
3755 if (blocked && playsink->audio_block_id == 0) {
3756 playsink->audio_block_id =
3757 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3758 sinkpad_blocked_cb, playsink, NULL);
3759 } else if (!blocked && playsink->audio_block_id) {
3760 gst_pad_remove_probe (opad, playsink->audio_block_id);
3761 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3762 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3763 playsink->audio_block_id = 0;
3764 playsink->audio_pad_blocked = FALSE;
3766 gst_object_unref (opad);
3771 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3773 if (playsink->text_pad) {
3775 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3776 (playsink->text_pad)));
3777 if (blocked && playsink->text_block_id == 0) {
3778 playsink->text_block_id =
3779 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3780 sinkpad_blocked_cb, playsink, NULL);
3781 } else if (!blocked && playsink->text_block_id) {
3782 gst_pad_remove_probe (opad, playsink->text_block_id);
3783 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3784 playsink->text_block_id = 0;
3785 playsink->text_pad_blocked = FALSE;
3787 gst_object_unref (opad);
3791 static GstPadProbeReturn
3792 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3795 GstPlaySink *playsink = (GstPlaySink *) user_data;
3798 GST_PLAY_SINK_LOCK (playsink);
3800 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3801 if (pad == playsink->video_pad) {
3802 playsink->video_pad_blocked = TRUE;
3803 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3804 } else if (pad == playsink->audio_pad) {
3805 playsink->audio_pad_blocked = TRUE;
3806 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3807 } else if (pad == playsink->text_pad) {
3808 playsink->text_pad_blocked = TRUE;
3809 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3812 /* We reconfigure when for ALL streams:
3813 * * there isn't a pad
3814 * * OR the pad is blocked
3815 * * OR there are no pending blocks on that pad
3818 if ((!playsink->video_pad || playsink->video_pad_blocked
3819 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3820 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3821 && (!playsink->text_pad || playsink->text_pad_blocked
3822 || !PENDING_TEXT_BLOCK (playsink))) {
3823 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3825 if (playsink->video_pad) {
3826 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3827 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3828 playsink->video_pad_raw);
3831 if (playsink->audio_pad) {
3832 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3833 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3834 playsink->audio_pad_raw);
3837 gst_play_sink_reconfigure (playsink);
3839 video_set_blocked (playsink, FALSE);
3840 audio_set_blocked (playsink, FALSE);
3841 text_set_blocked (playsink, FALSE);
3844 gst_object_unref (pad);
3846 GST_PLAY_SINK_UNLOCK (playsink);
3848 return GST_PAD_PROBE_OK;
3852 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3854 gboolean reconfigure = FALSE;
3858 g_object_get (pad, "caps", &caps, NULL);
3862 if (pad == playsink->audio_pad) {
3863 raw = is_raw_pad (pad);
3864 reconfigure = (!!playsink->audio_pad_raw != !!raw)
3865 && playsink->audiochain;
3866 GST_DEBUG_OBJECT (pad,
3867 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3869 } else if (pad == playsink->video_pad) {
3870 raw = is_raw_pad (pad);
3871 reconfigure = (!!playsink->video_pad_raw != !!raw)
3872 && playsink->videochain;
3873 GST_DEBUG_OBJECT (pad,
3874 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3878 gst_caps_unref (caps);
3881 GST_PLAY_SINK_LOCK (playsink);
3882 video_set_blocked (playsink, TRUE);
3883 audio_set_blocked (playsink, TRUE);
3884 text_set_blocked (playsink, TRUE);
3885 GST_PLAY_SINK_UNLOCK (playsink);
3890 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3891 GstPlaySinkType type)
3893 gulong *block_id = NULL;
3895 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3897 GST_PLAY_SINK_LOCK (playsink);
3898 if (pad == playsink->video_pad) {
3899 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3900 type != GST_PLAY_SINK_TYPE_VIDEO)
3902 block_id = &playsink->video_block_id;
3903 } else if (pad == playsink->audio_pad) {
3904 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3905 type != GST_PLAY_SINK_TYPE_AUDIO)
3907 block_id = &playsink->audio_block_id;
3908 } else if (pad == playsink->text_pad) {
3909 if (type != GST_PLAY_SINK_TYPE_TEXT)
3911 block_id = &playsink->text_block_id;
3914 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3916 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3919 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3920 sinkpad_blocked_cb, playsink, NULL);
3921 PENDING_FLAG_SET (playsink, type);
3922 gst_object_unref (blockpad);
3924 GST_PLAY_SINK_UNLOCK (playsink);
3931 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3933 GST_PLAY_SINK_UNLOCK (playsink);
3939 * gst_play_sink_request_pad
3940 * @playsink: a #GstPlaySink
3941 * @type: a #GstPlaySinkType
3943 * Create or return a pad of @type.
3945 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3948 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3951 gboolean created = FALSE;
3952 gboolean activate = TRUE;
3953 const gchar *pad_name = NULL;
3954 gulong *block_id = NULL;
3956 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3958 GST_PLAY_SINK_LOCK (playsink);
3960 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3961 case GST_PLAY_SINK_TYPE_AUDIO:
3962 pad_name = "audio_sink";
3963 if (!playsink->audio_tee) {
3964 GST_LOG_OBJECT (playsink, "creating tee");
3965 /* create tee when needed. This element will feed the audio sink chain
3966 * and the vis chain. */
3967 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3968 if (playsink->audio_tee == NULL) {
3969 post_missing_element_message (playsink, "tee");
3970 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3971 (_("Missing element '%s' - check your GStreamer installation."),
3976 playsink->audio_tee_sink =
3977 gst_element_get_static_pad (playsink->audio_tee, "sink");
3978 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3979 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3982 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3984 if (!playsink->audio_pad) {
3985 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3986 playsink->audio_pad =
3987 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3988 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3989 G_CALLBACK (caps_notify_cb), playsink);
3992 playsink->audio_pad_raw = FALSE;
3993 res = playsink->audio_pad;
3994 block_id = &playsink->audio_block_id;
3996 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3997 case GST_PLAY_SINK_TYPE_VIDEO:
3998 pad_name = "video_sink";
3999 if (!playsink->video_pad) {
4000 GST_LOG_OBJECT (playsink, "ghosting videosink");
4001 playsink->video_pad =
4002 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4003 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4004 G_CALLBACK (caps_notify_cb), playsink);
4007 playsink->video_pad_raw = FALSE;
4008 res = playsink->video_pad;
4009 block_id = &playsink->video_block_id;
4011 case GST_PLAY_SINK_TYPE_TEXT:
4012 GST_LOG_OBJECT (playsink, "ghosting text");
4013 if (!playsink->text_pad) {
4014 playsink->text_pad =
4015 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4018 res = playsink->text_pad;
4019 block_id = &playsink->text_block_id;
4021 case GST_PLAY_SINK_TYPE_FLUSHING:
4025 /* we need a unique padname for the flushing pad. */
4026 padname = g_strdup_printf ("flushing_%u", playsink->count);
4027 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4038 GST_PLAY_SINK_UNLOCK (playsink);
4040 if (created && res) {
4041 /* we have to add the pad when it's active or we get an error when the
4042 * element is 'running' */
4043 gst_pad_set_active (res, TRUE);
4044 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4045 if (block_id && *block_id == 0) {
4047 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4050 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4051 sinkpad_blocked_cb, playsink, NULL);
4052 PENDING_FLAG_SET (playsink, type);
4053 gst_object_unref (blockpad);
4056 gst_pad_set_active (res, activate);
4064 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4065 const gchar * name, const GstCaps * caps)
4069 GstPlaySinkType type;
4070 const gchar *tplname;
4072 g_return_val_if_fail (templ != NULL, NULL);
4074 GST_DEBUG_OBJECT (element, "name:%s", name);
4076 psink = GST_PLAY_SINK (element);
4077 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4079 /* Figure out the GstPlaySinkType based on the template */
4080 if (!strcmp (tplname, "audio_sink"))
4081 type = GST_PLAY_SINK_TYPE_AUDIO;
4082 else if (!strcmp (tplname, "audio_raw_sink"))
4083 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4084 else if (!strcmp (tplname, "video_sink"))
4085 type = GST_PLAY_SINK_TYPE_VIDEO;
4086 else if (!strcmp (tplname, "video_raw_sink"))
4087 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4088 else if (!strcmp (tplname, "text_sink"))
4089 type = GST_PLAY_SINK_TYPE_TEXT;
4091 goto unknown_template;
4093 pad = gst_play_sink_request_pad (psink, type);
4097 GST_WARNING_OBJECT (element, "Unknown pad template");
4102 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4104 GstPad **res = NULL;
4105 gboolean untarget = TRUE;
4107 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4109 GST_PLAY_SINK_LOCK (playsink);
4110 if (pad == playsink->video_pad) {
4111 res = &playsink->video_pad;
4112 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4114 } else if (pad == playsink->audio_pad) {
4115 res = &playsink->audio_pad;
4116 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4118 } else if (pad == playsink->text_pad) {
4119 res = &playsink->text_pad;
4121 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4125 GST_PLAY_SINK_UNLOCK (playsink);
4128 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4129 gst_pad_set_active (*res, FALSE);
4131 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4132 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4134 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4135 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4141 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4143 GstPlaySink *psink = GST_PLAY_SINK (element);
4145 gst_play_sink_release_pad (psink, pad);
4149 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4151 GstPlaySink *playsink;
4153 playsink = GST_PLAY_SINK_CAST (bin);
4155 switch (GST_MESSAGE_TYPE (message)) {
4156 case GST_MESSAGE_STEP_DONE:
4161 gboolean flush, intermediate, eos;
4164 GST_INFO_OBJECT (playsink, "Handling step-done message");
4165 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4166 &intermediate, &duration, &eos);
4168 if (format == GST_FORMAT_BUFFERS) {
4169 /* for the buffer format, we align the other streams */
4170 if (playsink->audiochain) {
4174 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4177 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4178 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4182 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4185 case GST_MESSAGE_ELEMENT:{
4186 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4187 GstVideoOverlay *overlay;
4189 GST_OBJECT_LOCK (playsink);
4190 if (playsink->overlay_element
4191 && GST_OBJECT_CAST (playsink->overlay_element) !=
4192 GST_MESSAGE_SRC (message)) {
4193 gst_object_unref (playsink->overlay_element);
4194 playsink->overlay_element = NULL;
4197 if (!playsink->overlay_element)
4198 playsink->overlay_element =
4199 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4201 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4202 GST_OBJECT_UNLOCK (playsink);
4204 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4206 if (playsink->overlay_handle_set)
4207 gst_video_overlay_set_window_handle (playsink->overlay_element,
4208 playsink->overlay_handle);
4209 if (playsink->overlay_handle_events_set)
4210 gst_video_overlay_handle_events (playsink->overlay_element,
4211 playsink->overlay_handle_events);
4212 if (playsink->overlay_render_rectangle_set)
4213 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4214 playsink->overlay_x, playsink->overlay_y,
4215 playsink->overlay_width, playsink->overlay_height);
4217 gst_object_unref (overlay);
4218 gst_message_unref (message);
4219 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4221 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4227 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4232 /* Send an event to our sinks until one of them works; don't then send to the
4233 * remaining sinks (unlike GstBin)
4234 * Special case: If a text sink is set we need to send the event
4235 * to them in case it's source is different from the a/v stream's source.
4238 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4240 gboolean res = TRUE;
4241 if (playsink->send_event_mode == MODE_FIRST) {
4242 if (playsink->textchain && playsink->textchain->sink) {
4243 gst_event_ref (event);
4245 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4246 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4248 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4252 if (playsink->videochain) {
4253 gst_event_ref (event);
4255 gst_element_send_event (playsink->videochain->chain.bin,
4257 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4260 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4262 if (playsink->audiochain) {
4263 gst_event_ref (event);
4265 gst_element_send_event (playsink->audiochain->chain.bin,
4267 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4270 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4274 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4275 (GST_ELEMENT_CAST (playsink), event);
4279 gst_event_unref (event);
4283 /* We only want to send the event to a single sink (overriding GstBin's
4284 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4285 * events appropriately. So, this is a messy duplication of code. */
4287 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4289 gboolean res = FALSE;
4290 GstEventType event_type = GST_EVENT_TYPE (event);
4291 GstPlaySink *playsink;
4292 playsink = GST_PLAY_SINK_CAST (element);
4293 switch (event_type) {
4294 case GST_EVENT_SEEK:
4295 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4296 res = gst_play_sink_send_event_to_sink (playsink, event);
4298 case GST_EVENT_STEP:
4303 gboolean flush, intermediate;
4304 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4306 if (format == GST_FORMAT_BUFFERS) {
4307 /* for buffers, we will try to step video frames, for other formats we
4308 * send the step to all sinks */
4309 res = gst_play_sink_send_event_to_sink (playsink, event);
4312 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4319 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4326 static GstStateChangeReturn
4327 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4329 GstStateChangeReturn ret;
4330 GstStateChangeReturn bret;
4331 GstPlaySink *playsink;
4332 playsink = GST_PLAY_SINK (element);
4333 switch (transition) {
4334 case GST_STATE_CHANGE_READY_TO_PAUSED:
4335 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4337 playsink->need_async_start = TRUE;
4338 /* we want to go async to PAUSED until we managed to configure and add the
4340 do_async_start (playsink);
4341 ret = GST_STATE_CHANGE_ASYNC;
4343 /* block all pads here */
4344 GST_PLAY_SINK_LOCK (playsink);
4345 if (playsink->video_pad && playsink->video_block_id == 0) {
4347 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4348 (playsink->video_pad)));
4349 playsink->video_block_id =
4350 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4351 sinkpad_blocked_cb, playsink, NULL);
4352 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4353 gst_object_unref (opad);
4356 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4358 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4359 (playsink->audio_pad)));
4360 playsink->audio_block_id =
4361 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4362 sinkpad_blocked_cb, playsink, NULL);
4363 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4364 gst_object_unref (opad);
4367 if (playsink->text_pad && playsink->text_block_id == 0) {
4369 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4370 (playsink->text_pad)));
4371 playsink->text_block_id =
4372 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4373 sinkpad_blocked_cb, playsink, NULL);
4374 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4375 gst_object_unref (opad);
4377 GST_PLAY_SINK_UNLOCK (playsink);
4379 case GST_STATE_CHANGE_PAUSED_TO_READY:
4380 /* unblock all pads here */
4381 GST_PLAY_SINK_LOCK (playsink);
4382 video_set_blocked (playsink, FALSE);
4383 audio_set_blocked (playsink, FALSE);
4384 text_set_blocked (playsink, FALSE);
4385 GST_PLAY_SINK_UNLOCK (playsink);
4387 case GST_STATE_CHANGE_READY_TO_NULL:
4388 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4389 /* remove our links to the mute and volume elements when they were
4390 * provided by a sink */
4391 disconnect_chain (playsink->audiochain, playsink);
4392 playsink->audiochain->volume = NULL;
4393 playsink->audiochain->mute = NULL;
4396 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4397 gst_object_unref (playsink->audiochain->ts_offset);
4398 playsink->audiochain->ts_offset = NULL;
4401 if (playsink->videochain && playsink->videochain->ts_offset) {
4402 gst_object_unref (playsink->videochain->ts_offset);
4403 playsink->videochain->ts_offset = NULL;
4406 GST_OBJECT_LOCK (playsink);
4407 if (playsink->overlay_element)
4408 gst_object_unref (playsink->overlay_element);
4409 playsink->overlay_element = NULL;
4411 if (playsink->colorbalance_element) {
4412 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4413 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4414 gst_object_unref (playsink->colorbalance_element);
4416 playsink->colorbalance_element = NULL;
4417 GST_OBJECT_UNLOCK (playsink);
4419 ret = GST_STATE_CHANGE_SUCCESS;
4422 /* all other state changes return SUCCESS by default, this value can be
4423 * overridden by the result of the children */
4424 ret = GST_STATE_CHANGE_SUCCESS;
4428 /* do the state change of the children */
4430 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4432 /* now look at the result of our children and adjust the return value */
4434 case GST_STATE_CHANGE_FAILURE:
4435 /* failure, we stop */
4436 goto activate_failed;
4437 case GST_STATE_CHANGE_NO_PREROLL:
4438 /* some child returned NO_PREROLL. This is strange but we never know. We
4439 * commit our async state change (if any) and return the NO_PREROLL */
4440 do_async_done (playsink);
4443 case GST_STATE_CHANGE_ASYNC:
4444 /* some child was async, return this */
4448 /* return our previously configured return value */
4452 switch (transition) {
4453 case GST_STATE_CHANGE_READY_TO_PAUSED:
4455 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4456 /* FIXME Release audio device when we implement that */
4457 playsink->need_async_start = TRUE;
4459 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4460 if (playsink->video_sinkpad_stream_synchronizer) {
4461 gst_element_release_request_pad (GST_ELEMENT_CAST
4462 (playsink->stream_synchronizer),
4463 playsink->video_sinkpad_stream_synchronizer);
4464 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4465 playsink->video_sinkpad_stream_synchronizer = NULL;
4466 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4467 playsink->video_srcpad_stream_synchronizer = NULL;
4469 if (playsink->audio_sinkpad_stream_synchronizer) {
4470 gst_element_release_request_pad (GST_ELEMENT_CAST
4471 (playsink->stream_synchronizer),
4472 playsink->audio_sinkpad_stream_synchronizer);
4473 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4474 playsink->audio_sinkpad_stream_synchronizer = NULL;
4475 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4476 playsink->audio_srcpad_stream_synchronizer = NULL;
4478 if (playsink->text_sinkpad_stream_synchronizer) {
4479 gst_element_release_request_pad (GST_ELEMENT_CAST
4480 (playsink->stream_synchronizer),
4481 playsink->text_sinkpad_stream_synchronizer);
4482 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4483 playsink->text_sinkpad_stream_synchronizer = NULL;
4484 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4485 playsink->text_srcpad_stream_synchronizer = NULL;
4489 case GST_STATE_CHANGE_READY_TO_NULL:
4490 /* remove sinks we added */
4491 if (playsink->videodeinterlacechain) {
4492 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4494 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4496 if (playsink->videochain) {
4497 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4498 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4500 if (playsink->audiochain) {
4501 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4502 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4504 if (playsink->vischain) {
4505 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4506 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4508 if (playsink->textchain) {
4509 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4510 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4512 do_async_done (playsink);
4513 /* when going to READY, keep elements around as long as possible,
4514 * so they may be re-used faster next time/url around.
4515 * when really going to NULL, clean up everything completely. */
4516 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4518 /* Unparent the sinks to allow reuse */
4519 if (playsink->videochain && playsink->videochain->sink)
4520 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4521 playsink->videochain->sink);
4522 if (playsink->audiochain && playsink->audiochain->sink)
4523 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4524 playsink->audiochain->sink);
4525 if (playsink->textchain && playsink->textchain->sink)
4526 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4527 playsink->textchain->sink);
4528 if (playsink->audio_sink != NULL)
4529 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4530 if (playsink->video_sink != NULL)
4531 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4532 if (playsink->visualisation != NULL)
4533 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4534 if (playsink->text_sink != NULL)
4535 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4536 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4537 playsink->videodeinterlacechain = NULL;
4538 free_chain ((GstPlayChain *) playsink->videochain);
4539 playsink->videochain = NULL;
4540 free_chain ((GstPlayChain *) playsink->audiochain);
4541 playsink->audiochain = NULL;
4542 free_chain ((GstPlayChain *) playsink->vischain);
4543 playsink->vischain = NULL;
4544 free_chain ((GstPlayChain *) playsink->textchain);
4545 playsink->textchain = NULL;
4555 GST_DEBUG_OBJECT (element,
4556 "element failed to change states -- activation problem?");
4557 return GST_STATE_CHANGE_FAILURE;
4562 gst_play_sink_set_property (GObject * object, guint prop_id,
4563 const GValue * value, GParamSpec * spec)
4565 GstPlaySink *playsink = GST_PLAY_SINK (object);
4568 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4571 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4574 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4576 case PROP_FONT_DESC:
4577 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4579 case PROP_SUBTITLE_ENCODING:
4580 gst_play_sink_set_subtitle_encoding (playsink,
4581 g_value_get_string (value));
4583 case PROP_VIS_PLUGIN:
4584 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4586 case PROP_AV_OFFSET:
4587 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4589 case PROP_VIDEO_SINK:
4590 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4591 g_value_get_object (value));
4593 case PROP_AUDIO_SINK:
4594 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4595 g_value_get_object (value));
4597 case PROP_TEXT_SINK:
4598 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4599 g_value_get_object (value));
4601 case PROP_SEND_EVENT_MODE:
4602 playsink->send_event_mode = g_value_get_enum (value);
4605 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4611 gst_play_sink_get_property (GObject * object, guint prop_id,
4612 GValue * value, GParamSpec * spec)
4614 GstPlaySink *playsink = GST_PLAY_SINK (object);
4617 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4620 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4623 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4625 case PROP_FONT_DESC:
4626 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4628 case PROP_SUBTITLE_ENCODING:
4629 g_value_take_string (value,
4630 gst_play_sink_get_subtitle_encoding (playsink));
4632 case PROP_VIS_PLUGIN:
4633 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4636 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4638 case PROP_AV_OFFSET:
4639 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4641 case PROP_VIDEO_SINK:
4642 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4643 GST_PLAY_SINK_TYPE_VIDEO));
4645 case PROP_AUDIO_SINK:
4646 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4647 GST_PLAY_SINK_TYPE_AUDIO));
4649 case PROP_TEXT_SINK:
4650 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4651 GST_PLAY_SINK_TYPE_TEXT));
4653 case PROP_SEND_EVENT_MODE:
4654 g_value_set_enum (value, playsink->send_event_mode);
4657 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4663 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4665 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4666 GstVideoOverlay *overlay_element;
4668 GST_OBJECT_LOCK (playsink);
4669 if (playsink->overlay_element)
4671 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4673 overlay_element = NULL;
4674 GST_OBJECT_UNLOCK (playsink);
4676 if (overlay_element) {
4677 gst_video_overlay_expose (overlay_element);
4678 gst_object_unref (overlay_element);
4683 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4684 gboolean handle_events)
4686 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4687 GstVideoOverlay *overlay_element;
4689 GST_OBJECT_LOCK (playsink);
4690 if (playsink->overlay_element)
4692 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4694 overlay_element = NULL;
4695 GST_OBJECT_UNLOCK (playsink);
4697 playsink->overlay_handle_events_set = TRUE;
4698 playsink->overlay_handle_events = handle_events;
4700 if (overlay_element) {
4701 gst_video_overlay_handle_events (overlay_element, handle_events);
4702 gst_object_unref (overlay_element);
4707 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4708 gint y, gint width, gint height)
4710 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4711 GstVideoOverlay *overlay_element;
4713 GST_OBJECT_LOCK (playsink);
4714 if (playsink->overlay_element)
4716 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4718 overlay_element = NULL;
4719 GST_OBJECT_UNLOCK (playsink);
4721 playsink->overlay_render_rectangle_set = TRUE;
4722 playsink->overlay_x = x;
4723 playsink->overlay_y = y;
4724 playsink->overlay_width = width;
4725 playsink->overlay_height = height;
4727 if (overlay_element) {
4728 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4730 gst_object_unref (overlay_element);
4735 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4738 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4739 GstVideoOverlay *overlay_element;
4741 GST_OBJECT_LOCK (playsink);
4742 if (playsink->overlay_element)
4744 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4746 overlay_element = NULL;
4747 GST_OBJECT_UNLOCK (playsink);
4749 playsink->overlay_handle_set = TRUE;
4750 playsink->overlay_handle = handle;
4752 if (overlay_element) {
4753 gst_video_overlay_set_window_handle (overlay_element, handle);
4754 gst_object_unref (overlay_element);
4759 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4761 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4762 iface->expose = gst_play_sink_overlay_expose;
4763 iface->handle_events = gst_play_sink_overlay_handle_events;
4764 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4765 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4769 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4770 GstStructure * structure)
4772 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4775 GST_PLAY_SINK_LOCK (playsink);
4776 if (playsink->videochain && playsink->videochain->chain.bin)
4777 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4778 GST_PLAY_SINK_UNLOCK (playsink);
4781 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4784 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4786 gst_object_unref (nav);
4788 GstEvent *event = gst_event_new_navigation (structure);
4790 gst_element_send_event (GST_ELEMENT (bin), event);
4793 gst_object_unref (bin);
4797 gst_structure_free (structure);
4801 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4803 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4805 iface->send_event = gst_play_sink_navigation_send_event;
4808 static const GList *
4809 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4811 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4813 return playsink->colorbalance_channels;
4817 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4818 GstColorBalanceChannel * proxy, gint value)
4820 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4823 GstColorBalance *balance_element = NULL;
4825 GST_OBJECT_LOCK (playsink);
4826 if (playsink->colorbalance_element)
4828 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4829 GST_OBJECT_UNLOCK (playsink);
4831 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4832 GstColorBalanceChannel *proxy_tmp = l->data;
4835 if (proxy_tmp != proxy)
4838 playsink->colorbalance_values[i] = value;
4840 if (balance_element) {
4841 GstColorBalanceChannel *channel = NULL;
4842 const GList *channels, *k;
4844 channels = gst_color_balance_list_channels (balance_element);
4845 for (k = channels; k; k = k->next) {
4846 GstColorBalanceChannel *tmp = l->data;
4848 if (g_strrstr (tmp->label, proxy->label)) {
4856 /* Convert to [0, 1] range */
4859 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4860 (gdouble) proxy->min_value);
4861 /* Convert to channel range */
4863 channel->min_value + new_val * ((gdouble) channel->max_value -
4864 (gdouble) channel->min_value);
4866 gst_color_balance_set_value (balance_element, channel,
4867 (gint) (new_val + 0.5));
4869 gst_object_unref (balance_element);
4872 gst_color_balance_value_changed (balance, proxy, value);
4878 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4879 GstColorBalanceChannel * proxy)
4881 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4885 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4886 GstColorBalanceChannel *proxy_tmp = l->data;
4888 if (proxy_tmp != proxy)
4891 return playsink->colorbalance_values[i];
4894 g_return_val_if_reached (0);
4897 static GstColorBalanceType
4898 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4900 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4901 GstColorBalance *balance_element = NULL;
4902 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4904 GST_OBJECT_LOCK (playsink);
4905 if (playsink->colorbalance_element)
4907 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4908 GST_OBJECT_UNLOCK (playsink);
4910 if (balance_element) {
4911 t = gst_color_balance_get_balance_type (balance_element);
4912 gst_object_unref (balance_element);
4919 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4921 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4923 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4924 iface->set_value = gst_play_sink_colorbalance_set_value;
4925 iface->get_value = gst_play_sink_colorbalance_get_value;
4926 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4930 gst_play_sink_plugin_init (GstPlugin * plugin)
4932 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4933 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4934 GST_TYPE_PLAY_SINK);