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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, 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;
245 gboolean force_aspect_ratio;
247 /* videooverlay proxy interface */
248 GstVideoOverlay *overlay_element; /* protected with LOCK */
249 gboolean overlay_handle_set;
250 guintptr overlay_handle;
251 gboolean overlay_render_rectangle_set;
252 gint overlay_x, overlay_y, overlay_width, overlay_height;
253 gboolean overlay_handle_events_set;
254 gboolean overlay_handle_events;
256 /* colorbalance proxy interface */
257 GstColorBalance *colorbalance_element;
258 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
259 gint colorbalance_values[4];
261 /* sending audio/video flushes break stream changes when the pipeline
262 * is paused and played again in 0.10 */
264 GstSegment video_segment;
265 gboolean video_custom_flush_finished;
266 gboolean video_ignore_wrong_state;
267 gboolean video_pending_flush;
269 GstSegment audio_segment;
270 gboolean audio_custom_flush_finished;
271 gboolean audio_ignore_wrong_state;
272 gboolean audio_pending_flush;
275 GstSegment text_segment;
276 gboolean text_custom_flush_finished;
277 gboolean text_ignore_wrong_state;
278 gboolean text_pending_flush;
281 struct _GstPlaySinkClass
283 GstBinClass parent_class;
285 gboolean (*reconfigure) (GstPlaySink * playsink);
287 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
291 static GstStaticPadTemplate audiotemplate =
292 GST_STATIC_PAD_TEMPLATE ("audio_sink",
295 GST_STATIC_CAPS_ANY);
296 static GstStaticPadTemplate videotemplate =
297 GST_STATIC_PAD_TEMPLATE ("video_sink",
300 GST_STATIC_CAPS_ANY);
301 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
304 GST_STATIC_CAPS_ANY);
306 /* FIXME 0.11: Remove */
307 static GstStaticPadTemplate audiorawtemplate =
308 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
311 GST_STATIC_CAPS_ANY);
312 static GstStaticPadTemplate videorawtemplate =
313 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
316 GST_STATIC_CAPS_ANY);
327 PROP_SUBTITLE_ENCODING,
334 PROP_SEND_EVENT_MODE,
335 PROP_FORCE_ASPECT_RATIO,
345 static void gst_play_sink_dispose (GObject * object);
346 static void gst_play_sink_finalize (GObject * object);
347 static void gst_play_sink_set_property (GObject * object, guint prop_id,
348 const GValue * value, GParamSpec * spec);
349 static void gst_play_sink_get_property (GObject * object, guint prop_id,
350 GValue * value, GParamSpec * spec);
352 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
353 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
354 static void gst_play_sink_release_request_pad (GstElement * element,
356 static gboolean gst_play_sink_send_event (GstElement * element,
358 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
359 GstStateChange transition);
361 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
363 /* sending audio/video flushes break stream changes when the pipeline
364 * is paused and played again in 0.10 */
366 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
367 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
369 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
370 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
373 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
375 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
376 GstObject * parent, GstBuffer * buffer);
378 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
379 GstPlaySink * playsink);
380 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
381 GstPlaySink * playsink);
383 static void update_av_offset (GstPlaySink * playsink);
385 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
387 static GQuark _playsink_reset_segment_event_marker_id = 0;
389 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
391 static void gst_play_sink_overlay_init (gpointer g_iface,
392 gpointer g_iface_data);
393 static void gst_play_sink_navigation_init (gpointer g_iface,
394 gpointer g_iface_data);
395 static void gst_play_sink_colorbalance_init (gpointer g_iface,
396 gpointer g_iface_data);
399 _do_init (GType type)
401 static const GInterfaceInfo svol_info = {
404 static const GInterfaceInfo ov_info = {
405 gst_play_sink_overlay_init,
408 static const GInterfaceInfo nav_info = {
409 gst_play_sink_navigation_init,
412 static const GInterfaceInfo col_info = {
413 gst_play_sink_colorbalance_init,
417 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
418 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
419 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
420 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
423 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
424 _do_init (g_define_type_id));
427 gst_play_sink_class_init (GstPlaySinkClass * klass)
429 GObjectClass *gobject_klass;
430 GstElementClass *gstelement_klass;
431 GstBinClass *gstbin_klass;
433 gobject_klass = (GObjectClass *) klass;
434 gstelement_klass = (GstElementClass *) klass;
435 gstbin_klass = (GstBinClass *) klass;
437 gobject_klass->dispose = gst_play_sink_dispose;
438 gobject_klass->finalize = gst_play_sink_finalize;
439 gobject_klass->set_property = gst_play_sink_set_property;
440 gobject_klass->get_property = gst_play_sink_get_property;
446 * Control the behaviour of playsink.
448 g_object_class_install_property (gobject_klass, PROP_FLAGS,
449 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
450 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
451 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 * GstPlaySink:volume:
456 * Get or set the current audio stream volume. 1.0 means 100%,
457 * 0.0 means mute. This uses a linear volume scale.
460 g_object_class_install_property (gobject_klass, PROP_VOLUME,
461 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
462 0.0, VOLUME_MAX_DOUBLE, 1.0,
463 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (gobject_klass, PROP_MUTE,
465 g_param_spec_boolean ("mute", "Mute",
466 "Mute the audio channel without changing the volume", FALSE,
467 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
468 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
469 g_param_spec_string ("subtitle-font-desc",
470 "Subtitle font description",
471 "Pango font description of font "
472 "to be used for subtitle rendering", NULL,
473 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
474 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
475 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
476 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
477 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
478 "be checked for an encoding to use. If that is not set either, "
479 "ISO-8859-15 will be assumed.", NULL,
480 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
481 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
482 g_param_spec_object ("vis-plugin", "Vis plugin",
483 "the visualization element to use (NULL = default)",
484 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
486 * GstPlaySink:sample:
488 * Get the currently rendered or prerolled sample in the video sink.
489 * The #GstCaps in the sample will describe the format of the buffer.
491 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
492 g_param_spec_boxed ("sample", "Sample",
493 "The last sample (NULL = no video available)",
494 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
496 * GstPlaySink:av-offset:
498 * Control the synchronisation offset between the audio and video streams.
499 * Positive values make the audio ahead of the video and negative values make
500 * the audio go behind the video.
504 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
505 g_param_spec_int64 ("av-offset", "AV Offset",
506 "The synchronisation offset between audio and video in nanoseconds",
507 G_MININT64, G_MAXINT64, 0,
508 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
511 * GstPlaySink:video-sink:
513 * Set the used video sink element. NULL will use the default sink. playsink
514 * must be in %GST_STATE_NULL
518 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
519 g_param_spec_object ("video-sink", "Video Sink",
520 "the video output element to use (NULL = default sink)",
521 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523 * GstPlaySink:audio-sink:
525 * Set the used audio sink element. NULL will use the default sink. playsink
526 * must be in %GST_STATE_NULL
530 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
531 g_param_spec_object ("audio-sink", "Audio Sink",
532 "the audio output element to use (NULL = default sink)",
533 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
536 * GstPlaySink:text-sink:
538 * Set the used text sink element. NULL will use the default sink. playsink
539 * must be in %GST_STATE_NULL
543 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
544 g_param_spec_object ("text-sink", "Text sink",
545 "the text output element to use (NULL = default subtitleoverlay)",
546 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
549 * GstPlaySink::send-event-mode:
551 * Sets the handling method used for events received from send_event
552 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
553 * handling (push the event to all internal sinks).
557 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
558 g_param_spec_enum ("send-event-mode", "Send event mode",
559 "How to send events received in send_event function",
560 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
561 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
564 * GstPlaySink::force-aspect-ratio:
566 * Requests the video sink to enforce the video display aspect ratio.
570 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
571 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
572 "When enabled, scaling will respect original aspect ratio", TRUE,
573 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
575 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
576 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
577 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
580 * GstPlaySink::convert-sample
581 * @playsink: a #GstPlaySink
582 * @caps: the target format of the sample
584 * Action signal to retrieve the currently playing video sample in the format
585 * specified by @caps.
586 * If @caps is %NULL, no conversion will be performed and this function is
587 * equivalent to the #GstPlaySink::sample property.
589 * Returns: a #GstSample of the current video sample converted to #caps.
590 * The caps in the sample will describe the final layout of the buffer data.
591 * %NULL is returned when no current sample can be retrieved or when the
594 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
595 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
596 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
597 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
599 gst_element_class_add_pad_template (gstelement_klass,
600 gst_static_pad_template_get (&audiorawtemplate));
601 gst_element_class_add_pad_template (gstelement_klass,
602 gst_static_pad_template_get (&audiotemplate));
603 gst_element_class_add_pad_template (gstelement_klass,
604 gst_static_pad_template_get (&videorawtemplate));
605 gst_element_class_add_pad_template (gstelement_klass,
606 gst_static_pad_template_get (&videotemplate));
607 gst_element_class_add_pad_template (gstelement_klass,
608 gst_static_pad_template_get (&texttemplate));
609 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
611 "Convenience sink for multiple streams",
612 "Wim Taymans <wim.taymans@gmail.com>");
614 gstelement_klass->change_state =
615 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
616 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
617 gstelement_klass->request_new_pad =
618 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
619 gstelement_klass->release_pad =
620 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
622 gstbin_klass->handle_message =
623 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
625 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
626 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
628 _playsink_reset_segment_event_marker_id =
629 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
631 g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
632 g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
636 gst_play_sink_init (GstPlaySink * playsink)
638 GstColorBalanceChannel *channel;
641 playsink->video_sink = NULL;
642 playsink->audio_sink = NULL;
643 playsink->visualisation = NULL;
644 playsink->text_sink = NULL;
645 playsink->volume = 1.0;
646 playsink->font_desc = NULL;
647 playsink->subtitle_encoding = NULL;
648 playsink->flags = DEFAULT_FLAGS;
649 playsink->send_event_mode = MODE_DEFAULT;
650 playsink->force_aspect_ratio = TRUE;
652 playsink->stream_synchronizer =
653 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
654 gst_bin_add (GST_BIN_CAST (playsink),
655 GST_ELEMENT_CAST (playsink->stream_synchronizer));
657 g_rec_mutex_init (&playsink->lock);
658 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
661 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
663 channel->label = g_strdup ("CONTRAST");
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[0] = 0;
671 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
673 channel->label = g_strdup ("BRIGHTNESS");
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[1] = 0;
681 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
683 channel->label = g_strdup ("HUE");
684 channel->min_value = -1000;
685 channel->max_value = 1000;
686 playsink->colorbalance_channels =
687 g_list_append (playsink->colorbalance_channels, channel);
688 playsink->colorbalance_values[2] = 0;
691 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
693 channel->label = g_strdup ("SATURATION");
694 channel->min_value = -1000;
695 channel->max_value = 1000;
696 playsink->colorbalance_channels =
697 g_list_append (playsink->colorbalance_channels, channel);
698 playsink->colorbalance_values[3] = 0;
702 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
706 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
709 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
715 free_chain (GstPlayChain * chain)
719 gst_object_unref (chain->bin);
725 gst_play_sink_dispose (GObject * object)
727 GstPlaySink *playsink;
729 playsink = GST_PLAY_SINK (object);
731 if (playsink->audio_sink != NULL) {
732 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
733 gst_object_unref (playsink->audio_sink);
734 playsink->audio_sink = NULL;
736 if (playsink->video_sink != NULL) {
737 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
738 gst_object_unref (playsink->video_sink);
739 playsink->video_sink = NULL;
741 if (playsink->visualisation != NULL) {
742 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
743 gst_object_unref (playsink->visualisation);
744 playsink->visualisation = NULL;
746 if (playsink->text_sink != NULL) {
747 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
748 gst_object_unref (playsink->text_sink);
749 playsink->text_sink = NULL;
752 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
753 playsink->videodeinterlacechain = NULL;
754 free_chain ((GstPlayChain *) playsink->videochain);
755 playsink->videochain = NULL;
756 free_chain ((GstPlayChain *) playsink->audiochain);
757 playsink->audiochain = NULL;
758 free_chain ((GstPlayChain *) playsink->vischain);
759 playsink->vischain = NULL;
760 free_chain ((GstPlayChain *) playsink->textchain);
761 playsink->textchain = NULL;
763 if (playsink->audio_tee_sink) {
764 gst_object_unref (playsink->audio_tee_sink);
765 playsink->audio_tee_sink = NULL;
768 if (playsink->audio_tee_vissrc) {
769 gst_element_release_request_pad (playsink->audio_tee,
770 playsink->audio_tee_vissrc);
771 gst_object_unref (playsink->audio_tee_vissrc);
772 playsink->audio_tee_vissrc = NULL;
775 if (playsink->audio_tee_asrc) {
776 gst_element_release_request_pad (playsink->audio_tee,
777 playsink->audio_tee_asrc);
778 gst_object_unref (playsink->audio_tee_asrc);
779 playsink->audio_tee_asrc = NULL;
782 g_free (playsink->font_desc);
783 playsink->font_desc = NULL;
785 g_free (playsink->subtitle_encoding);
786 playsink->subtitle_encoding = NULL;
788 playsink->stream_synchronizer = NULL;
790 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
792 g_list_free (playsink->colorbalance_channels);
793 playsink->colorbalance_channels = NULL;
795 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
799 gst_play_sink_finalize (GObject * object)
801 GstPlaySink *playsink;
803 playsink = GST_PLAY_SINK (object);
805 g_rec_mutex_clear (&playsink->lock);
807 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
811 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
814 GstElement **elem = NULL, *old = NULL;
816 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
818 GST_PLAY_SINK_LOCK (playsink);
820 case GST_PLAY_SINK_TYPE_AUDIO:
821 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
822 elem = &playsink->audio_sink;
824 case GST_PLAY_SINK_TYPE_VIDEO:
825 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
826 elem = &playsink->video_sink;
828 case GST_PLAY_SINK_TYPE_TEXT:
829 elem = &playsink->text_sink;
837 gst_object_ref (sink);
840 GST_PLAY_SINK_UNLOCK (playsink);
844 gst_element_set_state (old, GST_STATE_NULL);
845 gst_object_unref (old);
850 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
852 GstElement *result = NULL;
853 GstElement *elem = NULL, *chainp = NULL;
855 GST_PLAY_SINK_LOCK (playsink);
857 case GST_PLAY_SINK_TYPE_AUDIO:
858 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
860 GstPlayAudioChain *chain;
861 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
862 chainp = chain->sink;
863 elem = playsink->audio_sink;
866 case GST_PLAY_SINK_TYPE_VIDEO:
867 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
869 GstPlayVideoChain *chain;
870 if ((chain = (GstPlayVideoChain *) playsink->videochain))
871 chainp = chain->sink;
872 elem = playsink->video_sink;
875 case GST_PLAY_SINK_TYPE_TEXT:
877 GstPlayTextChain *chain;
878 if ((chain = (GstPlayTextChain *) playsink->textchain))
879 chainp = chain->sink;
880 elem = playsink->text_sink;
887 /* we have an active chain with a sink, get the sink */
888 result = gst_object_ref (chainp);
890 /* nothing found, return last configured sink */
891 if (result == NULL && elem)
892 result = gst_object_ref (elem);
893 GST_PLAY_SINK_UNLOCK (playsink);
898 static GstPadProbeReturn
899 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
902 GstPlaySink *playsink;
903 GstPlayVisChain *chain;
905 playsink = GST_PLAY_SINK (user_data);
907 GST_PLAY_SINK_LOCK (playsink);
908 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
909 /* now try to change the plugin in the running vis chain */
910 if (!(chain = (GstPlayVisChain *) playsink->vischain))
913 /* unlink the old plugin and unghost the pad */
914 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
915 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
917 /* set the old plugin to NULL and remove */
918 gst_element_set_state (chain->vis, GST_STATE_NULL);
919 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
921 /* add new plugin and set state to playing */
922 chain->vis = playsink->visualisation;
923 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
924 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
927 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
928 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
931 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
932 GST_PAD_LINK_CHECK_NOTHING);
933 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
937 GST_PLAY_SINK_UNLOCK (playsink);
939 /* remove the probe and unblock the pad */
940 return GST_PAD_PROBE_REMOVE;
944 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
946 GstPlayVisChain *chain;
948 /* setting NULL means creating the default vis plugin */
950 vis = gst_element_factory_make ("goom", "vis");
952 /* simply return if we don't have a vis plugin here */
956 GST_PLAY_SINK_LOCK (playsink);
957 /* first store the new vis */
958 if (playsink->visualisation)
959 gst_object_unref (playsink->visualisation);
961 gst_object_ref_sink (vis);
962 playsink->visualisation = vis;
964 /* now try to change the plugin in the running vis chain, if we have no chain,
965 * we don't bother, any future vis chain will be created with the new vis
967 if (!(chain = (GstPlayVisChain *) playsink->vischain))
970 /* block the pad, the next time the callback is called we can change the
971 * visualisation. It's possible that this never happens or that the pad was
972 * already blocked. If the callback never happens, we don't have new data so
973 * we don't need the new vis plugin. If the pad was already blocked, the
974 * function returns FALSE but the previous pad block will do the right thing
976 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
977 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
978 gst_play_sink_vis_blocked, playsink, NULL);
980 GST_PLAY_SINK_UNLOCK (playsink);
986 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
988 GstElement *result = NULL;
989 GstPlayVisChain *chain;
991 GST_PLAY_SINK_LOCK (playsink);
992 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
993 /* we have an active chain, get the sink */
995 result = gst_object_ref (chain->vis);
997 /* nothing found, return last configured sink */
998 if (result == NULL && playsink->visualisation)
999 result = gst_object_ref (playsink->visualisation);
1000 GST_PLAY_SINK_UNLOCK (playsink);
1006 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1008 GstPlayAudioChain *chain;
1010 GST_PLAY_SINK_LOCK (playsink);
1011 playsink->volume = volume;
1012 chain = (GstPlayAudioChain *) playsink->audiochain;
1013 if (chain && chain->volume) {
1014 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1015 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1016 chain->mute, volume, playsink->mute);
1017 /* if there is a mute element or we are not muted, set the volume */
1018 if (chain->mute || !playsink->mute)
1019 g_object_set (chain->volume, "volume", volume, NULL);
1021 GST_LOG_OBJECT (playsink, "no volume element");
1022 playsink->volume_changed = TRUE;
1024 GST_PLAY_SINK_UNLOCK (playsink);
1028 gst_play_sink_get_volume (GstPlaySink * playsink)
1031 GstPlayAudioChain *chain;
1033 GST_PLAY_SINK_LOCK (playsink);
1034 chain = (GstPlayAudioChain *) playsink->audiochain;
1035 result = playsink->volume;
1036 if (chain && chain->volume) {
1037 if (chain->mute || !playsink->mute) {
1038 g_object_get (chain->volume, "volume", &result, NULL);
1039 playsink->volume = result;
1042 GST_PLAY_SINK_UNLOCK (playsink);
1048 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1050 GstPlayAudioChain *chain;
1052 GST_PLAY_SINK_LOCK (playsink);
1053 playsink->mute = mute;
1054 chain = (GstPlayAudioChain *) playsink->audiochain;
1057 g_object_set (chain->mute, "mute", mute, NULL);
1058 } else if (chain->volume) {
1060 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1062 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1066 playsink->mute_changed = TRUE;
1068 GST_PLAY_SINK_UNLOCK (playsink);
1072 gst_play_sink_get_mute (GstPlaySink * playsink)
1075 GstPlayAudioChain *chain;
1077 GST_PLAY_SINK_LOCK (playsink);
1078 chain = (GstPlayAudioChain *) playsink->audiochain;
1079 if (chain && chain->mute) {
1080 g_object_get (chain->mute, "mute", &result, NULL);
1081 playsink->mute = result;
1083 result = playsink->mute;
1085 GST_PLAY_SINK_UNLOCK (playsink);
1091 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1095 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1096 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1100 add_chain (GstPlayChain * chain, gboolean add)
1102 if (chain->added == add)
1106 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1108 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1109 /* we don't want to lose our sink status */
1110 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1119 activate_chain (GstPlayChain * chain, gboolean activate)
1123 if (chain->activated == activate)
1126 GST_OBJECT_LOCK (chain->playsink);
1127 state = GST_STATE_TARGET (chain->playsink);
1128 GST_OBJECT_UNLOCK (chain->playsink);
1131 gst_element_set_state (chain->bin, state);
1133 gst_element_set_state (chain->bin, GST_STATE_NULL);
1135 chain->activated = activate;
1141 element_is_sink (GstElement * element)
1145 GST_OBJECT_LOCK (element);
1146 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1147 GST_OBJECT_UNLOCK (element);
1149 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1154 element_has_property (GstElement * element, const gchar * pname, GType type)
1158 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1160 if (pspec == NULL) {
1161 GST_DEBUG_OBJECT (element, "no %s property", pname);
1165 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1166 g_type_is_a (pspec->value_type, type)) {
1167 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1168 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1172 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1173 "and we expected it to be of type %s", pname,
1174 g_type_name (pspec->value_type), g_type_name (type));
1181 const gchar *prop_name;
1184 } FindPropertyHelper;
1187 find_property (const GValue * item, FindPropertyHelper * helper)
1189 GstElement *element = g_value_get_object (item);
1190 if (helper->need_sink && !element_is_sink (element)) {
1194 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1198 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1199 (helper->need_sink) ? "sink" : "element");
1200 return 0; /* keep it */
1203 /* FIXME: why not move these functions into core? */
1204 /* find a sink in the hierarchy with a property named @name. This function does
1205 * not increase the refcount of the returned object and thus remains valid as
1206 * long as the bin is valid. */
1208 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1209 const gchar * name, GType expected_type)
1211 GstElement *result = NULL;
1214 if (element_has_property (obj, name, expected_type)) {
1216 } else if (GST_IS_BIN (obj)) {
1218 GValue item = { 0, };
1219 FindPropertyHelper helper = { name, expected_type, TRUE };
1221 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1222 found = gst_iterator_find_custom (it,
1223 (GCompareFunc) find_property, &item, &helper);
1224 gst_iterator_free (it);
1226 result = g_value_get_object (&item);
1227 /* we don't need the extra ref */
1228 g_value_unset (&item);
1234 /* find an object in the hierarchy with a property named @name */
1236 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1237 const gchar * name, GType expected_type)
1239 GstElement *result = NULL;
1242 if (GST_IS_BIN (obj)) {
1244 GValue item = { 0, };
1245 FindPropertyHelper helper = { name, expected_type, FALSE };
1247 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1248 found = gst_iterator_find_custom (it,
1249 (GCompareFunc) find_property, &item, &helper);
1250 gst_iterator_free (it);
1252 result = g_value_dup_object (&item);
1253 g_value_unset (&item);
1256 if (element_has_property (obj, name, expected_type)) {
1258 gst_object_ref (obj);
1265 do_async_start (GstPlaySink * playsink)
1267 GstMessage *message;
1269 if (!playsink->need_async_start) {
1270 GST_INFO_OBJECT (playsink, "no async_start needed");
1274 playsink->async_pending = TRUE;
1276 GST_INFO_OBJECT (playsink, "Sending async_start message");
1277 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1278 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1279 (playsink), message);
1283 do_async_done (GstPlaySink * playsink)
1285 GstMessage *message;
1287 if (playsink->async_pending) {
1288 GST_INFO_OBJECT (playsink, "Sending async_done message");
1290 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1291 GST_CLOCK_TIME_NONE);
1292 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1293 (playsink), message);
1295 playsink->async_pending = FALSE;
1298 playsink->need_async_start = FALSE;
1301 /* try to change the state of an element. This function returns the element when
1302 * the state change could be performed. When this function returns NULL an error
1303 * occured and the element is unreffed if @unref is TRUE. */
1305 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1307 GstStateChangeReturn ret;
1310 ret = gst_element_set_state (element, GST_STATE_READY);
1311 if (ret == GST_STATE_CHANGE_FAILURE) {
1312 GST_DEBUG_OBJECT (playsink, "failed state change..");
1313 gst_element_set_state (element, GST_STATE_NULL);
1315 gst_object_unref (element);
1322 /* make the element (bin) that contains the elements needed to perform
1323 * video display. Only used for *raw* video streams.
1325 * +------------------------------------------------------------+
1327 * | +-------+ +----------+ +----------+ +---------+ |
1328 * | | queue | |colorspace| |videoscale| |videosink| |
1329 * | +-sink src-sink src-sink src-sink | |
1330 * | | +-------+ +----------+ +----------+ +---------+ |
1332 * +------------------------------------------------------------+
1335 static GstPlayVideoDeinterlaceChain *
1336 gen_video_deinterlace_chain (GstPlaySink * playsink)
1338 GstPlayVideoDeinterlaceChain *chain;
1341 GstElement *head = NULL, *prev = NULL;
1343 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1344 chain->chain.playsink = playsink;
1346 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1348 /* create a bin to hold objects, as we create them we add them to this bin so
1349 * that when something goes wrong we only need to unref the bin */
1350 chain->chain.bin = gst_bin_new ("vdbin");
1351 bin = GST_BIN_CAST (chain->chain.bin);
1352 gst_object_ref_sink (bin);
1354 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1355 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1356 if (chain->conv == NULL) {
1357 post_missing_element_message (playsink, COLORSPACE);
1358 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1359 (_("Missing element '%s' - check your GStreamer installation."),
1360 COLORSPACE), ("video rendering might fail"));
1362 gst_bin_add (bin, chain->conv);
1367 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1368 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1369 if (chain->deinterlace == NULL) {
1370 chain->deinterlace =
1371 gst_element_factory_make ("avdeinterlace", "deinterlace");
1373 if (chain->deinterlace == NULL) {
1374 post_missing_element_message (playsink, "deinterlace");
1375 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1376 (_("Missing element '%s' - check your GStreamer installation."),
1377 "deinterlace"), ("deinterlacing won't work"));
1379 gst_bin_add (bin, chain->deinterlace);
1381 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1382 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1385 head = chain->deinterlace;
1387 prev = chain->deinterlace;
1391 pad = gst_element_get_static_pad (head, "sink");
1392 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1393 gst_object_unref (pad);
1395 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1399 pad = gst_element_get_static_pad (prev, "src");
1400 chain->srcpad = gst_ghost_pad_new ("src", pad);
1401 gst_object_unref (pad);
1403 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1406 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1407 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1413 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1414 (NULL), ("Failed to configure the video deinterlace chain."));
1415 free_chain ((GstPlayChain *) chain);
1421 is_valid_color_balance_element (GstColorBalance * bal)
1423 gboolean have_brightness = FALSE;
1424 gboolean have_contrast = FALSE;
1425 gboolean have_hue = FALSE;
1426 gboolean have_saturation = FALSE;
1427 const GList *channels, *l;
1429 channels = gst_color_balance_list_channels (bal);
1430 for (l = channels; l; l = l->next) {
1431 GstColorBalanceChannel *ch = l->data;
1433 if (g_strrstr (ch->label, "BRIGHTNESS"))
1434 have_brightness = TRUE;
1435 else if (g_strrstr (ch->label, "CONTRAST"))
1436 have_contrast = TRUE;
1437 else if (g_strrstr (ch->label, "HUE"))
1439 else if (g_strrstr (ch->label, "SATURATION"))
1440 have_saturation = TRUE;
1443 return have_brightness && have_contrast && have_hue && have_saturation;
1447 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1450 GstColorBalance *cb, **cb_out = user_data;
1452 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1453 valid = is_valid_color_balance_element (cb);
1456 && gst_color_balance_get_balance_type (*cb_out) ==
1457 GST_COLOR_BALANCE_SOFTWARE) {
1458 gst_object_unref (*cb_out);
1459 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1460 } else if (!*cb_out) {
1461 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1466 static GstColorBalance *
1467 find_color_balance_element (GstElement * element)
1470 GstColorBalance *cb = NULL;
1472 if (GST_IS_COLOR_BALANCE (element)
1473 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1474 return GST_COLOR_BALANCE (gst_object_ref (element));
1475 else if (!GST_IS_BIN (element))
1478 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1479 GST_TYPE_COLOR_BALANCE);
1480 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1481 &cb) == GST_ITERATOR_RESYNC)
1482 gst_iterator_resync (it);
1483 gst_iterator_free (it);
1489 colorbalance_value_changed_cb (GstColorBalance * balance,
1490 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1495 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1496 GstColorBalanceChannel *proxy = l->data;
1498 if (g_strrstr (channel->label, proxy->label)) {
1501 /* Convert to [0, 1] range */
1504 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1505 (gdouble) channel->min_value);
1506 /* Convert to proxy range */
1508 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1509 (gdouble) proxy->min_value);
1510 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1512 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1513 playsink->colorbalance_values[i]);
1520 update_colorbalance (GstPlaySink * playsink)
1522 GstColorBalance *balance = NULL;
1526 GST_OBJECT_LOCK (playsink);
1527 if (playsink->colorbalance_element) {
1529 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1531 GST_OBJECT_UNLOCK (playsink);
1535 g_signal_handlers_block_by_func (balance,
1536 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1538 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1539 GstColorBalanceChannel *proxy = l->data;
1540 GstColorBalanceChannel *channel = NULL;
1541 const GList *channels, *k;
1543 channels = gst_color_balance_list_channels (balance);
1544 for (k = channels; k; k = k->next) {
1545 GstColorBalanceChannel *tmp = k->data;
1547 if (g_strrstr (tmp->label, proxy->label)) {
1555 gst_color_balance_set_value (balance, channel,
1556 playsink->colorbalance_values[i]);
1559 g_signal_handlers_unblock_by_func (balance,
1560 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1562 gst_object_unref (balance);
1565 /* make the element (bin) that contains the elements needed to perform
1568 * +------------------------------------------------------------+
1570 * | +-------+ +----------+ +----------+ +---------+ |
1571 * | | queue | |colorspace| |videoscale| |videosink| |
1572 * | +-sink src-sink src-sink src-sink | |
1573 * | | +-------+ +----------+ +----------+ +---------+ |
1575 * +------------------------------------------------------------+
1578 static GstPlayVideoChain *
1579 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1581 GstPlayVideoChain *chain;
1584 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1586 chain = g_new0 (GstPlayVideoChain, 1);
1587 chain->chain.playsink = playsink;
1588 chain->chain.raw = raw;
1590 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1592 if (playsink->video_sink) {
1593 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1594 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1596 /* only try fallback if no specific sink was chosen */
1597 if (chain->sink == NULL) {
1598 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1599 elem = gst_element_factory_make ("autovideosink", "videosink");
1600 chain->sink = try_element (playsink, elem, TRUE);
1602 if (chain->sink == NULL) {
1603 /* if default sink from config.h is different then try it too */
1604 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1605 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1606 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1607 chain->sink = try_element (playsink, elem, TRUE);
1611 playsink->video_sink = gst_object_ref (chain->sink);
1613 if (chain->sink == NULL)
1617 /* if we can disable async behaviour of the sink, we can avoid adding a
1618 * queue for the audio chain. */
1620 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1623 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1624 async, GST_ELEMENT_NAME (elem));
1625 g_object_set (elem, "async", async, NULL);
1626 chain->async = async;
1628 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1629 chain->async = TRUE;
1632 /* Make sure the aspect ratio is kept */
1634 gst_play_sink_find_property_sinks (playsink, chain->sink,
1635 "force-aspect-ratio", G_TYPE_BOOLEAN);
1637 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1640 /* find ts-offset element */
1641 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1642 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1645 /* create a bin to hold objects, as we create them we add them to this bin so
1646 * that when something goes wrong we only need to unref the bin */
1647 chain->chain.bin = gst_bin_new ("vbin");
1648 bin = GST_BIN_CAST (chain->chain.bin);
1649 gst_object_ref_sink (bin);
1650 gst_bin_add (bin, chain->sink);
1652 /* Get the VideoOverlay element */
1654 GstVideoOverlay *overlay = NULL;
1656 GST_OBJECT_LOCK (playsink);
1657 if (playsink->overlay_element)
1658 gst_object_unref (playsink->overlay_element);
1659 playsink->overlay_element =
1660 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1661 GST_TYPE_VIDEO_OVERLAY));
1662 if (playsink->overlay_element)
1663 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1664 GST_OBJECT_UNLOCK (playsink);
1667 if (playsink->overlay_handle_set)
1668 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1669 if (playsink->overlay_handle_events_set)
1670 gst_video_overlay_handle_events (overlay,
1671 playsink->overlay_handle_events);
1672 if (playsink->overlay_render_rectangle_set)
1673 gst_video_overlay_set_render_rectangle (overlay,
1674 playsink->overlay_x, playsink->overlay_y,
1675 playsink->overlay_width, playsink->overlay_height);
1676 gst_object_unref (overlay);
1680 /* decouple decoder from sink, this improves playback quite a lot since the
1681 * decoder can continue while the sink blocks for synchronisation. We don't
1682 * need a lot of buffers as this consumes a lot of memory and we don't want
1683 * too little because else we would be context switching too quickly. */
1684 chain->queue = gst_element_factory_make ("queue", "vqueue");
1685 if (chain->queue == NULL) {
1686 post_missing_element_message (playsink, "queue");
1687 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1688 (_("Missing element '%s' - check your GStreamer installation."),
1689 "queue"), ("video rendering might be suboptimal"));
1693 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1694 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1695 gst_bin_add (bin, chain->queue);
1696 head = prev = chain->queue;
1699 GST_OBJECT_LOCK (playsink);
1700 if (playsink->colorbalance_element) {
1701 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1702 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1703 gst_object_unref (playsink->colorbalance_element);
1705 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1706 if (playsink->colorbalance_element) {
1707 g_signal_connect (playsink->colorbalance_element, "value-changed",
1708 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1710 GST_OBJECT_UNLOCK (playsink);
1712 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1713 || (!playsink->colorbalance_element
1714 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1715 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1716 gboolean use_balance = !playsink->colorbalance_element
1717 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1719 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1721 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1722 "use-converters", use_converters, "use-balance", use_balance, NULL);
1724 GST_OBJECT_LOCK (playsink);
1725 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1726 playsink->colorbalance_element =
1727 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1728 (chain->conv)->balance));
1729 GST_OBJECT_UNLOCK (playsink);
1731 gst_bin_add (bin, chain->conv);
1733 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1734 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1742 update_colorbalance (playsink);
1745 GST_DEBUG_OBJECT (playsink, "linking to sink");
1746 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1747 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1751 pad = gst_element_get_static_pad (head, "sink");
1752 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1754 /* sending audio/video flushes break stream changes when the pipeline
1755 * is paused and played again in 0.10 */
1757 gst_pad_set_event_function (chain->sinkpad,
1758 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1759 gst_pad_set_chain_function (chain->sinkpad,
1760 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1763 gst_object_unref (pad);
1764 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1771 if (!elem && !playsink->video_sink) {
1772 post_missing_element_message (playsink, "autovideosink");
1773 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1774 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1775 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1776 (_("Both autovideosink and %s elements are missing."),
1777 DEFAULT_VIDEOSINK), (NULL));
1779 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1780 (_("The autovideosink element is missing.")), (NULL));
1783 if (playsink->video_sink) {
1784 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1785 (_("Configured videosink %s is not working."),
1786 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1787 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1788 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1789 (_("Both autovideosink and %s elements are not working."),
1790 DEFAULT_VIDEOSINK), (NULL));
1792 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1793 (_("The autovideosink element is not working.")), (NULL));
1796 free_chain ((GstPlayChain *) chain);
1802 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1803 (NULL), ("Failed to configure the video sink."));
1804 /* checking sink made it READY */
1805 gst_element_set_state (chain->sink, GST_STATE_NULL);
1806 /* Remove chain from the bin to allow reuse later */
1807 gst_bin_remove (bin, chain->sink);
1808 free_chain ((GstPlayChain *) chain);
1814 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1817 GstPlayVideoChain *chain;
1818 GstStateChangeReturn ret;
1820 chain = playsink->videochain;
1822 chain->chain.raw = raw;
1824 /* if the chain was active we don't do anything */
1825 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1828 /* try to set the sink element to READY again */
1829 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1830 if (ret == GST_STATE_CHANGE_FAILURE)
1833 /* Get the VideoOverlay element */
1835 GstVideoOverlay *overlay = NULL;
1837 GST_OBJECT_LOCK (playsink);
1838 if (playsink->overlay_element)
1839 gst_object_unref (playsink->overlay_element);
1840 playsink->overlay_element =
1841 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1842 GST_TYPE_VIDEO_OVERLAY));
1843 if (playsink->overlay_element)
1844 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1845 GST_OBJECT_UNLOCK (playsink);
1848 if (playsink->overlay_handle_set)
1849 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1850 if (playsink->overlay_handle_events_set)
1851 gst_video_overlay_handle_events (overlay,
1852 playsink->overlay_handle_events);
1853 if (playsink->overlay_render_rectangle_set)
1854 gst_video_overlay_set_render_rectangle (overlay,
1855 playsink->overlay_x, playsink->overlay_y,
1856 playsink->overlay_width, playsink->overlay_height);
1857 gst_object_unref (overlay);
1861 /* find ts-offset element */
1862 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1863 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1866 /* if we can disable async behaviour of the sink, we can avoid adding a
1867 * queue for the audio chain. */
1869 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1872 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1873 async, GST_ELEMENT_NAME (elem));
1874 g_object_set (elem, "async", async, NULL);
1875 chain->async = async;
1877 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1878 chain->async = TRUE;
1881 /* Make sure the aspect ratio is kept */
1883 gst_play_sink_find_property_sinks (playsink, chain->sink,
1884 "force-aspect-ratio", G_TYPE_BOOLEAN);
1886 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1889 GST_OBJECT_LOCK (playsink);
1890 if (playsink->colorbalance_element) {
1891 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1892 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1893 gst_object_unref (playsink->colorbalance_element);
1895 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1896 if (playsink->colorbalance_element) {
1897 g_signal_connect (playsink->colorbalance_element, "value-changed",
1898 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1900 GST_OBJECT_UNLOCK (playsink);
1903 gboolean use_balance = !playsink->colorbalance_element
1904 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1906 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1908 GST_OBJECT_LOCK (playsink);
1909 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1910 playsink->colorbalance_element =
1911 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1912 (chain->conv)->balance));
1913 GST_OBJECT_UNLOCK (playsink);
1916 update_colorbalance (playsink);
1922 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1926 GstStructure *structure;
1927 event = gst_event_new_segment (segment);
1928 structure = gst_event_writable_structure (event);
1929 gst_structure_id_set (structure,
1930 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1935 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1936 const gchar * sink_type,
1937 gboolean * sink_ignore_wrong_state,
1938 gboolean * sink_custom_flush_finished,
1939 gboolean * sink_pending_flush, GstSegment * sink_segment)
1941 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1943 const GstStructure *structure = gst_event_get_structure (event);
1945 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1946 gchar *custom_flush;
1947 gchar *custom_flush_finish;
1949 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1950 custom_flush_finish =
1951 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1952 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1953 GST_DEBUG_OBJECT (pad,
1954 "Custom %s flush event received, marking to flush %s", sink_type,
1956 GST_PLAY_SINK_LOCK (playsink);
1957 *sink_ignore_wrong_state = TRUE;
1958 *sink_custom_flush_finished = FALSE;
1959 GST_PLAY_SINK_UNLOCK (playsink);
1960 } else if (strcmp (gst_structure_get_name (structure),
1961 custom_flush_finish) == 0) {
1962 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1964 GST_PLAY_SINK_LOCK (playsink);
1965 *sink_pending_flush = TRUE;
1966 *sink_custom_flush_finished = TRUE;
1967 GST_PLAY_SINK_UNLOCK (playsink);
1970 g_free (custom_flush);
1971 g_free (custom_flush_finish);
1972 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1973 GST_PLAY_SINK_LOCK (playsink);
1974 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1976 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1977 GST_PLAY_SINK_UNLOCK (playsink);
1980 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1981 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
1983 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1984 const GstSegment *segment;
1986 gst_event_parse_segment (event, &segment);
1987 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1989 GST_PLAY_SINK_LOCK (playsink);
1990 if (sink_segment->format != segment->format) {
1991 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1993 gst_format_get_name (sink_segment->format),
1994 gst_format_get_name (segment->format));
1995 gst_segment_init (sink_segment, segment->format);
1998 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1999 sink_type, sink_segment);
2000 gst_segment_copy_into (&playsink->text_segment, sink_segment);
2001 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
2002 sink_type, sink_segment);
2003 GST_PLAY_SINK_UNLOCK (playsink);
2006 gst_event_unref (event);
2007 gst_object_unref (playsink);
2011 static GstFlowReturn
2012 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2013 const gchar * sink_type,
2014 gboolean * sink_ignore_wrong_state,
2015 gboolean * sink_custom_flush_finished,
2016 gboolean * sink_pending_flush, GstSegment * sink_segment)
2018 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2019 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2022 GST_PLAY_SINK_LOCK (playsink);
2024 if (*sink_pending_flush) {
2026 GstStructure *structure;
2028 *sink_pending_flush = FALSE;
2030 GST_PLAY_SINK_UNLOCK (playsink);
2032 /* make the bin drop all cached data.
2033 * This event will be dropped on the src pad, if any. */
2034 event = gst_event_new_flush_start ();
2035 structure = gst_event_writable_structure (event);
2036 gst_structure_id_set (structure,
2037 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2039 GST_DEBUG_OBJECT (pad,
2040 "Pushing %s flush-start event with reset segment marker set: %"
2041 GST_PTR_FORMAT, sink_type, event);
2042 gst_pad_send_event (pad, event);
2044 /* make queue drop all cached data.
2045 * This event will be dropped on the src pad. */
2046 event = gst_event_new_flush_stop (TRUE);
2047 structure = gst_event_writable_structure (event);
2048 gst_structure_id_set (structure,
2049 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2051 GST_DEBUG_OBJECT (pad,
2052 "Pushing %s flush-stop event with reset segment marker set: %"
2053 GST_PTR_FORMAT, sink_type, event);
2054 gst_pad_send_event (pad, event);
2056 /* Re-sync queue segment info after flush-stop.
2057 * This event will be dropped on the src pad. */
2058 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2061 _generate_update_newsegment_event (pad, sink_segment, &event1);
2062 GST_DEBUG_OBJECT (playsink,
2063 "Pushing segment event with reset "
2064 "segment marker set: %" GST_PTR_FORMAT, event1);
2065 gst_pad_send_event (pad, event1);
2068 GST_PLAY_SINK_UNLOCK (playsink);
2071 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2073 GST_PLAY_SINK_LOCK (playsink);
2074 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2075 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2077 if (*sink_custom_flush_finished) {
2078 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2079 "wrong state for %s", sink_type);
2080 *sink_ignore_wrong_state = FALSE;
2085 GST_PLAY_SINK_UNLOCK (playsink);
2087 gst_object_unref (playsink);
2088 gst_object_unref (tbin);
2092 /* sending audio/video flushes break stream changes when the pipeline
2093 * is paused and played again in 0.10 */
2096 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2098 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2099 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2102 ret = gst_play_sink_sink_event (pad, event, "video",
2103 &playsink->video_ignore_wrong_state,
2104 &playsink->video_custom_flush_finished,
2105 &playsink->video_pending_flush, &playsink->video_segment);
2107 gst_object_unref (playsink);
2108 gst_object_unref (tbin);
2112 static GstFlowReturn
2113 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2115 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2116 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2119 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2120 &playsink->video_ignore_wrong_state,
2121 &playsink->video_custom_flush_finished,
2122 &playsink->video_pending_flush, &playsink->video_segment);
2124 gst_object_unref (playsink);
2125 gst_object_unref (tbin);
2130 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2132 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2133 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2136 ret = gst_play_sink_sink_event (pad, event, "audio",
2137 &playsink->audio_ignore_wrong_state,
2138 &playsink->audio_custom_flush_finished,
2139 &playsink->audio_pending_flush, &playsink->audio_segment);
2141 gst_object_unref (playsink);
2142 gst_object_unref (tbin);
2146 static GstFlowReturn
2147 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2149 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2150 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2153 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2154 &playsink->audio_ignore_wrong_state,
2155 &playsink->audio_custom_flush_finished,
2156 &playsink->audio_pending_flush, &playsink->audio_segment);
2158 gst_object_unref (playsink);
2159 gst_object_unref (tbin);
2165 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2168 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2171 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2172 &playsink->text_ignore_wrong_state,
2173 &playsink->text_custom_flush_finished,
2174 &playsink->text_pending_flush, &playsink->text_segment);
2176 gst_object_unref (playsink);
2181 static GstFlowReturn
2182 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2186 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2188 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2189 &playsink->text_ignore_wrong_state,
2190 &playsink->text_custom_flush_finished,
2191 &playsink->text_pending_flush, &playsink->text_segment);
2193 gst_object_unref (playsink);
2198 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2202 const GstStructure *structure;
2204 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2206 structure = gst_event_get_structure (event);
2209 gst_structure_id_has_field (structure,
2210 _playsink_reset_segment_event_marker_id)) {
2211 /* the events marked with a reset segment marker
2212 * are sent internally to reset the queue and
2213 * must be dropped here */
2214 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2215 "segment marker set: %" GST_PTR_FORMAT, event);
2220 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2223 gst_event_unref (event);
2227 /* make an element for playback of video with subtitles embedded.
2228 * Only used for *raw* video streams.
2230 * +--------------------------------------------+
2232 * | +--------+ +-----------------+ |
2233 * | | queue | | subtitleoverlay | |
2234 * video--src sink---video_sink | |
2235 * | +--------+ | src--src
2236 * text------------------text_sink | |
2237 * | +-----------------+ |
2238 * +--------------------------------------------+
2241 static GstPlayTextChain *
2242 gen_text_chain (GstPlaySink * playsink)
2244 GstPlayTextChain *chain;
2247 GstPad *videosinkpad, *textsinkpad, *srcpad;
2249 chain = g_new0 (GstPlayTextChain, 1);
2250 chain->chain.playsink = playsink;
2252 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2254 chain->chain.bin = gst_bin_new ("tbin");
2255 bin = GST_BIN_CAST (chain->chain.bin);
2256 gst_object_ref_sink (bin);
2258 videosinkpad = textsinkpad = srcpad = NULL;
2260 /* first try to hook the text pad to the custom sink */
2261 if (playsink->text_sink) {
2262 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2263 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2266 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2269 /* make sure the sparse subtitles don't participate in the preroll */
2270 g_object_set (elem, "async", FALSE, NULL);
2271 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2272 gst_bin_add (bin, chain->sink);
2273 /* NOTE streamsynchronizer needs streams decoupled */
2274 /* make a little queue */
2275 chain->queue = gst_element_factory_make ("queue", "subqueue");
2276 if (chain->queue == NULL) {
2277 post_missing_element_message (playsink, "queue");
2278 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2279 (_("Missing element '%s' - check your GStreamer installation."),
2280 "queue"), ("rendering might be suboptimal"));
2282 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2283 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2284 "silent", TRUE, NULL);
2285 gst_bin_add (bin, chain->queue);
2287 /* we have a custom sink, this will be our textsinkpad */
2288 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2289 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2290 /* we're all fine now and we can add the sink to the chain */
2291 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2292 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2294 GST_WARNING_OBJECT (playsink,
2295 "can't find a sink pad on custom text sink");
2296 gst_bin_remove (bin, chain->sink);
2297 gst_bin_remove (bin, chain->queue);
2299 chain->queue = NULL;
2301 /* try to set sync to true but it's no biggie when we can't */
2302 if (chain->sink && (elem =
2303 gst_play_sink_find_property_sinks (playsink, chain->sink,
2304 "sync", G_TYPE_BOOLEAN)))
2305 g_object_set (elem, "sync", TRUE, NULL);
2308 gst_bin_remove (bin, chain->sink);
2310 GST_WARNING_OBJECT (playsink,
2311 "can't find async property in custom text sink");
2314 if (textsinkpad == NULL) {
2315 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2316 (_("Custom text sink element is not usable.")),
2317 ("fallback to default subtitleoverlay"));
2321 if (textsinkpad == NULL) {
2322 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2323 /* make a little queue */
2324 chain->queue = gst_element_factory_make ("queue", "vqueue");
2325 if (chain->queue == NULL) {
2326 post_missing_element_message (playsink, "queue");
2327 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2328 (_("Missing element '%s' - check your GStreamer installation."),
2329 "queue"), ("video rendering might be suboptimal"));
2331 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2332 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2333 "silent", TRUE, NULL);
2334 gst_bin_add (bin, chain->queue);
2335 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2339 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2340 if (chain->overlay == NULL) {
2341 post_missing_element_message (playsink, "subtitleoverlay");
2342 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2343 (_("Missing element '%s' - check your GStreamer installation."),
2344 "subtitleoverlay"), ("subtitle rendering disabled"));
2346 GstElement *element;
2348 gst_bin_add (bin, chain->overlay);
2350 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2351 if (playsink->font_desc) {
2352 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2353 playsink->font_desc, NULL);
2355 if (playsink->subtitle_encoding) {
2356 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2357 playsink->subtitle_encoding, NULL);
2360 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2361 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2363 /* make another little queue to decouple streams */
2364 element = gst_element_factory_make ("queue", "subqueue");
2365 if (element == NULL) {
2366 post_missing_element_message (playsink, "queue");
2367 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2368 (_("Missing element '%s' - check your GStreamer installation."),
2369 "queue"), ("rendering might be suboptimal"));
2371 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2372 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2373 "silent", TRUE, NULL);
2374 gst_bin_add (bin, element);
2375 if (gst_element_link_pads_full (element, "src", chain->overlay,
2376 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2377 textsinkpad = gst_element_get_static_pad (element, "sink");
2378 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2380 gst_bin_remove (bin, chain->sink);
2381 gst_bin_remove (bin, chain->overlay);
2383 chain->overlay = NULL;
2384 gst_object_unref (videosinkpad);
2385 videosinkpad = NULL;
2392 if (videosinkpad == NULL) {
2393 /* if we still don't have a videosink, we don't have an overlay. the only
2394 * thing we can do is insert an identity and ghost the src
2396 chain->identity = gst_element_factory_make ("identity", "tidentity");
2397 if (chain->identity == NULL) {
2398 post_missing_element_message (playsink, "identity");
2399 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2400 (_("Missing element '%s' - check your GStreamer installation."),
2401 "identity"), (NULL));
2403 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2404 g_object_set (chain->identity, "silent", TRUE, NULL);
2405 gst_bin_add (bin, chain->identity);
2406 srcpad = gst_element_get_static_pad (chain->identity, "src");
2407 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2411 /* expose the ghostpads */
2413 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2414 gst_object_unref (videosinkpad);
2415 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2418 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2419 gst_object_unref (textsinkpad);
2421 gst_pad_set_event_function (chain->textsinkpad,
2422 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2423 gst_pad_set_chain_function (chain->textsinkpad,
2424 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2426 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2429 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2430 gst_object_unref (srcpad);
2432 gst_pad_set_event_function (chain->srcpad,
2433 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2435 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2442 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2446 g_object_get (object, "volume", &vol, NULL);
2447 playsink->volume = vol;
2449 g_object_notify (G_OBJECT (playsink), "volume");
2453 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2457 g_object_get (object, "mute", &mute, NULL);
2458 playsink->mute = mute;
2460 g_object_notify (G_OBJECT (playsink), "mute");
2463 /* make the chain that contains the elements needed to perform
2466 * We add a tee as the first element so that we can link the visualisation chain
2467 * to it when requested.
2469 * +-------------------------------------------------------------+
2471 * | +---------+ +----------+ +---------+ +---------+ |
2472 * | |audioconv| |audioscale| | volume | |audiosink| |
2473 * | +-srck src-sink src-sink src-sink | |
2474 * | | +---------+ +----------+ +---------+ +---------+ |
2476 * +-------------------------------------------------------------+
2478 static GstPlayAudioChain *
2479 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2481 GstPlayAudioChain *chain;
2483 gboolean have_volume;
2485 GstElement *head, *prev, *elem = NULL;
2487 chain = g_new0 (GstPlayAudioChain, 1);
2488 chain->chain.playsink = playsink;
2489 chain->chain.raw = raw;
2491 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2493 if (playsink->audio_sink) {
2494 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2495 playsink->audio_sink);
2496 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2498 /* only try fallback if no specific sink was chosen */
2499 if (chain->sink == NULL) {
2500 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2501 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2502 chain->sink = try_element (playsink, elem, TRUE);
2504 if (chain->sink == NULL) {
2505 /* if default sink from config.h is different then try it too */
2506 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2507 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2508 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2509 chain->sink = try_element (playsink, elem, TRUE);
2513 playsink->audio_sink = gst_object_ref (chain->sink);
2515 if (chain->sink == NULL)
2518 chain->chain.bin = gst_bin_new ("abin");
2519 bin = GST_BIN_CAST (chain->chain.bin);
2520 gst_object_ref_sink (bin);
2521 gst_bin_add (bin, chain->sink);
2523 /* we have to add a queue when we need to decouple for the video sink in
2524 * visualisations and for streamsynchronizer */
2525 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2526 chain->queue = gst_element_factory_make ("queue", "aqueue");
2527 if (chain->queue == NULL) {
2528 post_missing_element_message (playsink, "queue");
2529 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2530 (_("Missing element '%s' - check your GStreamer installation."),
2531 "queue"), ("audio playback and visualizations might not work"));
2535 g_object_set (chain->queue, "silent", TRUE, NULL);
2536 gst_bin_add (bin, chain->queue);
2537 prev = head = chain->queue;
2540 /* find ts-offset element */
2541 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2542 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2545 /* check if the sink, or something within the sink, has the volume property.
2546 * If it does we don't need to add a volume element. */
2548 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2551 chain->volume = elem;
2553 g_signal_connect (chain->volume, "notify::volume",
2554 G_CALLBACK (notify_volume_cb), playsink);
2556 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2558 chain->sink_volume = TRUE;
2559 /* if the sink also has a mute property we can use this as well. We'll only
2560 * use the mute property if there is a volume property. We can simulate the
2561 * mute with the volume otherwise. */
2563 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2566 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2567 g_signal_connect (chain->mute, "notify::mute",
2568 G_CALLBACK (notify_mute_cb), playsink);
2570 /* use the sink to control the volume and mute */
2571 if (playsink->volume_changed) {
2572 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2573 playsink->volume_changed = FALSE;
2575 if (playsink->mute_changed) {
2577 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2580 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2582 playsink->mute_changed = FALSE;
2585 /* no volume, we need to add a volume element when we can */
2586 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2587 have_volume = FALSE;
2588 chain->sink_volume = FALSE;
2591 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2592 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2593 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2594 gboolean use_volume =
2595 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2596 GST_DEBUG_OBJECT (playsink,
2597 "creating audioconvert with use-converters %d, use-volume %d",
2598 use_converters, use_volume);
2600 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2601 "use-converters", use_converters, "use-volume", use_volume, NULL);
2602 gst_bin_add (bin, chain->conv);
2604 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2605 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2612 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2613 GstPlaySinkAudioConvert *conv =
2614 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2617 chain->volume = conv->volume;
2620 g_signal_connect (chain->volume, "notify::volume",
2621 G_CALLBACK (notify_volume_cb), playsink);
2623 /* volume also has the mute property */
2624 chain->mute = chain->volume;
2625 g_signal_connect (chain->mute, "notify::mute",
2626 G_CALLBACK (notify_mute_cb), playsink);
2628 /* configure with the latest volume and mute */
2629 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2631 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2637 /* we only have to link to the previous element if we have something in
2638 * front of the sink */
2639 GST_DEBUG_OBJECT (playsink, "linking to sink");
2640 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2641 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2645 /* post a warning if we have no way to configure the volume */
2647 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2648 (_("No volume control found")), ("Volume/mute is not available"));
2651 /* and ghost the sinkpad of the headmost element */
2652 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2653 pad = gst_element_get_static_pad (head, "sink");
2654 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2656 /* sending audio/video flushes break stream changes when the pipeline
2657 * is paused and played again in 0.10 */
2659 gst_pad_set_event_function (chain->sinkpad,
2660 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2661 gst_pad_set_chain_function (chain->sinkpad,
2662 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2665 gst_object_unref (pad);
2666 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2673 if (!elem && !playsink->audio_sink) {
2674 post_missing_element_message (playsink, "autoaudiosink");
2675 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2676 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2677 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2678 (_("Both autoaudiosink and %s elements are missing."),
2679 DEFAULT_AUDIOSINK), (NULL));
2681 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2682 (_("The autoaudiosink element is missing.")), (NULL));
2685 if (playsink->audio_sink) {
2686 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2687 (_("Configured audiosink %s is not working."),
2688 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2689 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2690 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2691 (_("Both autoaudiosink and %s elements are not working."),
2692 DEFAULT_AUDIOSINK), (NULL));
2694 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2695 (_("The autoaudiosink element is not working.")), (NULL));
2698 free_chain ((GstPlayChain *) chain);
2703 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2704 (NULL), ("Failed to configure the audio sink."));
2705 /* checking sink made it READY */
2706 gst_element_set_state (chain->sink, GST_STATE_NULL);
2707 /* Remove chain from the bin to allow reuse later */
2708 gst_bin_remove (bin, chain->sink);
2709 free_chain ((GstPlayChain *) chain);
2715 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2718 GstPlayAudioChain *chain;
2719 GstStateChangeReturn ret;
2720 GstPlaySinkAudioConvert *conv;
2722 chain = playsink->audiochain;
2723 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2725 chain->chain.raw = raw;
2727 /* if the chain was active we don't do anything */
2728 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2731 /* try to set the sink element to READY again */
2732 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2733 if (ret == GST_STATE_CHANGE_FAILURE)
2736 /* find ts-offset element */
2737 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2738 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2741 /* check if the sink, or something within the sink, has the volume property.
2742 * If it does we don't need to add a volume element. */
2744 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2747 chain->volume = elem;
2749 if (playsink->volume_changed) {
2750 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2752 /* use the sink to control the volume */
2753 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2754 playsink->volume_changed = FALSE;
2757 g_signal_connect (chain->volume, "notify::volume",
2758 G_CALLBACK (notify_volume_cb), playsink);
2759 /* if the sink also has a mute property we can use this as well. We'll only
2760 * use the mute property if there is a volume property. We can simulate the
2761 * mute with the volume otherwise. */
2763 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2766 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2767 g_signal_connect (chain->mute, "notify::mute",
2768 G_CALLBACK (notify_mute_cb), playsink);
2771 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2773 /* no volume, we need to add a volume element when we can */
2774 g_object_set (chain->conv, "use-volume",
2775 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2776 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2778 /* Disconnect signals */
2779 disconnect_chain (chain, playsink);
2781 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2782 chain->volume = conv->volume;
2783 chain->mute = chain->volume;
2785 g_signal_connect (chain->volume, "notify::volume",
2786 G_CALLBACK (notify_volume_cb), playsink);
2788 g_signal_connect (chain->mute, "notify::mute",
2789 G_CALLBACK (notify_mute_cb), playsink);
2791 /* configure with the latest volume and mute */
2792 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2793 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2796 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2802 * +-------------------------------------------------------------------+
2804 * | +----------+ +------------+ +----------+ +-------+ |
2805 * | | visqueue | | audioconv | | audiores | | vis | |
2806 * | +-sink src-sink + samp src-sink src-sink src-+ |
2807 * | | +----------+ +------------+ +----------+ +-------+ | |
2809 * +-------------------------------------------------------------------+
2812 static GstPlayVisChain *
2813 gen_vis_chain (GstPlaySink * playsink)
2815 GstPlayVisChain *chain;
2821 chain = g_new0 (GstPlayVisChain, 1);
2822 chain->chain.playsink = playsink;
2824 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2826 chain->chain.bin = gst_bin_new ("visbin");
2827 bin = GST_BIN_CAST (chain->chain.bin);
2828 gst_object_ref_sink (bin);
2830 /* we're queuing raw audio here, we can remove this queue when we can disable
2831 * async behaviour in the video sink. */
2832 chain->queue = gst_element_factory_make ("queue", "visqueue");
2833 if (chain->queue == NULL)
2835 g_object_set (chain->queue, "silent", TRUE, NULL);
2836 gst_bin_add (bin, chain->queue);
2838 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2839 if (chain->conv == NULL)
2840 goto no_audioconvert;
2841 gst_bin_add (bin, chain->conv);
2843 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2844 if (chain->resample == NULL)
2845 goto no_audioresample;
2846 gst_bin_add (bin, chain->resample);
2848 /* this pad will be used for blocking the dataflow and switching the vis
2850 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2852 if (playsink->visualisation) {
2853 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2854 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2856 if (chain->vis == NULL) {
2857 GST_DEBUG_OBJECT (playsink, "trying goom");
2858 elem = gst_element_factory_make ("goom", "vis");
2859 chain->vis = try_element (playsink, elem, TRUE);
2861 if (chain->vis == NULL)
2864 gst_bin_add (bin, chain->vis);
2866 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2867 GST_PAD_LINK_CHECK_NOTHING);
2869 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2870 GST_PAD_LINK_CHECK_NOTHING);
2872 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2873 GST_PAD_LINK_CHECK_NOTHING);
2877 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2878 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2880 pad = gst_element_get_static_pad (chain->queue, "sink");
2881 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2882 gst_object_unref (pad);
2883 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2885 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2886 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2893 post_missing_element_message (playsink, "queue");
2894 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2895 (_("Missing element '%s' - check your GStreamer installation."),
2897 free_chain ((GstPlayChain *) chain);
2902 post_missing_element_message (playsink, "audioconvert");
2903 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2904 (_("Missing element '%s' - check your GStreamer installation."),
2905 "audioconvert"), ("make sure audioconvert isn't blacklisted"));
2906 free_chain ((GstPlayChain *) chain);
2911 post_missing_element_message (playsink, "audioresample");
2912 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2913 (_("Missing element '%s' - check your GStreamer installation."),
2914 "audioresample"), (NULL));
2915 free_chain ((GstPlayChain *) chain);
2920 post_missing_element_message (playsink, "goom");
2921 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2922 (_("Missing element '%s' - check your GStreamer installation."),
2924 free_chain ((GstPlayChain *) chain);
2929 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2930 (NULL), ("Failed to configure the visualisation element."));
2931 /* element made it to READY */
2932 gst_element_set_state (chain->vis, GST_STATE_NULL);
2933 free_chain ((GstPlayChain *) chain);
2938 /* this function is called when all the request pads are requested and when we
2939 * have to construct the final pipeline. Based on the flags we construct the
2940 * final output pipelines.
2943 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2946 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2948 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2950 /* assume we need nothing */
2951 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2953 GST_PLAY_SINK_LOCK (playsink);
2954 GST_OBJECT_LOCK (playsink);
2955 /* get flags, there are protected with the object lock */
2956 flags = playsink->flags;
2957 GST_OBJECT_UNLOCK (playsink);
2959 /* figure out which components we need */
2960 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2961 /* we have subtitles and we are requested to show it */
2965 if (((flags & GST_PLAY_FLAG_VIDEO)
2966 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2967 /* we have video and we are requested to show it */
2970 /* we only deinterlace if native video is not requested and
2971 * we have raw video */
2972 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2973 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2974 need_deinterlace = TRUE;
2977 if (playsink->audio_pad) {
2978 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2981 if (playsink->audio_pad_raw) {
2982 /* only can do vis with raw uncompressed audio */
2983 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2984 /* also add video when we add visualisation */
2991 /* we have a text_pad and we need text rendering, in this case we need a
2992 * video_pad to combine the video with the text or visualizations */
2993 if (need_text && !need_video && !playsink->text_sink) {
2994 if (playsink->video_pad) {
2996 } else if (need_audio) {
2997 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2998 (_("Can't play a text file without video or visualizations.")),
2999 ("Have text pad but no video pad or visualizations"));
3002 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
3003 (_("Can't play a text file without video or visualizations.")),
3004 ("Have text pad but no video pad or visualizations"));
3005 GST_PLAY_SINK_UNLOCK (playsink);
3010 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3011 need_video, need_vis, need_text);
3013 /* set up video pipeline */
3015 gboolean raw, async;
3017 /* we need a raw sink when we do vis or when we have a raw pad */
3018 raw = need_vis ? TRUE : playsink->video_pad_raw;
3019 /* we try to set the sink async=FALSE when we need vis, this way we can
3020 * avoid a queue in the audio chain. */
3023 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3024 playsink->video_pad_raw);
3026 if (playsink->videochain) {
3027 /* try to reactivate the chain */
3028 if (!setup_video_chain (playsink, raw, async)) {
3029 if (playsink->video_sinkpad_stream_synchronizer) {
3030 gst_element_release_request_pad (GST_ELEMENT_CAST
3031 (playsink->stream_synchronizer),
3032 playsink->video_sinkpad_stream_synchronizer);
3033 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3034 playsink->video_sinkpad_stream_synchronizer = NULL;
3035 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3036 playsink->video_srcpad_stream_synchronizer = NULL;
3039 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3041 /* Remove the sink from the bin to keep its state
3042 * and unparent it to allow reuse */
3043 if (playsink->videochain->sink)
3044 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3045 playsink->videochain->sink);
3047 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3048 free_chain ((GstPlayChain *) playsink->videochain);
3049 playsink->videochain = NULL;
3051 GST_OBJECT_LOCK (playsink);
3052 if (playsink->overlay_element)
3053 gst_object_unref (playsink->overlay_element);
3054 playsink->overlay_element = NULL;
3056 if (playsink->colorbalance_element) {
3057 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3058 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3059 gst_object_unref (playsink->colorbalance_element);
3061 playsink->colorbalance_element = NULL;
3062 GST_OBJECT_UNLOCK (playsink);
3066 if (!playsink->videochain)
3067 playsink->videochain = gen_video_chain (playsink, raw, async);
3068 if (!playsink->videochain)
3071 if (!playsink->video_sinkpad_stream_synchronizer) {
3072 GValue item = { 0, };
3075 playsink->video_sinkpad_stream_synchronizer =
3076 gst_element_get_request_pad (GST_ELEMENT_CAST
3077 (playsink->stream_synchronizer), "sink_%u");
3078 it = gst_pad_iterate_internal_links
3079 (playsink->video_sinkpad_stream_synchronizer);
3081 gst_iterator_next (it, &item);
3082 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3083 g_value_unset (&item);
3084 g_assert (playsink->video_srcpad_stream_synchronizer);
3085 gst_iterator_free (it);
3088 if (playsink->video_pad)
3089 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3090 playsink->video_sinkpad_stream_synchronizer);
3092 if (need_deinterlace) {
3093 if (!playsink->videodeinterlacechain)
3094 playsink->videodeinterlacechain =
3095 gen_video_deinterlace_chain (playsink);
3096 if (!playsink->videodeinterlacechain)
3099 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3101 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3103 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3104 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3106 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3107 playsink->videochain->sinkpad);
3108 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3109 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3111 if (playsink->videodeinterlacechain) {
3112 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3113 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3118 GST_DEBUG_OBJECT (playsink, "adding video chain");
3119 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3120 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3121 /* if we are not part of vis or subtitles, set the ghostpad target */
3122 if (!need_vis && !need_text && (!playsink->textchain
3123 || !playsink->text_pad)) {
3124 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3125 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3126 playsink->videochain->sinkpad);
3127 if (playsink->videodeinterlacechain
3128 && playsink->videodeinterlacechain->srcpad)
3129 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3130 playsink->videochain->sinkpad);
3131 if (need_deinterlace)
3132 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3133 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3135 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3136 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3139 GST_DEBUG_OBJECT (playsink, "no video needed");
3140 if (playsink->videochain) {
3141 GST_DEBUG_OBJECT (playsink, "removing video chain");
3142 if (playsink->vischain) {
3145 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3147 /* also had visualisation, release the tee srcpad before we then
3148 * unlink the video from it */
3149 if (playsink->audio_tee_vissrc) {
3150 gst_element_release_request_pad (playsink->audio_tee,
3151 playsink->audio_tee_vissrc);
3152 gst_object_unref (playsink->audio_tee_vissrc);
3153 playsink->audio_tee_vissrc = NULL;
3156 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3157 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3160 if (playsink->video_sinkpad_stream_synchronizer) {
3161 gst_element_release_request_pad (GST_ELEMENT_CAST
3162 (playsink->stream_synchronizer),
3163 playsink->video_sinkpad_stream_synchronizer);
3164 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3165 playsink->video_sinkpad_stream_synchronizer = NULL;
3166 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3167 playsink->video_srcpad_stream_synchronizer = NULL;
3170 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3171 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3172 if (playsink->videochain->ts_offset)
3173 gst_object_unref (playsink->videochain->ts_offset);
3174 playsink->videochain->ts_offset = NULL;
3177 if (playsink->videodeinterlacechain) {
3178 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3179 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3182 if (playsink->video_pad)
3183 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3185 GST_OBJECT_LOCK (playsink);
3186 if (playsink->overlay_element)
3187 gst_object_unref (playsink->overlay_element);
3188 playsink->overlay_element = NULL;
3190 if (playsink->colorbalance_element) {
3191 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3192 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3193 gst_object_unref (playsink->colorbalance_element);
3195 playsink->colorbalance_element = NULL;
3196 GST_OBJECT_UNLOCK (playsink);
3203 GST_DEBUG_OBJECT (playsink, "adding audio");
3205 /* get a raw sink if we are asked for a raw pad */
3206 raw = playsink->audio_pad_raw;
3208 if (playsink->audiochain) {
3209 /* try to reactivate the chain */
3210 if (!setup_audio_chain (playsink, raw)) {
3211 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3212 if (playsink->audio_tee_asrc) {
3213 gst_element_release_request_pad (playsink->audio_tee,
3214 playsink->audio_tee_asrc);
3215 gst_object_unref (playsink->audio_tee_asrc);
3216 playsink->audio_tee_asrc = NULL;
3219 if (playsink->audio_sinkpad_stream_synchronizer) {
3220 gst_element_release_request_pad (GST_ELEMENT_CAST
3221 (playsink->stream_synchronizer),
3222 playsink->audio_sinkpad_stream_synchronizer);
3223 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3224 playsink->audio_sinkpad_stream_synchronizer = NULL;
3225 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3226 playsink->audio_srcpad_stream_synchronizer = NULL;
3229 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3231 /* Remove the sink from the bin to keep its state
3232 * and unparent it to allow reuse */
3233 if (playsink->audiochain->sink)
3234 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3235 playsink->audiochain->sink);
3237 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3238 disconnect_chain (playsink->audiochain, playsink);
3239 playsink->audiochain->volume = NULL;
3240 playsink->audiochain->mute = NULL;
3241 if (playsink->audiochain->ts_offset)
3242 gst_object_unref (playsink->audiochain->ts_offset);
3243 playsink->audiochain->ts_offset = NULL;
3244 free_chain ((GstPlayChain *) playsink->audiochain);
3245 playsink->audiochain = NULL;
3246 playsink->volume_changed = playsink->mute_changed = FALSE;
3250 if (!playsink->audiochain) {
3251 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3252 playsink->audiochain = gen_audio_chain (playsink, raw);
3255 if (!playsink->audio_sinkpad_stream_synchronizer) {
3256 GValue item = { 0, };
3259 playsink->audio_sinkpad_stream_synchronizer =
3260 gst_element_get_request_pad (GST_ELEMENT_CAST
3261 (playsink->stream_synchronizer), "sink_%u");
3262 it = gst_pad_iterate_internal_links
3263 (playsink->audio_sinkpad_stream_synchronizer);
3265 gst_iterator_next (it, &item);
3266 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3267 g_value_unset (&item);
3268 g_assert (playsink->audio_srcpad_stream_synchronizer);
3269 gst_iterator_free (it);
3272 if (playsink->audiochain) {
3273 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3274 if (playsink->audio_tee_asrc == NULL) {
3275 playsink->audio_tee_asrc =
3276 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3278 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3279 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3280 gst_pad_link_full (playsink->audio_tee_asrc,
3281 playsink->audio_sinkpad_stream_synchronizer,
3282 GST_PAD_LINK_CHECK_NOTHING);
3283 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3284 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3287 GST_DEBUG_OBJECT (playsink, "no audio needed");
3288 /* we have no audio or we are requested to not play audio */
3289 if (playsink->audiochain) {
3290 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3291 /* release the audio pad */
3292 if (playsink->audio_tee_asrc) {
3293 gst_element_release_request_pad (playsink->audio_tee,
3294 playsink->audio_tee_asrc);
3295 gst_object_unref (playsink->audio_tee_asrc);
3296 playsink->audio_tee_asrc = NULL;
3299 if (playsink->audio_sinkpad_stream_synchronizer) {
3300 gst_element_release_request_pad (GST_ELEMENT_CAST
3301 (playsink->stream_synchronizer),
3302 playsink->audio_sinkpad_stream_synchronizer);
3303 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3304 playsink->audio_sinkpad_stream_synchronizer = NULL;
3305 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3306 playsink->audio_srcpad_stream_synchronizer = NULL;
3309 if (playsink->audiochain->sink_volume) {
3310 disconnect_chain (playsink->audiochain, playsink);
3311 playsink->audiochain->volume = NULL;
3312 playsink->audiochain->mute = NULL;
3313 if (playsink->audiochain->ts_offset)
3314 gst_object_unref (playsink->audiochain->ts_offset);
3315 playsink->audiochain->ts_offset = NULL;
3317 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3318 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3325 if (!playsink->vischain)
3326 playsink->vischain = gen_vis_chain (playsink);
3328 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3330 if (playsink->vischain) {
3331 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3333 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3334 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3335 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3336 if (playsink->audio_tee_vissrc == NULL) {
3337 playsink->audio_tee_vissrc =
3338 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3340 gst_pad_link_full (playsink->audio_tee_vissrc,
3341 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3342 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3343 GST_PAD_LINK_CHECK_NOTHING);
3344 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3345 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3346 gst_object_unref (srcpad);
3349 GST_DEBUG_OBJECT (playsink, "no vis needed");
3350 if (playsink->vischain) {
3351 if (playsink->audio_tee_vissrc) {
3352 gst_element_release_request_pad (playsink->audio_tee,
3353 playsink->audio_tee_vissrc);
3354 gst_object_unref (playsink->audio_tee_vissrc);
3355 playsink->audio_tee_vissrc = NULL;
3357 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3358 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3359 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3364 GST_DEBUG_OBJECT (playsink, "adding text");
3365 if (!playsink->textchain) {
3366 GST_DEBUG_OBJECT (playsink, "creating text chain");
3367 playsink->textchain = gen_text_chain (playsink);
3369 if (playsink->textchain) {
3372 GST_DEBUG_OBJECT (playsink, "adding text chain");
3373 if (playsink->textchain->overlay)
3374 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3376 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3378 if (!playsink->text_sinkpad_stream_synchronizer) {
3379 GValue item = { 0, };
3381 playsink->text_sinkpad_stream_synchronizer =
3382 gst_element_get_request_pad (GST_ELEMENT_CAST
3383 (playsink->stream_synchronizer), "sink_%u");
3384 it = gst_pad_iterate_internal_links
3385 (playsink->text_sinkpad_stream_synchronizer);
3387 gst_iterator_next (it, &item);
3388 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3389 g_value_unset (&item);
3390 g_assert (playsink->text_srcpad_stream_synchronizer);
3391 gst_iterator_free (it);
3393 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3394 playsink->text_sinkpad_stream_synchronizer);
3395 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3396 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3399 if (need_vis || need_video) {
3404 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3405 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3406 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3407 GST_PAD_LINK_CHECK_NOTHING);
3408 gst_object_unref (srcpad);
3410 if (need_deinterlace)
3411 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3412 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3414 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3415 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3417 gst_pad_link_full (playsink->textchain->srcpad,
3418 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3421 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3424 GST_DEBUG_OBJECT (playsink, "no text needed");
3425 /* we have no subtitles/text or we are requested to not show them */
3427 if (playsink->text_sinkpad_stream_synchronizer) {
3428 gst_element_release_request_pad (GST_ELEMENT_CAST
3429 (playsink->stream_synchronizer),
3430 playsink->text_sinkpad_stream_synchronizer);
3431 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3432 playsink->text_sinkpad_stream_synchronizer = NULL;
3433 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3434 playsink->text_srcpad_stream_synchronizer = NULL;
3437 if (playsink->textchain) {
3438 if (playsink->text_pad == NULL) {
3439 /* no text pad, remove the chain entirely */
3440 GST_DEBUG_OBJECT (playsink, "removing text chain");
3441 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3442 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3444 /* we have a chain and a textpad, turn the subtitles off */
3445 GST_DEBUG_OBJECT (playsink, "turning off the text");
3446 if (playsink->textchain->overlay)
3447 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3451 if (!need_video && playsink->video_pad) {
3452 if (playsink->video_sinkpad_stream_synchronizer) {
3453 gst_element_release_request_pad (GST_ELEMENT_CAST
3454 (playsink->stream_synchronizer),
3455 playsink->video_sinkpad_stream_synchronizer);
3456 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3457 playsink->video_sinkpad_stream_synchronizer = NULL;
3458 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3459 playsink->video_srcpad_stream_synchronizer = NULL;
3462 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3465 if (playsink->text_pad && !playsink->textchain)
3466 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3468 update_av_offset (playsink);
3469 do_async_done (playsink);
3470 GST_PLAY_SINK_UNLOCK (playsink);
3477 /* gen_ chain already posted error */
3478 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3479 GST_PLAY_SINK_UNLOCK (playsink);
3485 * gst_play_sink_set_flags:
3486 * @playsink: a #GstPlaySink
3487 * @flags: #GstPlayFlags
3489 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3490 * when constructing the sink pipelins.
3492 * Returns: TRUE if the flags could be configured.
3495 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3497 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3499 GST_OBJECT_LOCK (playsink);
3500 playsink->flags = flags;
3501 GST_OBJECT_UNLOCK (playsink);
3507 * gst_play_sink_get_flags:
3508 * @playsink: a #GstPlaySink
3510 * Get the flags of @playsink. That flags control the behaviour of the sink when
3511 * it constructs the sink pipelines.
3513 * Returns: the currently configured #GstPlayFlags.
3516 gst_play_sink_get_flags (GstPlaySink * playsink)
3520 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3522 GST_OBJECT_LOCK (playsink);
3523 res = playsink->flags;
3524 GST_OBJECT_UNLOCK (playsink);
3530 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3532 GstPlayTextChain *chain;
3534 GST_PLAY_SINK_LOCK (playsink);
3535 chain = (GstPlayTextChain *) playsink->textchain;
3536 g_free (playsink->font_desc);
3537 playsink->font_desc = g_strdup (desc);
3538 if (chain && chain->overlay) {
3539 g_object_set (chain->overlay, "font-desc", desc, NULL);
3541 GST_PLAY_SINK_UNLOCK (playsink);
3545 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3547 gchar *result = NULL;
3548 GstPlayTextChain *chain;
3550 GST_PLAY_SINK_LOCK (playsink);
3551 chain = (GstPlayTextChain *) playsink->textchain;
3552 if (chain && chain->overlay) {
3553 g_object_get (chain->overlay, "font-desc", &result, NULL);
3554 playsink->font_desc = g_strdup (result);
3556 result = g_strdup (playsink->font_desc);
3558 GST_PLAY_SINK_UNLOCK (playsink);
3564 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3565 const gchar * encoding)
3567 GstPlayTextChain *chain;
3569 GST_PLAY_SINK_LOCK (playsink);
3570 chain = (GstPlayTextChain *) playsink->textchain;
3571 g_free (playsink->subtitle_encoding);
3572 playsink->subtitle_encoding = g_strdup (encoding);
3573 if (chain && chain->overlay) {
3574 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3576 GST_PLAY_SINK_UNLOCK (playsink);
3580 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3582 gchar *result = NULL;
3583 GstPlayTextChain *chain;
3585 GST_PLAY_SINK_LOCK (playsink);
3586 chain = (GstPlayTextChain *) playsink->textchain;
3587 if (chain && chain->overlay) {
3588 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3589 playsink->subtitle_encoding = g_strdup (result);
3591 result = g_strdup (playsink->subtitle_encoding);
3593 GST_PLAY_SINK_UNLOCK (playsink);
3599 update_av_offset (GstPlaySink * playsink)
3602 GstPlayAudioChain *achain;
3603 GstPlayVideoChain *vchain;
3605 av_offset = playsink->av_offset;
3606 achain = (GstPlayAudioChain *) playsink->audiochain;
3607 vchain = (GstPlayVideoChain *) playsink->videochain;
3609 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3610 g_object_set (achain->ts_offset,
3611 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3612 g_object_set (vchain->ts_offset,
3613 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3615 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3620 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3622 GST_PLAY_SINK_LOCK (playsink);
3623 playsink->av_offset = av_offset;
3624 update_av_offset (playsink);
3625 GST_PLAY_SINK_UNLOCK (playsink);
3629 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3633 GST_PLAY_SINK_LOCK (playsink);
3634 result = playsink->av_offset;
3635 GST_PLAY_SINK_UNLOCK (playsink);
3641 * gst_play_sink_get_last_sample:
3642 * @playsink: a #GstPlaySink
3644 * Get the last displayed sample from @playsink. This sample is in the native
3645 * format of the sink element, the caps in the result sample contain the format
3646 * of the frame data.
3648 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3652 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3654 GstSample *result = NULL;
3655 GstPlayVideoChain *chain;
3657 GST_PLAY_SINK_LOCK (playsink);
3658 GST_DEBUG_OBJECT (playsink, "taking last sample");
3659 /* get the video chain if we can */
3660 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3661 GST_DEBUG_OBJECT (playsink, "found video chain");
3662 /* see if the chain is active */
3663 if (chain->chain.activated && chain->sink) {
3666 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3668 /* find and get the last-buffer property now */
3670 gst_play_sink_find_property (playsink, chain->sink,
3671 "last-sample", GST_TYPE_SAMPLE))) {
3672 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3673 g_object_get (elem, "last-sample", &result, NULL);
3674 gst_object_unref (elem);
3678 GST_PLAY_SINK_UNLOCK (playsink);
3684 * gst_play_sink_convert_sample:
3685 * @playsink: a #GstPlaySink
3688 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3689 * be in the native format of the sink element and the caps on the buffer
3690 * describe the format of the frame. If @caps is not %NULL, the video
3691 * frame will be converted to the format of the caps.
3693 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3694 * available or when the conversion failed.
3697 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3702 result = gst_play_sink_get_last_sample (playsink);
3703 if (result != NULL && caps != NULL) {
3706 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3707 if (temp == NULL && err)
3710 gst_sample_unref (result);
3718 /* I'm really uncertain whether we should make playsink post an error
3719 * on the bus or not. It's not like it's a critical issue regarding
3720 * playsink behaviour. */
3721 GST_ERROR ("Error converting frame: %s", err->message);
3722 gst_sample_unref (result);
3729 is_raw_structure (GstStructure * s)
3733 name = gst_structure_get_name (s);
3735 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3741 is_raw_pad (GstPad * pad)
3743 GstPad *peer = gst_pad_get_peer (pad);
3745 gboolean raw = TRUE;
3750 caps = gst_pad_get_current_caps (peer);
3754 caps = gst_pad_query_caps (peer, NULL);
3756 n = gst_caps_get_size (caps);
3757 for (i = 0; i < n; i++) {
3758 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3762 } else if (raw != r) {
3763 GST_ERROR_OBJECT (pad,
3764 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3770 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3772 gst_caps_unref (caps);
3773 gst_object_unref (peer);
3778 static GstPadProbeReturn
3779 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3780 gpointer user_data);
3783 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3785 if (playsink->video_pad) {
3787 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3788 (playsink->video_pad)));
3789 if (blocked && playsink->video_block_id == 0) {
3790 playsink->video_block_id =
3791 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3792 sinkpad_blocked_cb, playsink, NULL);
3793 } else if (!blocked && playsink->video_block_id) {
3794 gst_pad_remove_probe (opad, playsink->video_block_id);
3795 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3796 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3797 playsink->video_block_id = 0;
3798 playsink->video_pad_blocked = FALSE;
3800 gst_object_unref (opad);
3805 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3807 if (playsink->audio_pad) {
3809 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3810 (playsink->audio_pad)));
3811 if (blocked && playsink->audio_block_id == 0) {
3812 playsink->audio_block_id =
3813 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3814 sinkpad_blocked_cb, playsink, NULL);
3815 } else if (!blocked && playsink->audio_block_id) {
3816 gst_pad_remove_probe (opad, playsink->audio_block_id);
3817 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3818 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3819 playsink->audio_block_id = 0;
3820 playsink->audio_pad_blocked = FALSE;
3822 gst_object_unref (opad);
3827 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3829 if (playsink->text_pad) {
3831 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3832 (playsink->text_pad)));
3833 if (blocked && playsink->text_block_id == 0) {
3834 playsink->text_block_id =
3835 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3836 sinkpad_blocked_cb, playsink, NULL);
3837 } else if (!blocked && playsink->text_block_id) {
3838 gst_pad_remove_probe (opad, playsink->text_block_id);
3839 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3840 playsink->text_block_id = 0;
3841 playsink->text_pad_blocked = FALSE;
3843 gst_object_unref (opad);
3848 gst_play_sink_reconfigure (GstPlaySink * playsink)
3850 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3852 GST_PLAY_SINK_LOCK (playsink);
3853 video_set_blocked (playsink, TRUE);
3854 audio_set_blocked (playsink, TRUE);
3855 text_set_blocked (playsink, TRUE);
3856 GST_PLAY_SINK_UNLOCK (playsink);
3861 static GstPadProbeReturn
3862 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3865 GstPlaySink *playsink = (GstPlaySink *) user_data;
3868 GST_PLAY_SINK_LOCK (playsink);
3870 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3871 if (pad == playsink->video_pad) {
3872 playsink->video_pad_blocked = TRUE;
3873 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3874 } else if (pad == playsink->audio_pad) {
3875 playsink->audio_pad_blocked = TRUE;
3876 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3877 } else if (pad == playsink->text_pad) {
3878 playsink->text_pad_blocked = TRUE;
3879 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3882 /* We reconfigure when for ALL streams:
3883 * * there isn't a pad
3884 * * OR the pad is blocked
3885 * * OR there are no pending blocks on that pad
3888 if ((!playsink->video_pad || playsink->video_pad_blocked
3889 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3890 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3891 && (!playsink->text_pad || playsink->text_pad_blocked
3892 || !PENDING_TEXT_BLOCK (playsink))) {
3893 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3895 if (playsink->video_pad) {
3896 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3897 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3898 playsink->video_pad_raw);
3901 if (playsink->audio_pad) {
3902 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3903 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3904 playsink->audio_pad_raw);
3907 gst_play_sink_do_reconfigure (playsink);
3909 video_set_blocked (playsink, FALSE);
3910 audio_set_blocked (playsink, FALSE);
3911 text_set_blocked (playsink, FALSE);
3914 gst_object_unref (pad);
3916 GST_PLAY_SINK_UNLOCK (playsink);
3918 return GST_PAD_PROBE_OK;
3922 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3924 gboolean reconfigure = FALSE;
3928 g_object_get (pad, "caps", &caps, NULL);
3932 if (pad == playsink->audio_pad) {
3933 raw = is_raw_pad (pad);
3934 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3935 && playsink->audiochain;
3936 GST_DEBUG_OBJECT (pad,
3937 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3939 } else if (pad == playsink->video_pad) {
3940 raw = is_raw_pad (pad);
3941 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3942 && playsink->videochain;
3943 GST_DEBUG_OBJECT (pad,
3944 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3948 gst_caps_unref (caps);
3951 gst_play_sink_reconfigure (playsink);
3955 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3956 GstPlaySinkType type)
3958 gulong *block_id = NULL;
3960 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3962 GST_PLAY_SINK_LOCK (playsink);
3963 if (pad == playsink->video_pad) {
3964 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3965 type != GST_PLAY_SINK_TYPE_VIDEO)
3967 block_id = &playsink->video_block_id;
3968 } else if (pad == playsink->audio_pad) {
3969 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3970 type != GST_PLAY_SINK_TYPE_AUDIO)
3972 block_id = &playsink->audio_block_id;
3973 } else if (pad == playsink->text_pad) {
3974 if (type != GST_PLAY_SINK_TYPE_TEXT)
3976 block_id = &playsink->text_block_id;
3979 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3981 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3984 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3985 sinkpad_blocked_cb, playsink, NULL);
3986 PENDING_FLAG_SET (playsink, type);
3987 gst_object_unref (blockpad);
3989 GST_PLAY_SINK_UNLOCK (playsink);
3996 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3998 GST_PLAY_SINK_UNLOCK (playsink);
4004 * gst_play_sink_request_pad
4005 * @playsink: a #GstPlaySink
4006 * @type: a #GstPlaySinkType
4008 * Create or return a pad of @type.
4010 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4013 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4016 gboolean created = FALSE;
4017 gboolean activate = TRUE;
4018 const gchar *pad_name = NULL;
4019 gulong *block_id = NULL;
4021 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4023 GST_PLAY_SINK_LOCK (playsink);
4025 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4026 case GST_PLAY_SINK_TYPE_AUDIO:
4027 pad_name = "audio_sink";
4028 if (!playsink->audio_tee) {
4029 GST_LOG_OBJECT (playsink, "creating tee");
4030 /* create tee when needed. This element will feed the audio sink chain
4031 * and the vis chain. */
4032 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4033 if (playsink->audio_tee == NULL) {
4034 post_missing_element_message (playsink, "tee");
4035 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4036 (_("Missing element '%s' - check your GStreamer installation."),
4041 playsink->audio_tee_sink =
4042 gst_element_get_static_pad (playsink->audio_tee, "sink");
4043 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4044 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4047 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4049 if (!playsink->audio_pad) {
4050 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4051 playsink->audio_pad =
4052 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4053 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4054 G_CALLBACK (caps_notify_cb), playsink);
4057 playsink->audio_pad_raw = FALSE;
4058 res = playsink->audio_pad;
4059 block_id = &playsink->audio_block_id;
4061 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4062 case GST_PLAY_SINK_TYPE_VIDEO:
4063 pad_name = "video_sink";
4064 if (!playsink->video_pad) {
4065 GST_LOG_OBJECT (playsink, "ghosting videosink");
4066 playsink->video_pad =
4067 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4068 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4069 G_CALLBACK (caps_notify_cb), playsink);
4072 playsink->video_pad_raw = FALSE;
4073 res = playsink->video_pad;
4074 block_id = &playsink->video_block_id;
4076 case GST_PLAY_SINK_TYPE_TEXT:
4077 GST_LOG_OBJECT (playsink, "ghosting text");
4078 if (!playsink->text_pad) {
4079 playsink->text_pad =
4080 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4083 res = playsink->text_pad;
4084 block_id = &playsink->text_block_id;
4086 case GST_PLAY_SINK_TYPE_FLUSHING:
4090 /* we need a unique padname for the flushing pad. */
4091 padname = g_strdup_printf ("flushing_%u", playsink->count);
4092 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4103 GST_PLAY_SINK_UNLOCK (playsink);
4105 if (created && res) {
4106 /* we have to add the pad when it's active or we get an error when the
4107 * element is 'running' */
4108 gst_pad_set_active (res, TRUE);
4109 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4110 if (block_id && *block_id == 0) {
4112 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4115 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4116 sinkpad_blocked_cb, playsink, NULL);
4117 PENDING_FLAG_SET (playsink, type);
4118 gst_object_unref (blockpad);
4121 gst_pad_set_active (res, activate);
4129 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4130 const gchar * name, const GstCaps * caps)
4134 GstPlaySinkType type;
4135 const gchar *tplname;
4137 g_return_val_if_fail (templ != NULL, NULL);
4139 GST_DEBUG_OBJECT (element, "name:%s", name);
4141 psink = GST_PLAY_SINK (element);
4142 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4144 /* Figure out the GstPlaySinkType based on the template */
4145 if (!strcmp (tplname, "audio_sink"))
4146 type = GST_PLAY_SINK_TYPE_AUDIO;
4147 else if (!strcmp (tplname, "audio_raw_sink"))
4148 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4149 else if (!strcmp (tplname, "video_sink"))
4150 type = GST_PLAY_SINK_TYPE_VIDEO;
4151 else if (!strcmp (tplname, "video_raw_sink"))
4152 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4153 else if (!strcmp (tplname, "text_sink"))
4154 type = GST_PLAY_SINK_TYPE_TEXT;
4156 goto unknown_template;
4158 pad = gst_play_sink_request_pad (psink, type);
4162 GST_WARNING_OBJECT (element, "Unknown pad template");
4167 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4169 GstPad **res = NULL;
4170 gboolean untarget = TRUE;
4172 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4174 GST_PLAY_SINK_LOCK (playsink);
4175 if (pad == playsink->video_pad) {
4176 res = &playsink->video_pad;
4177 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4179 video_set_blocked (playsink, FALSE);
4180 } else if (pad == playsink->audio_pad) {
4181 res = &playsink->audio_pad;
4182 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4184 audio_set_blocked (playsink, FALSE);
4185 } else if (pad == playsink->text_pad) {
4186 res = &playsink->text_pad;
4187 text_set_blocked (playsink, FALSE);
4189 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4193 GST_PLAY_SINK_UNLOCK (playsink);
4196 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4197 gst_pad_set_active (*res, FALSE);
4199 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4200 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4202 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4203 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4209 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4211 GstPlaySink *psink = GST_PLAY_SINK (element);
4213 gst_play_sink_release_pad (psink, pad);
4217 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4219 GstPlaySink *playsink;
4221 playsink = GST_PLAY_SINK_CAST (bin);
4223 switch (GST_MESSAGE_TYPE (message)) {
4224 case GST_MESSAGE_STEP_DONE:
4229 gboolean flush, intermediate, eos;
4232 GST_INFO_OBJECT (playsink, "Handling step-done message");
4233 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4234 &intermediate, &duration, &eos);
4236 if (format == GST_FORMAT_BUFFERS) {
4237 /* for the buffer format, we align the other streams */
4238 if (playsink->audiochain) {
4242 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4245 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4246 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4250 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4253 case GST_MESSAGE_ELEMENT:{
4254 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4255 GstVideoOverlay *overlay;
4257 GST_OBJECT_LOCK (playsink);
4258 if (playsink->overlay_element
4259 && GST_OBJECT_CAST (playsink->overlay_element) !=
4260 GST_MESSAGE_SRC (message)) {
4261 gst_object_unref (playsink->overlay_element);
4262 playsink->overlay_element = NULL;
4265 if (!playsink->overlay_element)
4266 playsink->overlay_element =
4267 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4269 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4270 GST_OBJECT_UNLOCK (playsink);
4272 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4274 if (playsink->overlay_handle_set)
4275 gst_video_overlay_set_window_handle (playsink->overlay_element,
4276 playsink->overlay_handle);
4277 if (playsink->overlay_handle_events_set)
4278 gst_video_overlay_handle_events (playsink->overlay_element,
4279 playsink->overlay_handle_events);
4280 if (playsink->overlay_render_rectangle_set)
4281 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4282 playsink->overlay_x, playsink->overlay_y,
4283 playsink->overlay_width, playsink->overlay_height);
4285 gst_object_unref (overlay);
4286 gst_message_unref (message);
4287 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4289 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4295 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4300 /* Send an event to our sinks until one of them works; don't then send to the
4301 * remaining sinks (unlike GstBin)
4302 * Special case: If a text sink is set we need to send the event
4303 * to them in case it's source is different from the a/v stream's source.
4306 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4308 gboolean res = TRUE;
4309 if (playsink->send_event_mode == MODE_FIRST) {
4310 if (playsink->textchain && playsink->textchain->sink) {
4311 gst_event_ref (event);
4313 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4314 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4316 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4320 if (playsink->videochain) {
4321 gst_event_ref (event);
4323 gst_element_send_event (playsink->videochain->chain.bin,
4325 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4328 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4330 if (playsink->audiochain) {
4331 gst_event_ref (event);
4333 gst_element_send_event (playsink->audiochain->chain.bin,
4335 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4338 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4342 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4343 (GST_ELEMENT_CAST (playsink), event);
4347 gst_event_unref (event);
4351 /* We only want to send the event to a single sink (overriding GstBin's
4352 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4353 * events appropriately. So, this is a messy duplication of code. */
4355 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4357 gboolean res = FALSE;
4358 GstEventType event_type = GST_EVENT_TYPE (event);
4359 GstPlaySink *playsink;
4360 playsink = GST_PLAY_SINK_CAST (element);
4361 switch (event_type) {
4362 case GST_EVENT_SEEK:
4363 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4364 res = gst_play_sink_send_event_to_sink (playsink, event);
4366 case GST_EVENT_STEP:
4371 gboolean flush, intermediate;
4372 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4374 if (format == GST_FORMAT_BUFFERS) {
4375 /* for buffers, we will try to step video frames, for other formats we
4376 * send the step to all sinks */
4377 res = gst_play_sink_send_event_to_sink (playsink, event);
4380 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4387 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4394 static GstStateChangeReturn
4395 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4397 GstStateChangeReturn ret;
4398 GstStateChangeReturn bret;
4399 GstPlaySink *playsink;
4400 playsink = GST_PLAY_SINK (element);
4401 switch (transition) {
4402 case GST_STATE_CHANGE_READY_TO_PAUSED:
4403 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4405 playsink->need_async_start = TRUE;
4406 /* we want to go async to PAUSED until we managed to configure and add the
4408 do_async_start (playsink);
4409 ret = GST_STATE_CHANGE_ASYNC;
4411 /* block all pads here */
4412 if (!gst_play_sink_reconfigure (playsink))
4413 ret = GST_STATE_CHANGE_FAILURE;
4415 case GST_STATE_CHANGE_PAUSED_TO_READY:
4416 /* unblock all pads here */
4417 GST_PLAY_SINK_LOCK (playsink);
4418 video_set_blocked (playsink, FALSE);
4419 audio_set_blocked (playsink, FALSE);
4420 text_set_blocked (playsink, FALSE);
4421 GST_PLAY_SINK_UNLOCK (playsink);
4423 case GST_STATE_CHANGE_READY_TO_NULL:
4424 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4425 /* remove our links to the mute and volume elements when they were
4426 * provided by a sink */
4427 disconnect_chain (playsink->audiochain, playsink);
4428 playsink->audiochain->volume = NULL;
4429 playsink->audiochain->mute = NULL;
4432 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4433 gst_object_unref (playsink->audiochain->ts_offset);
4434 playsink->audiochain->ts_offset = NULL;
4437 if (playsink->videochain && playsink->videochain->ts_offset) {
4438 gst_object_unref (playsink->videochain->ts_offset);
4439 playsink->videochain->ts_offset = NULL;
4442 GST_OBJECT_LOCK (playsink);
4443 if (playsink->overlay_element)
4444 gst_object_unref (playsink->overlay_element);
4445 playsink->overlay_element = NULL;
4447 if (playsink->colorbalance_element) {
4448 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4449 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4450 gst_object_unref (playsink->colorbalance_element);
4452 playsink->colorbalance_element = NULL;
4453 GST_OBJECT_UNLOCK (playsink);
4455 ret = GST_STATE_CHANGE_SUCCESS;
4458 /* all other state changes return SUCCESS by default, this value can be
4459 * overridden by the result of the children */
4460 ret = GST_STATE_CHANGE_SUCCESS;
4464 /* do the state change of the children */
4466 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4468 /* now look at the result of our children and adjust the return value */
4470 case GST_STATE_CHANGE_FAILURE:
4471 /* failure, we stop */
4472 goto activate_failed;
4473 case GST_STATE_CHANGE_NO_PREROLL:
4474 /* some child returned NO_PREROLL. This is strange but we never know. We
4475 * commit our async state change (if any) and return the NO_PREROLL */
4476 do_async_done (playsink);
4479 case GST_STATE_CHANGE_ASYNC:
4480 /* some child was async, return this */
4484 /* return our previously configured return value */
4488 switch (transition) {
4489 case GST_STATE_CHANGE_READY_TO_PAUSED:
4491 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4492 /* FIXME Release audio device when we implement that */
4493 playsink->need_async_start = TRUE;
4495 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4496 if (playsink->video_sinkpad_stream_synchronizer) {
4497 gst_element_release_request_pad (GST_ELEMENT_CAST
4498 (playsink->stream_synchronizer),
4499 playsink->video_sinkpad_stream_synchronizer);
4500 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4501 playsink->video_sinkpad_stream_synchronizer = NULL;
4502 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4503 playsink->video_srcpad_stream_synchronizer = NULL;
4505 if (playsink->audio_sinkpad_stream_synchronizer) {
4506 gst_element_release_request_pad (GST_ELEMENT_CAST
4507 (playsink->stream_synchronizer),
4508 playsink->audio_sinkpad_stream_synchronizer);
4509 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4510 playsink->audio_sinkpad_stream_synchronizer = NULL;
4511 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4512 playsink->audio_srcpad_stream_synchronizer = NULL;
4514 if (playsink->text_sinkpad_stream_synchronizer) {
4515 gst_element_release_request_pad (GST_ELEMENT_CAST
4516 (playsink->stream_synchronizer),
4517 playsink->text_sinkpad_stream_synchronizer);
4518 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4519 playsink->text_sinkpad_stream_synchronizer = NULL;
4520 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4521 playsink->text_srcpad_stream_synchronizer = NULL;
4525 case GST_STATE_CHANGE_READY_TO_NULL:
4526 /* remove sinks we added */
4527 if (playsink->videodeinterlacechain) {
4528 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4530 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4532 if (playsink->videochain) {
4533 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4534 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4536 if (playsink->audiochain) {
4537 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4538 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4540 if (playsink->vischain) {
4541 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4542 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4544 if (playsink->textchain) {
4545 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4546 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4548 do_async_done (playsink);
4549 /* when going to READY, keep elements around as long as possible,
4550 * so they may be re-used faster next time/url around.
4551 * when really going to NULL, clean up everything completely. */
4552 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4554 /* Unparent the sinks to allow reuse */
4555 if (playsink->videochain && playsink->videochain->sink)
4556 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4557 playsink->videochain->sink);
4558 if (playsink->audiochain && playsink->audiochain->sink)
4559 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4560 playsink->audiochain->sink);
4561 if (playsink->textchain && playsink->textchain->sink)
4562 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4563 playsink->textchain->sink);
4564 if (playsink->audio_sink != NULL)
4565 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4566 if (playsink->video_sink != NULL)
4567 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4568 if (playsink->visualisation != NULL)
4569 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4570 if (playsink->text_sink != NULL)
4571 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4572 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4573 playsink->videodeinterlacechain = NULL;
4574 free_chain ((GstPlayChain *) playsink->videochain);
4575 playsink->videochain = NULL;
4576 free_chain ((GstPlayChain *) playsink->audiochain);
4577 playsink->audiochain = NULL;
4578 free_chain ((GstPlayChain *) playsink->vischain);
4579 playsink->vischain = NULL;
4580 free_chain ((GstPlayChain *) playsink->textchain);
4581 playsink->textchain = NULL;
4591 GST_DEBUG_OBJECT (element,
4592 "element failed to change states -- activation problem?");
4593 return GST_STATE_CHANGE_FAILURE;
4598 gst_play_sink_set_property (GObject * object, guint prop_id,
4599 const GValue * value, GParamSpec * spec)
4601 GstPlaySink *playsink = GST_PLAY_SINK (object);
4604 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4607 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4610 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4612 case PROP_FONT_DESC:
4613 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4615 case PROP_SUBTITLE_ENCODING:
4616 gst_play_sink_set_subtitle_encoding (playsink,
4617 g_value_get_string (value));
4619 case PROP_VIS_PLUGIN:
4620 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4622 case PROP_AV_OFFSET:
4623 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4625 case PROP_VIDEO_SINK:
4626 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4627 g_value_get_object (value));
4629 case PROP_AUDIO_SINK:
4630 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4631 g_value_get_object (value));
4633 case PROP_TEXT_SINK:
4634 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4635 g_value_get_object (value));
4637 case PROP_SEND_EVENT_MODE:
4638 playsink->send_event_mode = g_value_get_enum (value);
4640 case PROP_FORCE_ASPECT_RATIO:{
4641 GstPlayVideoChain *chain;
4644 playsink->force_aspect_ratio = g_value_get_boolean (value);
4646 GST_PLAY_SINK_LOCK (playsink);
4647 if (playsink->videochain) {
4648 chain = (GstPlayVideoChain *) playsink->videochain;
4652 gst_play_sink_find_property_sinks (playsink, chain->sink,
4653 "force-aspect-ratio", G_TYPE_BOOLEAN);
4656 g_object_set (elem, "force-aspect-ratio",
4657 playsink->force_aspect_ratio, NULL);
4660 GST_PLAY_SINK_UNLOCK (playsink);
4664 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4670 gst_play_sink_get_property (GObject * object, guint prop_id,
4671 GValue * value, GParamSpec * spec)
4673 GstPlaySink *playsink = GST_PLAY_SINK (object);
4676 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4679 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4682 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4684 case PROP_FONT_DESC:
4685 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4687 case PROP_SUBTITLE_ENCODING:
4688 g_value_take_string (value,
4689 gst_play_sink_get_subtitle_encoding (playsink));
4691 case PROP_VIS_PLUGIN:
4692 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4695 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4697 case PROP_AV_OFFSET:
4698 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4700 case PROP_VIDEO_SINK:
4701 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4702 GST_PLAY_SINK_TYPE_VIDEO));
4704 case PROP_AUDIO_SINK:
4705 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4706 GST_PLAY_SINK_TYPE_AUDIO));
4708 case PROP_TEXT_SINK:
4709 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4710 GST_PLAY_SINK_TYPE_TEXT));
4712 case PROP_SEND_EVENT_MODE:
4713 g_value_set_enum (value, playsink->send_event_mode);
4715 case PROP_FORCE_ASPECT_RATIO:
4716 g_value_set_boolean (value, playsink->force_aspect_ratio);
4719 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4725 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4727 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4728 GstVideoOverlay *overlay_element;
4730 GST_OBJECT_LOCK (playsink);
4731 if (playsink->overlay_element)
4733 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4735 overlay_element = NULL;
4736 GST_OBJECT_UNLOCK (playsink);
4738 if (overlay_element) {
4739 gst_video_overlay_expose (overlay_element);
4740 gst_object_unref (overlay_element);
4745 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4746 gboolean handle_events)
4748 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4749 GstVideoOverlay *overlay_element;
4751 GST_OBJECT_LOCK (playsink);
4752 if (playsink->overlay_element)
4754 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4756 overlay_element = NULL;
4757 GST_OBJECT_UNLOCK (playsink);
4759 playsink->overlay_handle_events_set = TRUE;
4760 playsink->overlay_handle_events = handle_events;
4762 if (overlay_element) {
4763 gst_video_overlay_handle_events (overlay_element, handle_events);
4764 gst_object_unref (overlay_element);
4769 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4770 gint y, gint width, gint height)
4772 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4773 GstVideoOverlay *overlay_element;
4775 GST_OBJECT_LOCK (playsink);
4776 if (playsink->overlay_element)
4778 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4780 overlay_element = NULL;
4781 GST_OBJECT_UNLOCK (playsink);
4783 playsink->overlay_render_rectangle_set = TRUE;
4784 playsink->overlay_x = x;
4785 playsink->overlay_y = y;
4786 playsink->overlay_width = width;
4787 playsink->overlay_height = height;
4789 if (overlay_element) {
4790 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4792 gst_object_unref (overlay_element);
4797 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4800 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4801 GstVideoOverlay *overlay_element;
4803 GST_OBJECT_LOCK (playsink);
4804 if (playsink->overlay_element)
4806 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4808 overlay_element = NULL;
4809 GST_OBJECT_UNLOCK (playsink);
4811 playsink->overlay_handle_set = TRUE;
4812 playsink->overlay_handle = handle;
4814 if (overlay_element) {
4815 gst_video_overlay_set_window_handle (overlay_element, handle);
4816 gst_object_unref (overlay_element);
4821 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4823 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4824 iface->expose = gst_play_sink_overlay_expose;
4825 iface->handle_events = gst_play_sink_overlay_handle_events;
4826 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4827 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4831 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4832 GstStructure * structure)
4834 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4837 GST_PLAY_SINK_LOCK (playsink);
4838 if (playsink->videochain && playsink->videochain->chain.bin)
4839 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4840 GST_PLAY_SINK_UNLOCK (playsink);
4843 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4846 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4848 gst_object_unref (nav);
4850 GstEvent *event = gst_event_new_navigation (structure);
4852 gst_element_send_event (GST_ELEMENT (bin), event);
4855 gst_object_unref (bin);
4859 gst_structure_free (structure);
4863 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4865 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4867 iface->send_event = gst_play_sink_navigation_send_event;
4870 static const GList *
4871 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4873 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4875 return playsink->colorbalance_channels;
4879 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4880 GstColorBalanceChannel * proxy, gint value)
4882 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4885 GstColorBalance *balance_element = NULL;
4887 GST_OBJECT_LOCK (playsink);
4888 if (playsink->colorbalance_element)
4890 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4891 GST_OBJECT_UNLOCK (playsink);
4893 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4894 GstColorBalanceChannel *proxy_tmp = l->data;
4897 if (proxy_tmp != proxy)
4900 playsink->colorbalance_values[i] = value;
4902 if (balance_element) {
4903 GstColorBalanceChannel *channel = NULL;
4904 const GList *channels, *k;
4906 channels = gst_color_balance_list_channels (balance_element);
4907 for (k = channels; k; k = k->next) {
4908 GstColorBalanceChannel *tmp = l->data;
4910 if (g_strrstr (tmp->label, proxy->label)) {
4918 /* Convert to [0, 1] range */
4921 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4922 (gdouble) proxy->min_value);
4923 /* Convert to channel range */
4925 channel->min_value + new_val * ((gdouble) channel->max_value -
4926 (gdouble) channel->min_value);
4928 gst_color_balance_set_value (balance_element, channel,
4929 (gint) (new_val + 0.5));
4931 gst_object_unref (balance_element);
4934 gst_color_balance_value_changed (balance, proxy, value);
4940 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4941 GstColorBalanceChannel * proxy)
4943 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4947 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4948 GstColorBalanceChannel *proxy_tmp = l->data;
4950 if (proxy_tmp != proxy)
4953 return playsink->colorbalance_values[i];
4956 g_return_val_if_reached (0);
4959 static GstColorBalanceType
4960 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4962 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4963 GstColorBalance *balance_element = NULL;
4964 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4966 GST_OBJECT_LOCK (playsink);
4967 if (playsink->colorbalance_element)
4969 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4970 GST_OBJECT_UNLOCK (playsink);
4972 if (balance_element) {
4973 t = gst_color_balance_get_balance_type (balance_element);
4974 gst_object_unref (balance_element);
4981 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4983 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4985 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4986 iface->set_value = gst_play_sink_colorbalance_set_value;
4987 iface->get_value = gst_play_sink_colorbalance_get_value;
4988 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4992 gst_play_sink_plugin_init (GstPlugin * plugin)
4994 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4995 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4996 GST_TYPE_PLAY_SINK);