2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
31 #include <gst/audio/streamvolume.h>
32 #include <gst/video/colorbalance.h>
33 #include <gst/video/videooverlay.h>
34 #include <gst/video/navigation.h>
36 #include "gstplaysink.h"
37 #include "gststreamsynchronizer.h"
38 #include "gstplaysinkvideoconvert.h"
39 #include "gstplaysinkaudioconvert.h"
41 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
42 #define GST_CAT_DEFAULT gst_play_sink_debug
44 #define VOLUME_MAX_DOUBLE 10.0
46 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
47 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
49 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
53 * GstPlaySinkSendEventMode:
54 * @MODE_DEFAULT: default GstBin's send_event handling
55 * @MODE_FIRST: send event only to the first sink that return true
57 * Send event handling to use
63 } GstPlaySinkSendEventMode;
66 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
68 gst_play_sink_send_event_mode_get_type (void)
70 static GType gtype = 0;
73 static const GEnumValue values[] = {
74 {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
76 {MODE_FIRST, "Sends the event to sinks until the first one handles it",
81 gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
86 /* holds the common data fields for the audio and video pipelines. We keep them
87 * in a structure to more easily have all the info available. */
90 GstPlaySink *playsink;
103 GstElement *volume; /* element with the volume property */
104 gboolean sink_volume; /* if the volume was provided by the sink */
105 GstElement *mute; /* element with the mute property */
107 GstElement *ts_offset;
113 GstPad *sinkpad, *srcpad;
115 GstElement *deinterlace;
116 } GstPlayVideoDeinterlaceChain;
126 GstElement *ts_offset;
135 GstElement *resample;
136 GstPad *blockpad; /* srcpad of resample, used for switching the vis */
137 GstPad *vissinkpad; /* visualisation sinkpad, */
139 GstPad *vissrcpad; /* visualisation srcpad, */
140 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
149 GstElement *identity;
151 GstPad *videosinkpad;
153 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
155 GstElement *sink; /* custom sink to receive subtitle buffers */
158 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
159 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
160 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
161 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
162 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
164 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
165 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
166 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
169 #define PENDING_FLAG_SET(playsink, flagtype) \
170 ((playsink->pending_blocked_pads) |= (1 << flagtype))
171 #define PENDING_FLAG_UNSET(playsink, flagtype) \
172 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
173 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
174 ((playsink->pending_blocked_pads) & (1 << flagtype))
175 #define PENDING_VIDEO_BLOCK(playsink) \
176 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
177 #define PENDING_AUDIO_BLOCK(playsink) \
178 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
179 #define PENDING_TEXT_BLOCK(playsink) \
180 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
188 gboolean async_pending;
189 gboolean need_async_start;
193 GstStreamSynchronizer *stream_synchronizer;
196 GstPlayAudioChain *audiochain;
197 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
198 GstPlayVideoChain *videochain;
199 GstPlayVisChain *vischain;
200 GstPlayTextChain *textchain;
204 gboolean audio_pad_raw;
205 gboolean audio_pad_blocked;
206 GstPad *audio_srcpad_stream_synchronizer;
207 GstPad *audio_sinkpad_stream_synchronizer;
208 gulong audio_block_id;
210 GstElement *audio_tee;
211 GstPad *audio_tee_sink;
212 GstPad *audio_tee_asrc;
213 GstPad *audio_tee_vissrc;
216 gboolean video_pad_raw;
217 gboolean video_pad_blocked;
218 GstPad *video_srcpad_stream_synchronizer;
219 GstPad *video_sinkpad_stream_synchronizer;
220 gulong video_block_id;
223 gboolean text_pad_blocked;
224 GstPad *text_srcpad_stream_synchronizer;
225 GstPad *text_sinkpad_stream_synchronizer;
226 gulong text_block_id;
228 guint32 pending_blocked_pads;
231 GstElement *audio_sink;
232 GstElement *video_sink;
233 GstElement *visualisation;
234 GstElement *text_sink;
237 gchar *font_desc; /* font description */
238 gchar *subtitle_encoding; /* subtitle encoding */
239 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
241 gboolean volume_changed; /* volume/mute changed while no audiochain */
242 gboolean mute_changed; /* ... has been created yet */
244 GstPlaySinkSendEventMode send_event_mode;
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 GQuark _playsink_reset_segment_event_marker_id = 0;
387 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
389 static void gst_play_sink_overlay_init (gpointer g_iface,
390 gpointer g_iface_data);
391 static void gst_play_sink_navigation_init (gpointer g_iface,
392 gpointer g_iface_data);
393 static void gst_play_sink_colorbalance_init (gpointer g_iface,
394 gpointer g_iface_data);
397 _do_init (GType type)
399 static const GInterfaceInfo svol_info = {
402 static const GInterfaceInfo ov_info = {
403 gst_play_sink_overlay_init,
406 static const GInterfaceInfo nav_info = {
407 gst_play_sink_navigation_init,
410 static const GInterfaceInfo col_info = {
411 gst_play_sink_colorbalance_init,
415 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
416 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
417 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
418 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
421 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
422 _do_init (g_define_type_id));
425 gst_play_sink_class_init (GstPlaySinkClass * klass)
427 GObjectClass *gobject_klass;
428 GstElementClass *gstelement_klass;
429 GstBinClass *gstbin_klass;
431 gobject_klass = (GObjectClass *) klass;
432 gstelement_klass = (GstElementClass *) klass;
433 gstbin_klass = (GstBinClass *) klass;
435 gobject_klass->dispose = gst_play_sink_dispose;
436 gobject_klass->finalize = gst_play_sink_finalize;
437 gobject_klass->set_property = gst_play_sink_set_property;
438 gobject_klass->get_property = gst_play_sink_get_property;
444 * Control the behaviour of playsink.
446 g_object_class_install_property (gobject_klass, PROP_FLAGS,
447 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
448 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
449 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
452 * GstPlaySink:volume:
454 * Get or set the current audio stream volume. 1.0 means 100%,
455 * 0.0 means mute. This uses a linear volume scale.
458 g_object_class_install_property (gobject_klass, PROP_VOLUME,
459 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
460 0.0, VOLUME_MAX_DOUBLE, 1.0,
461 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
462 g_object_class_install_property (gobject_klass, PROP_MUTE,
463 g_param_spec_boolean ("mute", "Mute",
464 "Mute the audio channel without changing the volume", FALSE,
465 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
466 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
467 g_param_spec_string ("subtitle-font-desc",
468 "Subtitle font description",
469 "Pango font description of font "
470 "to be used for subtitle rendering", NULL,
471 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
472 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
473 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
474 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
475 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
476 "be checked for an encoding to use. If that is not set either, "
477 "ISO-8859-15 will be assumed.", NULL,
478 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
479 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
480 g_param_spec_object ("vis-plugin", "Vis plugin",
481 "the visualization element to use (NULL = default)",
482 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
484 * GstPlaySink:sample:
486 * Get the currently rendered or prerolled sample in the video sink.
487 * The #GstCaps in the sample will describe the format of the buffer.
489 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
490 g_param_spec_boxed ("sample", "Sample",
491 "The last sample (NULL = no video available)",
492 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
494 * GstPlaySink:av-offset:
496 * Control the synchronisation offset between the audio and video streams.
497 * Positive values make the audio ahead of the video and negative values make
498 * the audio go behind the video.
502 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
503 g_param_spec_int64 ("av-offset", "AV Offset",
504 "The synchronisation offset between audio and video in nanoseconds",
505 G_MININT64, G_MAXINT64, 0,
506 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
509 * GstPlaySink:video-sink:
511 * Set the used video sink element. NULL will use the default sink. playsink
512 * must be in %GST_STATE_NULL
516 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
517 g_param_spec_object ("video-sink", "Video Sink",
518 "the video output element to use (NULL = default sink)",
519 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
521 * GstPlaySink:audio-sink:
523 * Set the used audio sink element. NULL will use the default sink. playsink
524 * must be in %GST_STATE_NULL
528 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
529 g_param_spec_object ("audio-sink", "Audio Sink",
530 "the audio output element to use (NULL = default sink)",
531 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
534 * GstPlaySink:text-sink:
536 * Set the used text sink element. NULL will use the default sink. playsink
537 * must be in %GST_STATE_NULL
541 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
542 g_param_spec_object ("text-sink", "Text sink",
543 "the text output element to use (NULL = default subtitleoverlay)",
544 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
547 * GstPlaySink::send-event-mode:
549 * Sets the handling method used for events received from send_event
550 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
551 * handling (push the event to all internal sinks).
555 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
556 g_param_spec_enum ("send-event-mode", "Send event mode",
557 "How to send events received in send_event function",
558 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
559 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
562 * GstPlaySink::force-aspect-ratio:
564 * Requests the video sink to enforce the video display aspect ratio.
568 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
569 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
570 "When enabled, scaling will respect original aspect ratio", TRUE,
571 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
573 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
574 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
575 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
578 * GstPlaySink::convert-sample
579 * @playsink: a #GstPlaySink
580 * @caps: the target format of the sample
582 * Action signal to retrieve the currently playing video sample in the format
583 * specified by @caps.
584 * If @caps is %NULL, no conversion will be performed and this function is
585 * equivalent to the #GstPlaySink::sample property.
587 * Returns: a #GstSample of the current video sample converted to #caps.
588 * The caps in the sample will describe the final layout of the buffer data.
589 * %NULL is returned when no current sample can be retrieved or when the
592 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
593 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
594 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
595 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
597 gst_element_class_add_pad_template (gstelement_klass,
598 gst_static_pad_template_get (&audiorawtemplate));
599 gst_element_class_add_pad_template (gstelement_klass,
600 gst_static_pad_template_get (&audiotemplate));
601 gst_element_class_add_pad_template (gstelement_klass,
602 gst_static_pad_template_get (&videorawtemplate));
603 gst_element_class_add_pad_template (gstelement_klass,
604 gst_static_pad_template_get (&videotemplate));
605 gst_element_class_add_pad_template (gstelement_klass,
606 gst_static_pad_template_get (&texttemplate));
607 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
609 "Convenience sink for multiple streams",
610 "Wim Taymans <wim.taymans@gmail.com>");
612 gstelement_klass->change_state =
613 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
614 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
615 gstelement_klass->request_new_pad =
616 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
617 gstelement_klass->release_pad =
618 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
620 gstbin_klass->handle_message =
621 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
623 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
624 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
626 _playsink_reset_segment_event_marker_id =
627 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
631 gst_play_sink_init (GstPlaySink * playsink)
633 GstColorBalanceChannel *channel;
636 playsink->video_sink = NULL;
637 playsink->audio_sink = NULL;
638 playsink->visualisation = NULL;
639 playsink->text_sink = NULL;
640 playsink->volume = 1.0;
641 playsink->font_desc = NULL;
642 playsink->subtitle_encoding = NULL;
643 playsink->flags = DEFAULT_FLAGS;
644 playsink->send_event_mode = MODE_DEFAULT;
645 playsink->force_aspect_ratio = TRUE;
647 playsink->stream_synchronizer =
648 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
649 gst_bin_add (GST_BIN_CAST (playsink),
650 GST_ELEMENT_CAST (playsink->stream_synchronizer));
652 g_rec_mutex_init (&playsink->lock);
653 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
656 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
658 channel->label = g_strdup ("CONTRAST");
659 channel->min_value = -1000;
660 channel->max_value = 1000;
661 playsink->colorbalance_channels =
662 g_list_append (playsink->colorbalance_channels, channel);
663 playsink->colorbalance_values[0] = 0;
666 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
668 channel->label = g_strdup ("BRIGHTNESS");
669 channel->min_value = -1000;
670 channel->max_value = 1000;
671 playsink->colorbalance_channels =
672 g_list_append (playsink->colorbalance_channels, channel);
673 playsink->colorbalance_values[1] = 0;
676 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
678 channel->label = g_strdup ("HUE");
679 channel->min_value = -1000;
680 channel->max_value = 1000;
681 playsink->colorbalance_channels =
682 g_list_append (playsink->colorbalance_channels, channel);
683 playsink->colorbalance_values[2] = 0;
686 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
688 channel->label = g_strdup ("SATURATION");
689 channel->min_value = -1000;
690 channel->max_value = 1000;
691 playsink->colorbalance_channels =
692 g_list_append (playsink->colorbalance_channels, channel);
693 playsink->colorbalance_values[3] = 0;
697 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
701 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
704 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
710 free_chain (GstPlayChain * chain)
714 gst_object_unref (chain->bin);
720 gst_play_sink_dispose (GObject * object)
722 GstPlaySink *playsink;
724 playsink = GST_PLAY_SINK (object);
726 if (playsink->audio_sink != NULL) {
727 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
728 gst_object_unref (playsink->audio_sink);
729 playsink->audio_sink = NULL;
731 if (playsink->video_sink != NULL) {
732 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
733 gst_object_unref (playsink->video_sink);
734 playsink->video_sink = NULL;
736 if (playsink->visualisation != NULL) {
737 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
738 gst_object_unref (playsink->visualisation);
739 playsink->visualisation = NULL;
741 if (playsink->text_sink != NULL) {
742 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
743 gst_object_unref (playsink->text_sink);
744 playsink->text_sink = NULL;
747 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
748 playsink->videodeinterlacechain = NULL;
749 free_chain ((GstPlayChain *) playsink->videochain);
750 playsink->videochain = NULL;
751 free_chain ((GstPlayChain *) playsink->audiochain);
752 playsink->audiochain = NULL;
753 free_chain ((GstPlayChain *) playsink->vischain);
754 playsink->vischain = NULL;
755 free_chain ((GstPlayChain *) playsink->textchain);
756 playsink->textchain = NULL;
758 if (playsink->audio_tee_sink) {
759 gst_object_unref (playsink->audio_tee_sink);
760 playsink->audio_tee_sink = NULL;
763 if (playsink->audio_tee_vissrc) {
764 gst_element_release_request_pad (playsink->audio_tee,
765 playsink->audio_tee_vissrc);
766 gst_object_unref (playsink->audio_tee_vissrc);
767 playsink->audio_tee_vissrc = NULL;
770 if (playsink->audio_tee_asrc) {
771 gst_element_release_request_pad (playsink->audio_tee,
772 playsink->audio_tee_asrc);
773 gst_object_unref (playsink->audio_tee_asrc);
774 playsink->audio_tee_asrc = NULL;
777 g_free (playsink->font_desc);
778 playsink->font_desc = NULL;
780 g_free (playsink->subtitle_encoding);
781 playsink->subtitle_encoding = NULL;
783 playsink->stream_synchronizer = NULL;
785 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
787 g_list_free (playsink->colorbalance_channels);
788 playsink->colorbalance_channels = NULL;
790 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
794 gst_play_sink_finalize (GObject * object)
796 GstPlaySink *playsink;
798 playsink = GST_PLAY_SINK (object);
800 g_rec_mutex_clear (&playsink->lock);
802 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
806 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
809 GstElement **elem = NULL, *old = NULL;
811 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
813 GST_PLAY_SINK_LOCK (playsink);
815 case GST_PLAY_SINK_TYPE_AUDIO:
816 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
817 elem = &playsink->audio_sink;
819 case GST_PLAY_SINK_TYPE_VIDEO:
820 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
821 elem = &playsink->video_sink;
823 case GST_PLAY_SINK_TYPE_TEXT:
824 elem = &playsink->text_sink;
832 gst_object_ref (sink);
835 GST_PLAY_SINK_UNLOCK (playsink);
839 gst_element_set_state (old, GST_STATE_NULL);
840 gst_object_unref (old);
845 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
847 GstElement *result = NULL;
848 GstElement *elem = NULL, *chainp = NULL;
850 GST_PLAY_SINK_LOCK (playsink);
852 case GST_PLAY_SINK_TYPE_AUDIO:
853 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
855 GstPlayAudioChain *chain;
856 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
857 chainp = chain->sink;
858 elem = playsink->audio_sink;
861 case GST_PLAY_SINK_TYPE_VIDEO:
862 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
864 GstPlayVideoChain *chain;
865 if ((chain = (GstPlayVideoChain *) playsink->videochain))
866 chainp = chain->sink;
867 elem = playsink->video_sink;
870 case GST_PLAY_SINK_TYPE_TEXT:
872 GstPlayTextChain *chain;
873 if ((chain = (GstPlayTextChain *) playsink->textchain))
874 chainp = chain->sink;
875 elem = playsink->text_sink;
882 /* we have an active chain with a sink, get the sink */
883 result = gst_object_ref (chainp);
885 /* nothing found, return last configured sink */
886 if (result == NULL && elem)
887 result = gst_object_ref (elem);
888 GST_PLAY_SINK_UNLOCK (playsink);
893 static GstPadProbeReturn
894 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
897 GstPlaySink *playsink;
898 GstPlayVisChain *chain;
900 playsink = GST_PLAY_SINK (user_data);
902 GST_PLAY_SINK_LOCK (playsink);
903 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
904 /* now try to change the plugin in the running vis chain */
905 if (!(chain = (GstPlayVisChain *) playsink->vischain))
908 /* unlink the old plugin and unghost the pad */
909 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
910 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
912 /* set the old plugin to NULL and remove */
913 gst_element_set_state (chain->vis, GST_STATE_NULL);
914 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
916 /* add new plugin and set state to playing */
917 chain->vis = playsink->visualisation;
918 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
919 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
922 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
923 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
926 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
927 GST_PAD_LINK_CHECK_NOTHING);
928 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
932 GST_PLAY_SINK_UNLOCK (playsink);
934 /* remove the probe and unblock the pad */
935 return GST_PAD_PROBE_REMOVE;
939 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
941 GstPlayVisChain *chain;
943 /* setting NULL means creating the default vis plugin */
945 vis = gst_element_factory_make ("goom", "vis");
947 /* simply return if we don't have a vis plugin here */
951 GST_PLAY_SINK_LOCK (playsink);
952 /* first store the new vis */
953 if (playsink->visualisation)
954 gst_object_unref (playsink->visualisation);
956 gst_object_ref_sink (vis);
957 playsink->visualisation = vis;
959 /* now try to change the plugin in the running vis chain, if we have no chain,
960 * we don't bother, any future vis chain will be created with the new vis
962 if (!(chain = (GstPlayVisChain *) playsink->vischain))
965 /* block the pad, the next time the callback is called we can change the
966 * visualisation. It's possible that this never happens or that the pad was
967 * already blocked. If the callback never happens, we don't have new data so
968 * we don't need the new vis plugin. If the pad was already blocked, the
969 * function returns FALSE but the previous pad block will do the right thing
971 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
972 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
973 gst_play_sink_vis_blocked, playsink, NULL);
975 GST_PLAY_SINK_UNLOCK (playsink);
981 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
983 GstElement *result = NULL;
984 GstPlayVisChain *chain;
986 GST_PLAY_SINK_LOCK (playsink);
987 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
988 /* we have an active chain, get the sink */
990 result = gst_object_ref (chain->vis);
992 /* nothing found, return last configured sink */
993 if (result == NULL && playsink->visualisation)
994 result = gst_object_ref (playsink->visualisation);
995 GST_PLAY_SINK_UNLOCK (playsink);
1001 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1003 GstPlayAudioChain *chain;
1005 GST_PLAY_SINK_LOCK (playsink);
1006 playsink->volume = volume;
1007 chain = (GstPlayAudioChain *) playsink->audiochain;
1008 if (chain && chain->volume) {
1009 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1010 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1011 chain->mute, volume, playsink->mute);
1012 /* if there is a mute element or we are not muted, set the volume */
1013 if (chain->mute || !playsink->mute)
1014 g_object_set (chain->volume, "volume", volume, NULL);
1016 GST_LOG_OBJECT (playsink, "no volume element");
1017 playsink->volume_changed = TRUE;
1019 GST_PLAY_SINK_UNLOCK (playsink);
1023 gst_play_sink_get_volume (GstPlaySink * playsink)
1026 GstPlayAudioChain *chain;
1028 GST_PLAY_SINK_LOCK (playsink);
1029 chain = (GstPlayAudioChain *) playsink->audiochain;
1030 result = playsink->volume;
1031 if (chain && chain->volume) {
1032 if (chain->mute || !playsink->mute) {
1033 g_object_get (chain->volume, "volume", &result, NULL);
1034 playsink->volume = result;
1037 GST_PLAY_SINK_UNLOCK (playsink);
1043 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1045 GstPlayAudioChain *chain;
1047 GST_PLAY_SINK_LOCK (playsink);
1048 playsink->mute = mute;
1049 chain = (GstPlayAudioChain *) playsink->audiochain;
1052 g_object_set (chain->mute, "mute", mute, NULL);
1053 } else if (chain->volume) {
1055 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1057 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1061 playsink->mute_changed = TRUE;
1063 GST_PLAY_SINK_UNLOCK (playsink);
1067 gst_play_sink_get_mute (GstPlaySink * playsink)
1070 GstPlayAudioChain *chain;
1072 GST_PLAY_SINK_LOCK (playsink);
1073 chain = (GstPlayAudioChain *) playsink->audiochain;
1074 if (chain && chain->mute) {
1075 g_object_get (chain->mute, "mute", &result, NULL);
1076 playsink->mute = result;
1078 result = playsink->mute;
1080 GST_PLAY_SINK_UNLOCK (playsink);
1086 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1090 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1091 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1095 add_chain (GstPlayChain * chain, gboolean add)
1097 if (chain->added == add)
1101 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1103 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1104 /* we don't want to lose our sink status */
1105 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1114 activate_chain (GstPlayChain * chain, gboolean activate)
1118 if (chain->activated == activate)
1121 GST_OBJECT_LOCK (chain->playsink);
1122 state = GST_STATE_TARGET (chain->playsink);
1123 GST_OBJECT_UNLOCK (chain->playsink);
1126 gst_element_set_state (chain->bin, state);
1128 gst_element_set_state (chain->bin, GST_STATE_NULL);
1130 chain->activated = activate;
1136 element_is_sink (GstElement * element)
1140 GST_OBJECT_LOCK (element);
1141 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1142 GST_OBJECT_UNLOCK (element);
1144 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1149 element_has_property (GstElement * element, const gchar * pname, GType type)
1153 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1155 if (pspec == NULL) {
1156 GST_DEBUG_OBJECT (element, "no %s property", pname);
1160 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1161 g_type_is_a (pspec->value_type, type)) {
1162 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1163 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1167 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1168 "and we expected it to be of type %s", pname,
1169 g_type_name (pspec->value_type), g_type_name (type));
1176 const gchar *prop_name;
1179 } FindPropertyHelper;
1182 find_property (const GValue * item, FindPropertyHelper * helper)
1184 GstElement *element = g_value_get_object (item);
1185 if (helper->need_sink && !element_is_sink (element)) {
1189 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1193 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1194 (helper->need_sink) ? "sink" : "element");
1195 return 0; /* keep it */
1198 /* FIXME: why not move these functions into core? */
1199 /* find a sink in the hierarchy with a property named @name. This function does
1200 * not increase the refcount of the returned object and thus remains valid as
1201 * long as the bin is valid. */
1203 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1204 const gchar * name, GType expected_type)
1206 GstElement *result = NULL;
1209 if (element_has_property (obj, name, expected_type)) {
1211 } else if (GST_IS_BIN (obj)) {
1213 GValue item = { 0, };
1214 FindPropertyHelper helper = { name, expected_type, TRUE };
1216 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1217 found = gst_iterator_find_custom (it,
1218 (GCompareFunc) find_property, &item, &helper);
1219 gst_iterator_free (it);
1221 result = g_value_get_object (&item);
1222 /* we don't need the extra ref */
1223 g_value_unset (&item);
1229 /* find an object in the hierarchy with a property named @name */
1231 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1232 const gchar * name, GType expected_type)
1234 GstElement *result = NULL;
1237 if (GST_IS_BIN (obj)) {
1239 GValue item = { 0, };
1240 FindPropertyHelper helper = { name, expected_type, FALSE };
1242 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1243 found = gst_iterator_find_custom (it,
1244 (GCompareFunc) find_property, &item, &helper);
1245 gst_iterator_free (it);
1247 result = g_value_dup_object (&item);
1248 g_value_unset (&item);
1251 if (element_has_property (obj, name, expected_type)) {
1253 gst_object_ref (obj);
1260 do_async_start (GstPlaySink * playsink)
1262 GstMessage *message;
1264 if (!playsink->need_async_start) {
1265 GST_INFO_OBJECT (playsink, "no async_start needed");
1269 playsink->async_pending = TRUE;
1271 GST_INFO_OBJECT (playsink, "Sending async_start message");
1272 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1273 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1274 (playsink), message);
1278 do_async_done (GstPlaySink * playsink)
1280 GstMessage *message;
1282 if (playsink->async_pending) {
1283 GST_INFO_OBJECT (playsink, "Sending async_done message");
1285 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1286 GST_CLOCK_TIME_NONE);
1287 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1288 (playsink), message);
1290 playsink->async_pending = FALSE;
1293 playsink->need_async_start = FALSE;
1296 /* try to change the state of an element. This function returns the element when
1297 * the state change could be performed. When this function returns NULL an error
1298 * occured and the element is unreffed if @unref is TRUE. */
1300 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1302 GstStateChangeReturn ret;
1305 ret = gst_element_set_state (element, GST_STATE_READY);
1306 if (ret == GST_STATE_CHANGE_FAILURE) {
1307 GST_DEBUG_OBJECT (playsink, "failed state change..");
1308 gst_element_set_state (element, GST_STATE_NULL);
1310 gst_object_unref (element);
1317 /* make the element (bin) that contains the elements needed to perform
1318 * video display. Only used for *raw* video streams.
1320 * +------------------------------------------------------------+
1322 * | +-------+ +----------+ +----------+ +---------+ |
1323 * | | queue | |colorspace| |videoscale| |videosink| |
1324 * | +-sink src-sink src-sink src-sink | |
1325 * | | +-------+ +----------+ +----------+ +---------+ |
1327 * +------------------------------------------------------------+
1330 static GstPlayVideoDeinterlaceChain *
1331 gen_video_deinterlace_chain (GstPlaySink * playsink)
1333 GstPlayVideoDeinterlaceChain *chain;
1336 GstElement *head = NULL, *prev = NULL;
1338 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1339 chain->chain.playsink = playsink;
1341 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1343 /* create a bin to hold objects, as we create them we add them to this bin so
1344 * that when something goes wrong we only need to unref the bin */
1345 chain->chain.bin = gst_bin_new ("vdbin");
1346 bin = GST_BIN_CAST (chain->chain.bin);
1347 gst_object_ref_sink (bin);
1349 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1350 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1351 if (chain->conv == NULL) {
1352 post_missing_element_message (playsink, COLORSPACE);
1353 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1354 (_("Missing element '%s' - check your GStreamer installation."),
1355 COLORSPACE), ("video rendering might fail"));
1357 gst_bin_add (bin, chain->conv);
1362 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1363 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1364 if (chain->deinterlace == NULL) {
1365 post_missing_element_message (playsink, "deinterlace");
1366 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1367 (_("Missing element '%s' - check your GStreamer installation."),
1368 "deinterlace"), ("deinterlacing won't work"));
1370 gst_bin_add (bin, chain->deinterlace);
1372 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1373 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1376 head = chain->deinterlace;
1378 prev = chain->deinterlace;
1382 pad = gst_element_get_static_pad (head, "sink");
1383 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1384 gst_object_unref (pad);
1386 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1390 pad = gst_element_get_static_pad (prev, "src");
1391 chain->srcpad = gst_ghost_pad_new ("src", pad);
1392 gst_object_unref (pad);
1394 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1397 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1398 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1404 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1405 (NULL), ("Failed to configure the video deinterlace chain."));
1406 free_chain ((GstPlayChain *) chain);
1412 is_valid_color_balance_element (GstColorBalance * bal)
1414 gboolean have_brightness = FALSE;
1415 gboolean have_contrast = FALSE;
1416 gboolean have_hue = FALSE;
1417 gboolean have_saturation = FALSE;
1418 const GList *channels, *l;
1420 channels = gst_color_balance_list_channels (bal);
1421 for (l = channels; l; l = l->next) {
1422 GstColorBalanceChannel *ch = l->data;
1424 if (g_strrstr (ch->label, "BRIGHTNESS"))
1425 have_brightness = TRUE;
1426 else if (g_strrstr (ch->label, "CONTRAST"))
1427 have_contrast = TRUE;
1428 else if (g_strrstr (ch->label, "HUE"))
1430 else if (g_strrstr (ch->label, "SATURATION"))
1431 have_saturation = TRUE;
1434 return have_brightness && have_contrast && have_hue && have_saturation;
1438 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1441 GstColorBalance *cb, **cb_out = user_data;
1443 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1444 valid = is_valid_color_balance_element (cb);
1447 && gst_color_balance_get_balance_type (*cb_out) ==
1448 GST_COLOR_BALANCE_SOFTWARE) {
1449 gst_object_unref (*cb_out);
1450 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1451 } else if (!*cb_out) {
1452 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1457 static GstColorBalance *
1458 find_color_balance_element (GstElement * element)
1461 GstColorBalance *cb = NULL;
1463 if (GST_IS_COLOR_BALANCE (element)
1464 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1465 return GST_COLOR_BALANCE (gst_object_ref (element));
1466 else if (!GST_IS_BIN (element))
1469 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1470 GST_TYPE_COLOR_BALANCE);
1471 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1472 &cb) == GST_ITERATOR_RESYNC)
1473 gst_iterator_resync (it);
1474 gst_iterator_free (it);
1480 colorbalance_value_changed_cb (GstColorBalance * balance,
1481 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1486 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1487 GstColorBalanceChannel *proxy = l->data;
1489 if (g_strrstr (channel->label, proxy->label)) {
1492 /* Convert to [0, 1] range */
1495 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1496 (gdouble) channel->min_value);
1497 /* Convert to proxy range */
1499 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1500 (gdouble) proxy->min_value);
1501 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1503 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1504 playsink->colorbalance_values[i]);
1511 update_colorbalance (GstPlaySink * playsink)
1513 GstColorBalance *balance = NULL;
1517 GST_OBJECT_LOCK (playsink);
1518 if (playsink->colorbalance_element) {
1520 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1522 GST_OBJECT_UNLOCK (playsink);
1526 g_signal_handlers_block_by_func (balance,
1527 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1529 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1530 GstColorBalanceChannel *proxy = l->data;
1531 GstColorBalanceChannel *channel = NULL;
1532 const GList *channels, *k;
1534 channels = gst_color_balance_list_channels (balance);
1535 for (k = channels; k; k = k->next) {
1536 GstColorBalanceChannel *tmp = k->data;
1538 if (g_strrstr (tmp->label, proxy->label)) {
1546 gst_color_balance_set_value (balance, channel,
1547 playsink->colorbalance_values[i]);
1550 g_signal_handlers_unblock_by_func (balance,
1551 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1553 gst_object_unref (balance);
1556 /* make the element (bin) that contains the elements needed to perform
1559 * +------------------------------------------------------------+
1561 * | +-------+ +----------+ +----------+ +---------+ |
1562 * | | queue | |colorspace| |videoscale| |videosink| |
1563 * | +-sink src-sink src-sink src-sink | |
1564 * | | +-------+ +----------+ +----------+ +---------+ |
1566 * +------------------------------------------------------------+
1569 static GstPlayVideoChain *
1570 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1572 GstPlayVideoChain *chain;
1575 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1577 chain = g_new0 (GstPlayVideoChain, 1);
1578 chain->chain.playsink = playsink;
1579 chain->chain.raw = raw;
1581 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1583 if (playsink->video_sink) {
1584 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1585 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1587 /* only try fallback if no specific sink was chosen */
1588 if (chain->sink == NULL) {
1589 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1590 elem = gst_element_factory_make ("autovideosink", "videosink");
1591 chain->sink = try_element (playsink, elem, TRUE);
1593 if (chain->sink == NULL) {
1594 /* if default sink from config.h is different then try it too */
1595 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1596 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1597 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1598 chain->sink = try_element (playsink, elem, TRUE);
1602 playsink->video_sink = gst_object_ref (chain->sink);
1604 if (chain->sink == NULL)
1608 /* if we can disable async behaviour of the sink, we can avoid adding a
1609 * queue for the audio chain. */
1611 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1614 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1615 async, GST_ELEMENT_NAME (elem));
1616 g_object_set (elem, "async", async, NULL);
1617 chain->async = async;
1619 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1620 chain->async = TRUE;
1623 /* Make sure the aspect ratio is kept */
1625 gst_play_sink_find_property_sinks (playsink, chain->sink,
1626 "force-aspect-ratio", G_TYPE_BOOLEAN);
1628 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1631 /* find ts-offset element */
1632 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1633 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1636 /* create a bin to hold objects, as we create them we add them to this bin so
1637 * that when something goes wrong we only need to unref the bin */
1638 chain->chain.bin = gst_bin_new ("vbin");
1639 bin = GST_BIN_CAST (chain->chain.bin);
1640 gst_object_ref_sink (bin);
1641 gst_bin_add (bin, chain->sink);
1643 /* Get the VideoOverlay element */
1645 GstVideoOverlay *overlay = NULL;
1647 GST_OBJECT_LOCK (playsink);
1648 if (playsink->overlay_element)
1649 gst_object_unref (playsink->overlay_element);
1650 playsink->overlay_element =
1651 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1652 GST_TYPE_VIDEO_OVERLAY));
1653 if (playsink->overlay_element)
1654 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1655 GST_OBJECT_UNLOCK (playsink);
1658 if (playsink->overlay_handle_set)
1659 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1660 if (playsink->overlay_handle_events_set)
1661 gst_video_overlay_handle_events (overlay,
1662 playsink->overlay_handle_events);
1663 if (playsink->overlay_render_rectangle_set)
1664 gst_video_overlay_set_render_rectangle (overlay,
1665 playsink->overlay_x, playsink->overlay_y,
1666 playsink->overlay_width, playsink->overlay_height);
1667 gst_object_unref (overlay);
1671 /* decouple decoder from sink, this improves playback quite a lot since the
1672 * decoder can continue while the sink blocks for synchronisation. We don't
1673 * need a lot of buffers as this consumes a lot of memory and we don't want
1674 * too little because else we would be context switching too quickly. */
1675 chain->queue = gst_element_factory_make ("queue", "vqueue");
1676 if (chain->queue == NULL) {
1677 post_missing_element_message (playsink, "queue");
1678 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1679 (_("Missing element '%s' - check your GStreamer installation."),
1680 "queue"), ("video rendering might be suboptimal"));
1684 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1685 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1686 gst_bin_add (bin, chain->queue);
1687 head = prev = chain->queue;
1690 GST_OBJECT_LOCK (playsink);
1691 if (playsink->colorbalance_element) {
1692 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1693 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1694 gst_object_unref (playsink->colorbalance_element);
1696 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1697 if (playsink->colorbalance_element) {
1698 g_signal_connect (playsink->colorbalance_element, "value-changed",
1699 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1701 GST_OBJECT_UNLOCK (playsink);
1703 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1704 || (!playsink->colorbalance_element
1705 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1706 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1707 gboolean use_balance = !playsink->colorbalance_element
1708 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1710 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1712 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1713 "use-converters", use_converters, "use-balance", use_balance, NULL);
1715 GST_OBJECT_LOCK (playsink);
1716 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1717 playsink->colorbalance_element =
1718 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1719 (chain->conv)->balance));
1720 GST_OBJECT_UNLOCK (playsink);
1722 gst_bin_add (bin, chain->conv);
1724 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1725 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1733 update_colorbalance (playsink);
1736 GST_DEBUG_OBJECT (playsink, "linking to sink");
1737 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1738 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1742 pad = gst_element_get_static_pad (head, "sink");
1743 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1745 /* sending audio/video flushes break stream changes when the pipeline
1746 * is paused and played again in 0.10 */
1748 gst_pad_set_event_function (chain->sinkpad,
1749 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1750 gst_pad_set_chain_function (chain->sinkpad,
1751 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1754 gst_object_unref (pad);
1755 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1762 if (!elem && !playsink->video_sink) {
1763 post_missing_element_message (playsink, "autovideosink");
1764 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1765 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1766 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1767 (_("Both autovideosink and %s elements are missing."),
1768 DEFAULT_VIDEOSINK), (NULL));
1770 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1771 (_("The autovideosink element is missing.")), (NULL));
1774 if (playsink->video_sink) {
1775 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1776 (_("Configured videosink %s is not working."),
1777 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1778 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1779 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1780 (_("Both autovideosink and %s elements are not working."),
1781 DEFAULT_VIDEOSINK), (NULL));
1783 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1784 (_("The autovideosink element is not working.")), (NULL));
1787 free_chain ((GstPlayChain *) chain);
1793 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1794 (NULL), ("Failed to configure the video sink."));
1795 /* checking sink made it READY */
1796 gst_element_set_state (chain->sink, GST_STATE_NULL);
1797 /* Remove chain from the bin to allow reuse later */
1798 gst_bin_remove (bin, chain->sink);
1799 free_chain ((GstPlayChain *) chain);
1805 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1808 GstPlayVideoChain *chain;
1809 GstStateChangeReturn ret;
1811 chain = playsink->videochain;
1813 chain->chain.raw = raw;
1815 /* if the chain was active we don't do anything */
1816 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1819 /* try to set the sink element to READY again */
1820 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1821 if (ret == GST_STATE_CHANGE_FAILURE)
1824 /* Get the VideoOverlay element */
1826 GstVideoOverlay *overlay = NULL;
1828 GST_OBJECT_LOCK (playsink);
1829 if (playsink->overlay_element)
1830 gst_object_unref (playsink->overlay_element);
1831 playsink->overlay_element =
1832 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1833 GST_TYPE_VIDEO_OVERLAY));
1834 if (playsink->overlay_element)
1835 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1836 GST_OBJECT_UNLOCK (playsink);
1839 if (playsink->overlay_handle_set)
1840 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1841 if (playsink->overlay_handle_events_set)
1842 gst_video_overlay_handle_events (overlay,
1843 playsink->overlay_handle_events);
1844 if (playsink->overlay_render_rectangle_set)
1845 gst_video_overlay_set_render_rectangle (overlay,
1846 playsink->overlay_x, playsink->overlay_y,
1847 playsink->overlay_width, playsink->overlay_height);
1848 gst_object_unref (overlay);
1852 /* find ts-offset element */
1853 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1854 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1857 /* if we can disable async behaviour of the sink, we can avoid adding a
1858 * queue for the audio chain. */
1860 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1863 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1864 async, GST_ELEMENT_NAME (elem));
1865 g_object_set (elem, "async", async, NULL);
1866 chain->async = async;
1868 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1869 chain->async = TRUE;
1872 /* Make sure the aspect ratio is kept */
1874 gst_play_sink_find_property_sinks (playsink, chain->sink,
1875 "force-aspect-ratio", G_TYPE_BOOLEAN);
1877 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1880 GST_OBJECT_LOCK (playsink);
1881 if (playsink->colorbalance_element) {
1882 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1883 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1884 gst_object_unref (playsink->colorbalance_element);
1886 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1887 if (playsink->colorbalance_element) {
1888 g_signal_connect (playsink->colorbalance_element, "value-changed",
1889 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1891 GST_OBJECT_UNLOCK (playsink);
1894 gboolean use_balance = !playsink->colorbalance_element
1895 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1897 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1899 GST_OBJECT_LOCK (playsink);
1900 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1901 playsink->colorbalance_element =
1902 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1903 (chain->conv)->balance));
1904 GST_OBJECT_UNLOCK (playsink);
1907 update_colorbalance (playsink);
1913 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1917 GstStructure *structure;
1918 event = gst_event_new_segment (segment);
1919 structure = gst_event_writable_structure (event);
1920 gst_structure_id_set (structure,
1921 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1926 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1927 const gchar * sink_type,
1928 gboolean * sink_ignore_wrong_state,
1929 gboolean * sink_custom_flush_finished,
1930 gboolean * sink_pending_flush, GstSegment * sink_segment)
1932 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1934 const GstStructure *structure = gst_event_get_structure (event);
1936 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1937 gchar *custom_flush;
1938 gchar *custom_flush_finish;
1940 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1941 custom_flush_finish =
1942 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1943 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1944 GST_DEBUG_OBJECT (pad,
1945 "Custom %s flush event received, marking to flush %s", sink_type,
1947 GST_PLAY_SINK_LOCK (playsink);
1948 *sink_ignore_wrong_state = TRUE;
1949 *sink_custom_flush_finished = FALSE;
1950 GST_PLAY_SINK_UNLOCK (playsink);
1951 } else if (strcmp (gst_structure_get_name (structure),
1952 custom_flush_finish) == 0) {
1953 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1955 GST_PLAY_SINK_LOCK (playsink);
1956 *sink_pending_flush = TRUE;
1957 *sink_custom_flush_finished = TRUE;
1958 GST_PLAY_SINK_UNLOCK (playsink);
1961 g_free (custom_flush);
1962 g_free (custom_flush_finish);
1963 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1964 GST_PLAY_SINK_LOCK (playsink);
1965 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1967 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1968 GST_PLAY_SINK_UNLOCK (playsink);
1971 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1972 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1974 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1975 const GstSegment *segment;
1977 gst_event_parse_segment (event, &segment);
1978 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1980 GST_PLAY_SINK_LOCK (playsink);
1981 if (sink_segment->format != segment->format) {
1982 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1984 gst_format_get_name (sink_segment->format),
1985 gst_format_get_name (segment->format));
1986 gst_segment_init (sink_segment, segment->format);
1989 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1990 sink_type, sink_segment);
1991 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1992 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1993 sink_type, sink_segment);
1994 GST_PLAY_SINK_UNLOCK (playsink);
1997 gst_event_unref (event);
1998 gst_object_unref (playsink);
2002 static GstFlowReturn
2003 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2004 const gchar * sink_type,
2005 gboolean * sink_ignore_wrong_state,
2006 gboolean * sink_custom_flush_finished,
2007 gboolean * sink_pending_flush, GstSegment * sink_segment)
2009 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2010 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2013 GST_PLAY_SINK_LOCK (playsink);
2015 if (*sink_pending_flush) {
2017 GstStructure *structure;
2019 *sink_pending_flush = FALSE;
2021 GST_PLAY_SINK_UNLOCK (playsink);
2023 /* make the bin drop all cached data.
2024 * This event will be dropped on the src pad, if any. */
2025 event = gst_event_new_flush_start ();
2026 structure = gst_event_writable_structure (event);
2027 gst_structure_id_set (structure,
2028 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2030 GST_DEBUG_OBJECT (pad,
2031 "Pushing %s flush-start event with reset segment marker set: %"
2032 GST_PTR_FORMAT, sink_type, event);
2033 gst_pad_send_event (pad, event);
2035 /* make queue drop all cached data.
2036 * This event will be dropped on the src pad. */
2037 event = gst_event_new_flush_stop (TRUE);
2038 structure = gst_event_writable_structure (event);
2039 gst_structure_id_set (structure,
2040 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2042 GST_DEBUG_OBJECT (pad,
2043 "Pushing %s flush-stop event with reset segment marker set: %"
2044 GST_PTR_FORMAT, sink_type, event);
2045 gst_pad_send_event (pad, event);
2047 /* Re-sync queue segment info after flush-stop.
2048 * This event will be dropped on the src pad. */
2049 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2052 _generate_update_newsegment_event (pad, sink_segment, &event1);
2053 GST_DEBUG_OBJECT (playsink,
2054 "Pushing segment event with reset "
2055 "segment marker set: %" GST_PTR_FORMAT, event1);
2056 gst_pad_send_event (pad, event1);
2059 GST_PLAY_SINK_UNLOCK (playsink);
2062 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2064 GST_PLAY_SINK_LOCK (playsink);
2065 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2066 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2068 if (*sink_custom_flush_finished) {
2069 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2070 "wrong state for %s", sink_type);
2071 *sink_ignore_wrong_state = FALSE;
2076 GST_PLAY_SINK_UNLOCK (playsink);
2078 gst_object_unref (playsink);
2079 gst_object_unref (tbin);
2083 /* sending audio/video flushes break stream changes when the pipeline
2084 * is paused and played again in 0.10 */
2087 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2089 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2090 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2093 ret = gst_play_sink_sink_event (pad, event, "video",
2094 &playsink->video_ignore_wrong_state,
2095 &playsink->video_custom_flush_finished,
2096 &playsink->video_pending_flush, &playsink->video_segment);
2098 gst_object_unref (playsink);
2099 gst_object_unref (tbin);
2103 static GstFlowReturn
2104 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2106 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2107 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2110 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2111 &playsink->video_ignore_wrong_state,
2112 &playsink->video_custom_flush_finished,
2113 &playsink->video_pending_flush, &playsink->video_segment);
2115 gst_object_unref (playsink);
2116 gst_object_unref (tbin);
2121 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2123 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2124 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2127 ret = gst_play_sink_sink_event (pad, event, "audio",
2128 &playsink->audio_ignore_wrong_state,
2129 &playsink->audio_custom_flush_finished,
2130 &playsink->audio_pending_flush, &playsink->audio_segment);
2132 gst_object_unref (playsink);
2133 gst_object_unref (tbin);
2137 static GstFlowReturn
2138 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2140 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2141 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2144 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2145 &playsink->audio_ignore_wrong_state,
2146 &playsink->audio_custom_flush_finished,
2147 &playsink->audio_pending_flush, &playsink->audio_segment);
2149 gst_object_unref (playsink);
2150 gst_object_unref (tbin);
2156 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2159 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2162 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2163 &playsink->text_ignore_wrong_state,
2164 &playsink->text_custom_flush_finished,
2165 &playsink->text_pending_flush, &playsink->text_segment);
2167 gst_object_unref (playsink);
2172 static GstFlowReturn
2173 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2177 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2179 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2180 &playsink->text_ignore_wrong_state,
2181 &playsink->text_custom_flush_finished,
2182 &playsink->text_pending_flush, &playsink->text_segment);
2184 gst_object_unref (playsink);
2189 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2193 const GstStructure *structure;
2195 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2197 structure = gst_event_get_structure (event);
2200 gst_structure_id_has_field (structure,
2201 _playsink_reset_segment_event_marker_id)) {
2202 /* the events marked with a reset segment marker
2203 * are sent internally to reset the queue and
2204 * must be dropped here */
2205 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2206 "segment marker set: %" GST_PTR_FORMAT, event);
2211 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2214 gst_event_unref (event);
2218 /* make an element for playback of video with subtitles embedded.
2219 * Only used for *raw* video streams.
2221 * +--------------------------------------------+
2223 * | +--------+ +-----------------+ |
2224 * | | queue | | subtitleoverlay | |
2225 * video--src sink---video_sink | |
2226 * | +--------+ | src--src
2227 * text------------------text_sink | |
2228 * | +-----------------+ |
2229 * +--------------------------------------------+
2232 static GstPlayTextChain *
2233 gen_text_chain (GstPlaySink * playsink)
2235 GstPlayTextChain *chain;
2238 GstPad *videosinkpad, *textsinkpad, *srcpad;
2240 chain = g_new0 (GstPlayTextChain, 1);
2241 chain->chain.playsink = playsink;
2243 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2245 chain->chain.bin = gst_bin_new ("tbin");
2246 bin = GST_BIN_CAST (chain->chain.bin);
2247 gst_object_ref_sink (bin);
2249 videosinkpad = textsinkpad = srcpad = NULL;
2251 /* first try to hook the text pad to the custom sink */
2252 if (playsink->text_sink) {
2253 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2254 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2257 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2260 /* make sure the sparse subtitles don't participate in the preroll */
2261 g_object_set (elem, "async", FALSE, NULL);
2262 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2263 gst_bin_add (bin, chain->sink);
2264 /* NOTE streamsynchronizer needs streams decoupled */
2265 /* make a little queue */
2266 chain->queue = gst_element_factory_make ("queue", "subqueue");
2267 if (chain->queue == NULL) {
2268 post_missing_element_message (playsink, "queue");
2269 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2270 (_("Missing element '%s' - check your GStreamer installation."),
2271 "queue"), ("rendering might be suboptimal"));
2273 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2274 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2275 "silent", TRUE, NULL);
2276 gst_bin_add (bin, chain->queue);
2278 /* we have a custom sink, this will be our textsinkpad */
2279 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2280 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2281 /* we're all fine now and we can add the sink to the chain */
2282 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2283 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2285 GST_WARNING_OBJECT (playsink,
2286 "can't find a sink pad on custom text sink");
2287 gst_bin_remove (bin, chain->sink);
2288 gst_bin_remove (bin, chain->queue);
2290 chain->queue = NULL;
2292 /* try to set sync to true but it's no biggie when we can't */
2293 if (chain->sink && (elem =
2294 gst_play_sink_find_property_sinks (playsink, chain->sink,
2295 "sync", G_TYPE_BOOLEAN)))
2296 g_object_set (elem, "sync", TRUE, NULL);
2299 gst_bin_remove (bin, chain->sink);
2301 GST_WARNING_OBJECT (playsink,
2302 "can't find async property in custom text sink");
2305 if (textsinkpad == NULL) {
2306 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2307 (_("Custom text sink element is not usable.")),
2308 ("fallback to default subtitleoverlay"));
2312 if (textsinkpad == NULL) {
2313 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2314 /* make a little queue */
2315 chain->queue = gst_element_factory_make ("queue", "vqueue");
2316 if (chain->queue == NULL) {
2317 post_missing_element_message (playsink, "queue");
2318 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2319 (_("Missing element '%s' - check your GStreamer installation."),
2320 "queue"), ("video rendering might be suboptimal"));
2322 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2323 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2324 "silent", TRUE, NULL);
2325 gst_bin_add (bin, chain->queue);
2326 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2330 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2331 if (chain->overlay == NULL) {
2332 post_missing_element_message (playsink, "subtitleoverlay");
2333 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2334 (_("Missing element '%s' - check your GStreamer installation."),
2335 "subtitleoverlay"), ("subtitle rendering disabled"));
2337 GstElement *element;
2339 gst_bin_add (bin, chain->overlay);
2341 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2342 if (playsink->font_desc) {
2343 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2344 playsink->font_desc, NULL);
2346 if (playsink->subtitle_encoding) {
2347 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2348 playsink->subtitle_encoding, NULL);
2351 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2352 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2354 /* make another little queue to decouple streams */
2355 element = gst_element_factory_make ("queue", "subqueue");
2356 if (element == NULL) {
2357 post_missing_element_message (playsink, "queue");
2358 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2359 (_("Missing element '%s' - check your GStreamer installation."),
2360 "queue"), ("rendering might be suboptimal"));
2362 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2363 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2364 "silent", TRUE, NULL);
2365 gst_bin_add (bin, element);
2366 if (gst_element_link_pads_full (element, "src", chain->overlay,
2367 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2368 textsinkpad = gst_element_get_static_pad (element, "sink");
2369 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2371 gst_bin_remove (bin, chain->sink);
2372 gst_bin_remove (bin, chain->overlay);
2374 chain->overlay = NULL;
2375 gst_object_unref (videosinkpad);
2376 videosinkpad = NULL;
2383 if (videosinkpad == NULL) {
2384 /* if we still don't have a videosink, we don't have an overlay. the only
2385 * thing we can do is insert an identity and ghost the src
2387 chain->identity = gst_element_factory_make ("identity", "tidentity");
2388 if (chain->identity == NULL) {
2389 post_missing_element_message (playsink, "identity");
2390 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2391 (_("Missing element '%s' - check your GStreamer installation."),
2392 "identity"), (NULL));
2394 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2395 g_object_set (chain->identity, "silent", TRUE, NULL);
2396 gst_bin_add (bin, chain->identity);
2397 srcpad = gst_element_get_static_pad (chain->identity, "src");
2398 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2402 /* expose the ghostpads */
2404 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2405 gst_object_unref (videosinkpad);
2406 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2409 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2410 gst_object_unref (textsinkpad);
2412 gst_pad_set_event_function (chain->textsinkpad,
2413 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2414 gst_pad_set_chain_function (chain->textsinkpad,
2415 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2417 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2420 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2421 gst_object_unref (srcpad);
2423 gst_pad_set_event_function (chain->srcpad,
2424 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2426 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2433 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2437 g_object_get (object, "volume", &vol, NULL);
2438 playsink->volume = vol;
2440 g_object_notify (G_OBJECT (playsink), "volume");
2444 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2448 g_object_get (object, "mute", &mute, NULL);
2449 playsink->mute = mute;
2451 g_object_notify (G_OBJECT (playsink), "mute");
2454 /* make the chain that contains the elements needed to perform
2457 * We add a tee as the first element so that we can link the visualisation chain
2458 * to it when requested.
2460 * +-------------------------------------------------------------+
2462 * | +---------+ +----------+ +---------+ +---------+ |
2463 * | |audioconv| |audioscale| | volume | |audiosink| |
2464 * | +-srck src-sink src-sink src-sink | |
2465 * | | +---------+ +----------+ +---------+ +---------+ |
2467 * +-------------------------------------------------------------+
2469 static GstPlayAudioChain *
2470 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2472 GstPlayAudioChain *chain;
2474 gboolean have_volume;
2476 GstElement *head, *prev, *elem = NULL;
2478 chain = g_new0 (GstPlayAudioChain, 1);
2479 chain->chain.playsink = playsink;
2480 chain->chain.raw = raw;
2482 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2484 if (playsink->audio_sink) {
2485 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2486 playsink->audio_sink);
2487 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2489 /* only try fallback if no specific sink was chosen */
2490 if (chain->sink == NULL) {
2491 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2492 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2493 chain->sink = try_element (playsink, elem, TRUE);
2495 if (chain->sink == NULL) {
2496 /* if default sink from config.h is different then try it too */
2497 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2498 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2499 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2500 chain->sink = try_element (playsink, elem, TRUE);
2504 playsink->audio_sink = gst_object_ref (chain->sink);
2506 if (chain->sink == NULL)
2509 chain->chain.bin = gst_bin_new ("abin");
2510 bin = GST_BIN_CAST (chain->chain.bin);
2511 gst_object_ref_sink (bin);
2512 gst_bin_add (bin, chain->sink);
2514 /* we have to add a queue when we need to decouple for the video sink in
2515 * visualisations and for streamsynchronizer */
2516 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2517 chain->queue = gst_element_factory_make ("queue", "aqueue");
2518 if (chain->queue == NULL) {
2519 post_missing_element_message (playsink, "queue");
2520 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2521 (_("Missing element '%s' - check your GStreamer installation."),
2522 "queue"), ("audio playback and visualizations might not work"));
2526 g_object_set (chain->queue, "silent", TRUE, NULL);
2527 gst_bin_add (bin, chain->queue);
2528 prev = head = chain->queue;
2531 /* find ts-offset element */
2532 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2533 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2536 /* check if the sink, or something within the sink, has the volume property.
2537 * If it does we don't need to add a volume element. */
2539 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2542 chain->volume = elem;
2544 g_signal_connect (chain->volume, "notify::volume",
2545 G_CALLBACK (notify_volume_cb), playsink);
2547 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2549 chain->sink_volume = TRUE;
2550 /* if the sink also has a mute property we can use this as well. We'll only
2551 * use the mute property if there is a volume property. We can simulate the
2552 * mute with the volume otherwise. */
2554 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2557 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2558 g_signal_connect (chain->mute, "notify::mute",
2559 G_CALLBACK (notify_mute_cb), playsink);
2561 /* use the sink to control the volume and mute */
2562 if (playsink->volume_changed) {
2563 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2564 playsink->volume_changed = FALSE;
2566 if (playsink->mute_changed) {
2568 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2571 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2573 playsink->mute_changed = FALSE;
2576 /* no volume, we need to add a volume element when we can */
2577 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2578 have_volume = FALSE;
2579 chain->sink_volume = FALSE;
2582 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2583 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2584 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2585 gboolean use_volume =
2586 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2587 GST_DEBUG_OBJECT (playsink,
2588 "creating audioconvert with use-converters %d, use-volume %d",
2589 use_converters, use_volume);
2591 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2592 "use-converters", use_converters, "use-volume", use_volume, NULL);
2593 gst_bin_add (bin, chain->conv);
2595 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2596 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2603 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2604 GstPlaySinkAudioConvert *conv =
2605 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2608 chain->volume = conv->volume;
2611 g_signal_connect (chain->volume, "notify::volume",
2612 G_CALLBACK (notify_volume_cb), playsink);
2614 /* volume also has the mute property */
2615 chain->mute = chain->volume;
2616 g_signal_connect (chain->mute, "notify::mute",
2617 G_CALLBACK (notify_mute_cb), playsink);
2619 /* configure with the latest volume and mute */
2620 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2622 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2628 /* we only have to link to the previous element if we have something in
2629 * front of the sink */
2630 GST_DEBUG_OBJECT (playsink, "linking to sink");
2631 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2632 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2636 /* post a warning if we have no way to configure the volume */
2638 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2639 (_("No volume control found")), ("Volume/mute is not available"));
2642 /* and ghost the sinkpad of the headmost element */
2643 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2644 pad = gst_element_get_static_pad (head, "sink");
2645 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2647 /* sending audio/video flushes break stream changes when the pipeline
2648 * is paused and played again in 0.10 */
2650 gst_pad_set_event_function (chain->sinkpad,
2651 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2652 gst_pad_set_chain_function (chain->sinkpad,
2653 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2656 gst_object_unref (pad);
2657 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2664 if (!elem && !playsink->audio_sink) {
2665 post_missing_element_message (playsink, "autoaudiosink");
2666 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2667 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2668 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2669 (_("Both autoaudiosink and %s elements are missing."),
2670 DEFAULT_AUDIOSINK), (NULL));
2672 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2673 (_("The autoaudiosink element is missing.")), (NULL));
2676 if (playsink->audio_sink) {
2677 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2678 (_("Configured audiosink %s is not working."),
2679 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2680 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2681 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2682 (_("Both autoaudiosink and %s elements are not working."),
2683 DEFAULT_AUDIOSINK), (NULL));
2685 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2686 (_("The autoaudiosink element is not working.")), (NULL));
2689 free_chain ((GstPlayChain *) chain);
2694 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2695 (NULL), ("Failed to configure the audio sink."));
2696 /* checking sink made it READY */
2697 gst_element_set_state (chain->sink, GST_STATE_NULL);
2698 /* Remove chain from the bin to allow reuse later */
2699 gst_bin_remove (bin, chain->sink);
2700 free_chain ((GstPlayChain *) chain);
2706 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2709 GstPlayAudioChain *chain;
2710 GstStateChangeReturn ret;
2711 GstPlaySinkAudioConvert *conv;
2713 chain = playsink->audiochain;
2714 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2716 chain->chain.raw = raw;
2718 /* if the chain was active we don't do anything */
2719 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2722 /* try to set the sink element to READY again */
2723 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2724 if (ret == GST_STATE_CHANGE_FAILURE)
2727 /* find ts-offset element */
2728 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2729 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2732 /* check if the sink, or something within the sink, has the volume property.
2733 * If it does we don't need to add a volume element. */
2735 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2738 chain->volume = elem;
2740 if (playsink->volume_changed) {
2741 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2743 /* use the sink to control the volume */
2744 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2745 playsink->volume_changed = FALSE;
2748 g_signal_connect (chain->volume, "notify::volume",
2749 G_CALLBACK (notify_volume_cb), playsink);
2750 /* if the sink also has a mute property we can use this as well. We'll only
2751 * use the mute property if there is a volume property. We can simulate the
2752 * mute with the volume otherwise. */
2754 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2757 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2758 g_signal_connect (chain->mute, "notify::mute",
2759 G_CALLBACK (notify_mute_cb), playsink);
2762 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2764 /* no volume, we need to add a volume element when we can */
2765 g_object_set (chain->conv, "use-volume",
2766 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2767 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2769 /* Disconnect signals */
2770 disconnect_chain (chain, playsink);
2772 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2773 chain->volume = conv->volume;
2774 chain->mute = chain->volume;
2776 g_signal_connect (chain->volume, "notify::volume",
2777 G_CALLBACK (notify_volume_cb), playsink);
2779 g_signal_connect (chain->mute, "notify::mute",
2780 G_CALLBACK (notify_mute_cb), playsink);
2782 /* configure with the latest volume and mute */
2783 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2784 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2787 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2793 * +-------------------------------------------------------------------+
2795 * | +----------+ +------------+ +----------+ +-------+ |
2796 * | | visqueue | | audioconv | | audiores | | vis | |
2797 * | +-sink src-sink + samp src-sink src-sink src-+ |
2798 * | | +----------+ +------------+ +----------+ +-------+ | |
2800 * +-------------------------------------------------------------------+
2803 static GstPlayVisChain *
2804 gen_vis_chain (GstPlaySink * playsink)
2806 GstPlayVisChain *chain;
2812 chain = g_new0 (GstPlayVisChain, 1);
2813 chain->chain.playsink = playsink;
2815 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2817 chain->chain.bin = gst_bin_new ("visbin");
2818 bin = GST_BIN_CAST (chain->chain.bin);
2819 gst_object_ref_sink (bin);
2821 /* we're queuing raw audio here, we can remove this queue when we can disable
2822 * async behaviour in the video sink. */
2823 chain->queue = gst_element_factory_make ("queue", "visqueue");
2824 if (chain->queue == NULL)
2826 g_object_set (chain->queue, "silent", TRUE, NULL);
2827 gst_bin_add (bin, chain->queue);
2829 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2830 if (chain->conv == NULL)
2831 goto no_audioconvert;
2832 gst_bin_add (bin, chain->conv);
2834 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2835 if (chain->resample == NULL)
2836 goto no_audioresample;
2837 gst_bin_add (bin, chain->resample);
2839 /* this pad will be used for blocking the dataflow and switching the vis
2841 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2843 if (playsink->visualisation) {
2844 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2845 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2847 if (chain->vis == NULL) {
2848 GST_DEBUG_OBJECT (playsink, "trying goom");
2849 elem = gst_element_factory_make ("goom", "vis");
2850 chain->vis = try_element (playsink, elem, TRUE);
2852 if (chain->vis == NULL)
2855 gst_bin_add (bin, chain->vis);
2857 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2858 GST_PAD_LINK_CHECK_NOTHING);
2860 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2861 GST_PAD_LINK_CHECK_NOTHING);
2863 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2864 GST_PAD_LINK_CHECK_NOTHING);
2868 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2869 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2871 pad = gst_element_get_static_pad (chain->queue, "sink");
2872 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2873 gst_object_unref (pad);
2874 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2876 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2877 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2884 post_missing_element_message (playsink, "queue");
2885 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2886 (_("Missing element '%s' - check your GStreamer installation."),
2888 free_chain ((GstPlayChain *) chain);
2893 post_missing_element_message (playsink, "audioconvert");
2894 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2895 (_("Missing element '%s' - check your GStreamer installation."),
2896 "audioconvert"), ("possibly a liboil version mismatch?"));
2897 free_chain ((GstPlayChain *) chain);
2902 post_missing_element_message (playsink, "audioresample");
2903 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2904 (_("Missing element '%s' - check your GStreamer installation."),
2905 "audioresample"), (NULL));
2906 free_chain ((GstPlayChain *) chain);
2911 post_missing_element_message (playsink, "goom");
2912 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2913 (_("Missing element '%s' - check your GStreamer installation."),
2915 free_chain ((GstPlayChain *) chain);
2920 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2921 (NULL), ("Failed to configure the visualisation element."));
2922 /* element made it to READY */
2923 gst_element_set_state (chain->vis, GST_STATE_NULL);
2924 free_chain ((GstPlayChain *) chain);
2929 /* this function is called when all the request pads are requested and when we
2930 * have to construct the final pipeline. Based on the flags we construct the
2931 * final output pipelines.
2934 gst_play_sink_reconfigure (GstPlaySink * playsink)
2937 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2939 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2941 /* assume we need nothing */
2942 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2944 GST_PLAY_SINK_LOCK (playsink);
2945 GST_OBJECT_LOCK (playsink);
2946 /* get flags, there are protected with the object lock */
2947 flags = playsink->flags;
2948 GST_OBJECT_UNLOCK (playsink);
2950 /* figure out which components we need */
2951 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2952 /* we have subtitles and we are requested to show it */
2956 if (((flags & GST_PLAY_FLAG_VIDEO)
2957 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2958 /* we have video and we are requested to show it */
2961 /* we only deinterlace if native video is not requested and
2962 * we have raw video */
2963 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2964 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2965 need_deinterlace = TRUE;
2968 if (playsink->audio_pad) {
2969 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2972 if (playsink->audio_pad_raw) {
2973 /* only can do vis with raw uncompressed audio */
2974 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2975 /* also add video when we add visualisation */
2982 /* we have a text_pad and we need text rendering, in this case we need a
2983 * video_pad to combine the video with the text or visualizations */
2984 if (need_text && !need_video && !playsink->text_sink) {
2985 if (playsink->video_pad) {
2987 } else if (need_audio) {
2988 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2989 (_("Can't play a text file without video or visualizations.")),
2990 ("Have text pad but no video pad or visualizations"));
2993 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2994 (_("Can't play a text file without video or visualizations.")),
2995 ("Have text pad but no video pad or visualizations"));
2996 GST_PLAY_SINK_UNLOCK (playsink);
3001 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3002 need_video, need_vis, need_text);
3004 /* set up video pipeline */
3006 gboolean raw, async;
3008 /* we need a raw sink when we do vis or when we have a raw pad */
3009 raw = need_vis ? TRUE : playsink->video_pad_raw;
3010 /* we try to set the sink async=FALSE when we need vis, this way we can
3011 * avoid a queue in the audio chain. */
3014 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3015 playsink->video_pad_raw);
3017 if (playsink->videochain) {
3018 /* try to reactivate the chain */
3019 if (!setup_video_chain (playsink, raw, async)) {
3020 if (playsink->video_sinkpad_stream_synchronizer) {
3021 gst_element_release_request_pad (GST_ELEMENT_CAST
3022 (playsink->stream_synchronizer),
3023 playsink->video_sinkpad_stream_synchronizer);
3024 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3025 playsink->video_sinkpad_stream_synchronizer = NULL;
3026 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3027 playsink->video_srcpad_stream_synchronizer = NULL;
3030 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3032 /* Remove the sink from the bin to keep its state
3033 * and unparent it to allow reuse */
3034 if (playsink->videochain->sink)
3035 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3036 playsink->videochain->sink);
3038 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3039 free_chain ((GstPlayChain *) playsink->videochain);
3040 playsink->videochain = NULL;
3042 GST_OBJECT_LOCK (playsink);
3043 if (playsink->overlay_element)
3044 gst_object_unref (playsink->overlay_element);
3045 playsink->overlay_element = NULL;
3047 if (playsink->colorbalance_element) {
3048 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3049 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3050 gst_object_unref (playsink->colorbalance_element);
3052 playsink->colorbalance_element = NULL;
3053 GST_OBJECT_UNLOCK (playsink);
3057 if (!playsink->videochain)
3058 playsink->videochain = gen_video_chain (playsink, raw, async);
3059 if (!playsink->videochain)
3062 if (!playsink->video_sinkpad_stream_synchronizer) {
3063 GValue item = { 0, };
3066 playsink->video_sinkpad_stream_synchronizer =
3067 gst_element_get_request_pad (GST_ELEMENT_CAST
3068 (playsink->stream_synchronizer), "sink_%u");
3069 it = gst_pad_iterate_internal_links
3070 (playsink->video_sinkpad_stream_synchronizer);
3072 gst_iterator_next (it, &item);
3073 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3074 g_value_unset (&item);
3075 g_assert (playsink->video_srcpad_stream_synchronizer);
3076 gst_iterator_free (it);
3079 if (playsink->video_pad)
3080 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3081 playsink->video_sinkpad_stream_synchronizer);
3083 if (need_deinterlace) {
3084 if (!playsink->videodeinterlacechain)
3085 playsink->videodeinterlacechain =
3086 gen_video_deinterlace_chain (playsink);
3087 if (!playsink->videodeinterlacechain)
3090 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3092 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3094 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3095 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3097 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3098 playsink->videochain->sinkpad);
3099 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3100 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3102 if (playsink->videodeinterlacechain) {
3103 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3104 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3109 GST_DEBUG_OBJECT (playsink, "adding video chain");
3110 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3111 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3112 /* if we are not part of vis or subtitles, set the ghostpad target */
3113 if (!need_vis && !need_text && (!playsink->textchain
3114 || !playsink->text_pad)) {
3115 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3116 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3117 playsink->videochain->sinkpad);
3118 if (playsink->videodeinterlacechain
3119 && playsink->videodeinterlacechain->srcpad)
3120 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3121 playsink->videochain->sinkpad);
3122 if (need_deinterlace)
3123 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3124 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3126 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3127 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3130 GST_DEBUG_OBJECT (playsink, "no video needed");
3131 if (playsink->videochain) {
3132 GST_DEBUG_OBJECT (playsink, "removing video chain");
3133 if (playsink->vischain) {
3136 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3138 /* also had visualisation, release the tee srcpad before we then
3139 * unlink the video from it */
3140 if (playsink->audio_tee_vissrc) {
3141 gst_element_release_request_pad (playsink->audio_tee,
3142 playsink->audio_tee_vissrc);
3143 gst_object_unref (playsink->audio_tee_vissrc);
3144 playsink->audio_tee_vissrc = NULL;
3147 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3148 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3151 if (playsink->video_sinkpad_stream_synchronizer) {
3152 gst_element_release_request_pad (GST_ELEMENT_CAST
3153 (playsink->stream_synchronizer),
3154 playsink->video_sinkpad_stream_synchronizer);
3155 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3156 playsink->video_sinkpad_stream_synchronizer = NULL;
3157 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3158 playsink->video_srcpad_stream_synchronizer = NULL;
3161 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3162 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3163 if (playsink->videochain->ts_offset)
3164 gst_object_unref (playsink->videochain->ts_offset);
3165 playsink->videochain->ts_offset = NULL;
3168 if (playsink->videodeinterlacechain) {
3169 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3170 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3173 if (playsink->video_pad)
3174 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3176 GST_OBJECT_LOCK (playsink);
3177 if (playsink->overlay_element)
3178 gst_object_unref (playsink->overlay_element);
3179 playsink->overlay_element = NULL;
3181 if (playsink->colorbalance_element) {
3182 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3183 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3184 gst_object_unref (playsink->colorbalance_element);
3186 playsink->colorbalance_element = NULL;
3187 GST_OBJECT_UNLOCK (playsink);
3194 GST_DEBUG_OBJECT (playsink, "adding audio");
3196 /* get a raw sink if we are asked for a raw pad */
3197 raw = playsink->audio_pad_raw;
3199 if (playsink->audiochain) {
3200 /* try to reactivate the chain */
3201 if (!setup_audio_chain (playsink, raw)) {
3202 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3203 if (playsink->audio_tee_asrc) {
3204 gst_element_release_request_pad (playsink->audio_tee,
3205 playsink->audio_tee_asrc);
3206 gst_object_unref (playsink->audio_tee_asrc);
3207 playsink->audio_tee_asrc = NULL;
3210 if (playsink->audio_sinkpad_stream_synchronizer) {
3211 gst_element_release_request_pad (GST_ELEMENT_CAST
3212 (playsink->stream_synchronizer),
3213 playsink->audio_sinkpad_stream_synchronizer);
3214 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3215 playsink->audio_sinkpad_stream_synchronizer = NULL;
3216 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3217 playsink->audio_srcpad_stream_synchronizer = NULL;
3220 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3222 /* Remove the sink from the bin to keep its state
3223 * and unparent it to allow reuse */
3224 if (playsink->audiochain->sink)
3225 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3226 playsink->audiochain->sink);
3228 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3229 disconnect_chain (playsink->audiochain, playsink);
3230 playsink->audiochain->volume = NULL;
3231 playsink->audiochain->mute = NULL;
3232 if (playsink->audiochain->ts_offset)
3233 gst_object_unref (playsink->audiochain->ts_offset);
3234 playsink->audiochain->ts_offset = NULL;
3235 free_chain ((GstPlayChain *) playsink->audiochain);
3236 playsink->audiochain = NULL;
3237 playsink->volume_changed = playsink->mute_changed = FALSE;
3241 if (!playsink->audiochain) {
3242 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3243 playsink->audiochain = gen_audio_chain (playsink, raw);
3246 if (!playsink->audio_sinkpad_stream_synchronizer) {
3247 GValue item = { 0, };
3250 playsink->audio_sinkpad_stream_synchronizer =
3251 gst_element_get_request_pad (GST_ELEMENT_CAST
3252 (playsink->stream_synchronizer), "sink_%u");
3253 it = gst_pad_iterate_internal_links
3254 (playsink->audio_sinkpad_stream_synchronizer);
3256 gst_iterator_next (it, &item);
3257 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3258 g_value_unset (&item);
3259 g_assert (playsink->audio_srcpad_stream_synchronizer);
3260 gst_iterator_free (it);
3263 if (playsink->audiochain) {
3264 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3265 if (playsink->audio_tee_asrc == NULL) {
3266 playsink->audio_tee_asrc =
3267 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3269 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3270 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3271 gst_pad_link_full (playsink->audio_tee_asrc,
3272 playsink->audio_sinkpad_stream_synchronizer,
3273 GST_PAD_LINK_CHECK_NOTHING);
3274 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3275 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3278 GST_DEBUG_OBJECT (playsink, "no audio needed");
3279 /* we have no audio or we are requested to not play audio */
3280 if (playsink->audiochain) {
3281 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3282 /* release the audio pad */
3283 if (playsink->audio_tee_asrc) {
3284 gst_element_release_request_pad (playsink->audio_tee,
3285 playsink->audio_tee_asrc);
3286 gst_object_unref (playsink->audio_tee_asrc);
3287 playsink->audio_tee_asrc = NULL;
3290 if (playsink->audio_sinkpad_stream_synchronizer) {
3291 gst_element_release_request_pad (GST_ELEMENT_CAST
3292 (playsink->stream_synchronizer),
3293 playsink->audio_sinkpad_stream_synchronizer);
3294 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3295 playsink->audio_sinkpad_stream_synchronizer = NULL;
3296 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3297 playsink->audio_srcpad_stream_synchronizer = NULL;
3300 if (playsink->audiochain->sink_volume) {
3301 disconnect_chain (playsink->audiochain, playsink);
3302 playsink->audiochain->volume = NULL;
3303 playsink->audiochain->mute = NULL;
3304 if (playsink->audiochain->ts_offset)
3305 gst_object_unref (playsink->audiochain->ts_offset);
3306 playsink->audiochain->ts_offset = NULL;
3308 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3309 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3316 if (!playsink->vischain)
3317 playsink->vischain = gen_vis_chain (playsink);
3319 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3321 if (playsink->vischain) {
3322 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3324 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3325 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3326 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3327 if (playsink->audio_tee_vissrc == NULL) {
3328 playsink->audio_tee_vissrc =
3329 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3331 gst_pad_link_full (playsink->audio_tee_vissrc,
3332 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3333 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3334 GST_PAD_LINK_CHECK_NOTHING);
3335 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3336 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3337 gst_object_unref (srcpad);
3340 GST_DEBUG_OBJECT (playsink, "no vis needed");
3341 if (playsink->vischain) {
3342 if (playsink->audio_tee_vissrc) {
3343 gst_element_release_request_pad (playsink->audio_tee,
3344 playsink->audio_tee_vissrc);
3345 gst_object_unref (playsink->audio_tee_vissrc);
3346 playsink->audio_tee_vissrc = NULL;
3348 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3349 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3350 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3355 GST_DEBUG_OBJECT (playsink, "adding text");
3356 if (!playsink->textchain) {
3357 GST_DEBUG_OBJECT (playsink, "creating text chain");
3358 playsink->textchain = gen_text_chain (playsink);
3360 if (playsink->textchain) {
3363 GST_DEBUG_OBJECT (playsink, "adding text chain");
3364 if (playsink->textchain->overlay)
3365 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3367 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3369 if (!playsink->text_sinkpad_stream_synchronizer) {
3370 GValue item = { 0, };
3372 playsink->text_sinkpad_stream_synchronizer =
3373 gst_element_get_request_pad (GST_ELEMENT_CAST
3374 (playsink->stream_synchronizer), "sink_%u");
3375 it = gst_pad_iterate_internal_links
3376 (playsink->text_sinkpad_stream_synchronizer);
3378 gst_iterator_next (it, &item);
3379 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3380 g_value_unset (&item);
3381 g_assert (playsink->text_srcpad_stream_synchronizer);
3382 gst_iterator_free (it);
3384 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3385 playsink->text_sinkpad_stream_synchronizer);
3386 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3387 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3390 if (need_vis || need_video) {
3395 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3396 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3397 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3398 GST_PAD_LINK_CHECK_NOTHING);
3399 gst_object_unref (srcpad);
3401 if (need_deinterlace)
3402 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3403 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3405 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3406 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3408 gst_pad_link_full (playsink->textchain->srcpad,
3409 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3412 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3415 GST_DEBUG_OBJECT (playsink, "no text needed");
3416 /* we have no subtitles/text or we are requested to not show them */
3418 if (playsink->text_sinkpad_stream_synchronizer) {
3419 gst_element_release_request_pad (GST_ELEMENT_CAST
3420 (playsink->stream_synchronizer),
3421 playsink->text_sinkpad_stream_synchronizer);
3422 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3423 playsink->text_sinkpad_stream_synchronizer = NULL;
3424 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3425 playsink->text_srcpad_stream_synchronizer = NULL;
3428 if (playsink->textchain) {
3429 if (playsink->text_pad == NULL) {
3430 /* no text pad, remove the chain entirely */
3431 GST_DEBUG_OBJECT (playsink, "removing text chain");
3432 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3433 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3435 /* we have a chain and a textpad, turn the subtitles off */
3436 GST_DEBUG_OBJECT (playsink, "turning off the text");
3437 if (playsink->textchain->overlay)
3438 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3442 if (!need_video && playsink->video_pad) {
3443 if (playsink->video_sinkpad_stream_synchronizer) {
3444 gst_element_release_request_pad (GST_ELEMENT_CAST
3445 (playsink->stream_synchronizer),
3446 playsink->video_sinkpad_stream_synchronizer);
3447 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3448 playsink->video_sinkpad_stream_synchronizer = NULL;
3449 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3450 playsink->video_srcpad_stream_synchronizer = NULL;
3453 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3456 if (playsink->text_pad && !playsink->textchain)
3457 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3459 update_av_offset (playsink);
3460 do_async_done (playsink);
3461 GST_PLAY_SINK_UNLOCK (playsink);
3468 /* gen_ chain already posted error */
3469 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3470 GST_PLAY_SINK_UNLOCK (playsink);
3476 * gst_play_sink_set_flags:
3477 * @playsink: a #GstPlaySink
3478 * @flags: #GstPlayFlags
3480 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3481 * when constructing the sink pipelins.
3483 * Returns: TRUE if the flags could be configured.
3486 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3488 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3490 GST_OBJECT_LOCK (playsink);
3491 playsink->flags = flags;
3492 GST_OBJECT_UNLOCK (playsink);
3498 * gst_play_sink_get_flags:
3499 * @playsink: a #GstPlaySink
3501 * Get the flags of @playsink. That flags control the behaviour of the sink when
3502 * it constructs the sink pipelines.
3504 * Returns: the currently configured #GstPlayFlags.
3507 gst_play_sink_get_flags (GstPlaySink * playsink)
3511 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3513 GST_OBJECT_LOCK (playsink);
3514 res = playsink->flags;
3515 GST_OBJECT_UNLOCK (playsink);
3521 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3523 GstPlayTextChain *chain;
3525 GST_PLAY_SINK_LOCK (playsink);
3526 chain = (GstPlayTextChain *) playsink->textchain;
3527 g_free (playsink->font_desc);
3528 playsink->font_desc = g_strdup (desc);
3529 if (chain && chain->overlay) {
3530 g_object_set (chain->overlay, "font-desc", desc, NULL);
3532 GST_PLAY_SINK_UNLOCK (playsink);
3536 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3538 gchar *result = NULL;
3539 GstPlayTextChain *chain;
3541 GST_PLAY_SINK_LOCK (playsink);
3542 chain = (GstPlayTextChain *) playsink->textchain;
3543 if (chain && chain->overlay) {
3544 g_object_get (chain->overlay, "font-desc", &result, NULL);
3545 playsink->font_desc = g_strdup (result);
3547 result = g_strdup (playsink->font_desc);
3549 GST_PLAY_SINK_UNLOCK (playsink);
3555 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3556 const gchar * encoding)
3558 GstPlayTextChain *chain;
3560 GST_PLAY_SINK_LOCK (playsink);
3561 chain = (GstPlayTextChain *) playsink->textchain;
3562 g_free (playsink->subtitle_encoding);
3563 playsink->subtitle_encoding = g_strdup (encoding);
3564 if (chain && chain->overlay) {
3565 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3567 GST_PLAY_SINK_UNLOCK (playsink);
3571 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3573 gchar *result = NULL;
3574 GstPlayTextChain *chain;
3576 GST_PLAY_SINK_LOCK (playsink);
3577 chain = (GstPlayTextChain *) playsink->textchain;
3578 if (chain && chain->overlay) {
3579 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3580 playsink->subtitle_encoding = g_strdup (result);
3582 result = g_strdup (playsink->subtitle_encoding);
3584 GST_PLAY_SINK_UNLOCK (playsink);
3590 update_av_offset (GstPlaySink * playsink)
3593 GstPlayAudioChain *achain;
3594 GstPlayVideoChain *vchain;
3596 av_offset = playsink->av_offset;
3597 achain = (GstPlayAudioChain *) playsink->audiochain;
3598 vchain = (GstPlayVideoChain *) playsink->videochain;
3600 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3601 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3602 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3604 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3609 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3611 GST_PLAY_SINK_LOCK (playsink);
3612 playsink->av_offset = av_offset;
3613 update_av_offset (playsink);
3614 GST_PLAY_SINK_UNLOCK (playsink);
3618 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3622 GST_PLAY_SINK_LOCK (playsink);
3623 result = playsink->av_offset;
3624 GST_PLAY_SINK_UNLOCK (playsink);
3630 * gst_play_sink_get_last_sample:
3631 * @playsink: a #GstPlaySink
3633 * Get the last displayed sample from @playsink. This sample is in the native
3634 * format of the sink element, the caps in the result sample contain the format
3635 * of the frame data.
3637 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3641 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3643 GstSample *result = NULL;
3644 GstPlayVideoChain *chain;
3646 GST_PLAY_SINK_LOCK (playsink);
3647 GST_DEBUG_OBJECT (playsink, "taking last sample");
3648 /* get the video chain if we can */
3649 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3650 GST_DEBUG_OBJECT (playsink, "found video chain");
3651 /* see if the chain is active */
3652 if (chain->chain.activated && chain->sink) {
3655 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3657 /* find and get the last-buffer property now */
3659 gst_play_sink_find_property (playsink, chain->sink,
3660 "last-sample", GST_TYPE_SAMPLE))) {
3661 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3662 g_object_get (elem, "last-sample", &result, NULL);
3663 gst_object_unref (elem);
3667 GST_PLAY_SINK_UNLOCK (playsink);
3673 * gst_play_sink_convert_sample:
3674 * @playsink: a #GstPlaySink
3677 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3678 * be in the native format of the sink element and the caps on the buffer
3679 * describe the format of the frame. If @caps is not %NULL, the video
3680 * frame will be converted to the format of the caps.
3682 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3683 * available or when the conversion failed.
3686 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3691 result = gst_play_sink_get_last_sample (playsink);
3692 if (result != NULL && caps != NULL) {
3695 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3696 if (temp == NULL && err)
3699 gst_sample_unref (result);
3707 /* I'm really uncertain whether we should make playsink post an error
3708 * on the bus or not. It's not like it's a critical issue regarding
3709 * playsink behaviour. */
3710 GST_ERROR ("Error converting frame: %s", err->message);
3711 gst_sample_unref (result);
3718 is_raw_structure (GstStructure * s)
3722 name = gst_structure_get_name (s);
3724 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3730 is_raw_pad (GstPad * pad)
3732 GstPad *peer = gst_pad_get_peer (pad);
3734 gboolean raw = TRUE;
3739 caps = gst_pad_get_current_caps (peer);
3743 caps = gst_pad_query_caps (peer, NULL);
3745 n = gst_caps_get_size (caps);
3746 for (i = 0; i < n; i++) {
3747 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3751 } else if (raw != r) {
3752 GST_ERROR_OBJECT (pad,
3753 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3759 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3761 gst_caps_unref (caps);
3762 gst_object_unref (peer);
3767 static GstPadProbeReturn
3768 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3769 gpointer user_data);
3772 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3774 if (playsink->video_pad) {
3776 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3777 (playsink->video_pad)));
3778 if (blocked && playsink->video_block_id == 0) {
3779 playsink->video_block_id =
3780 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3781 sinkpad_blocked_cb, playsink, NULL);
3782 } else if (!blocked && playsink->video_block_id) {
3783 gst_pad_remove_probe (opad, playsink->video_block_id);
3784 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3785 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3786 playsink->video_block_id = 0;
3787 playsink->video_pad_blocked = FALSE;
3789 gst_object_unref (opad);
3794 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3796 if (playsink->audio_pad) {
3798 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3799 (playsink->audio_pad)));
3800 if (blocked && playsink->audio_block_id == 0) {
3801 playsink->audio_block_id =
3802 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3803 sinkpad_blocked_cb, playsink, NULL);
3804 } else if (!blocked && playsink->audio_block_id) {
3805 gst_pad_remove_probe (opad, playsink->audio_block_id);
3806 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3807 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3808 playsink->audio_block_id = 0;
3809 playsink->audio_pad_blocked = FALSE;
3811 gst_object_unref (opad);
3816 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3818 if (playsink->text_pad) {
3820 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3821 (playsink->text_pad)));
3822 if (blocked && playsink->text_block_id == 0) {
3823 playsink->text_block_id =
3824 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3825 sinkpad_blocked_cb, playsink, NULL);
3826 } else if (!blocked && playsink->text_block_id) {
3827 gst_pad_remove_probe (opad, playsink->text_block_id);
3828 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3829 playsink->text_block_id = 0;
3830 playsink->text_pad_blocked = FALSE;
3832 gst_object_unref (opad);
3836 static GstPadProbeReturn
3837 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3840 GstPlaySink *playsink = (GstPlaySink *) user_data;
3843 GST_PLAY_SINK_LOCK (playsink);
3845 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3846 if (pad == playsink->video_pad) {
3847 playsink->video_pad_blocked = TRUE;
3848 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3849 } else if (pad == playsink->audio_pad) {
3850 playsink->audio_pad_blocked = TRUE;
3851 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3852 } else if (pad == playsink->text_pad) {
3853 playsink->text_pad_blocked = TRUE;
3854 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3857 /* We reconfigure when for ALL streams:
3858 * * there isn't a pad
3859 * * OR the pad is blocked
3860 * * OR there are no pending blocks on that pad
3863 if ((!playsink->video_pad || playsink->video_pad_blocked
3864 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3865 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3866 && (!playsink->text_pad || playsink->text_pad_blocked
3867 || !PENDING_TEXT_BLOCK (playsink))) {
3868 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3870 if (playsink->video_pad) {
3871 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3872 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3873 playsink->video_pad_raw);
3876 if (playsink->audio_pad) {
3877 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3878 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3879 playsink->audio_pad_raw);
3882 gst_play_sink_reconfigure (playsink);
3884 video_set_blocked (playsink, FALSE);
3885 audio_set_blocked (playsink, FALSE);
3886 text_set_blocked (playsink, FALSE);
3889 gst_object_unref (pad);
3891 GST_PLAY_SINK_UNLOCK (playsink);
3893 return GST_PAD_PROBE_OK;
3897 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3899 gboolean reconfigure = FALSE;
3903 g_object_get (pad, "caps", &caps, NULL);
3907 if (pad == playsink->audio_pad) {
3908 raw = is_raw_pad (pad);
3909 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3910 && playsink->audiochain;
3911 GST_DEBUG_OBJECT (pad,
3912 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3914 } else if (pad == playsink->video_pad) {
3915 raw = is_raw_pad (pad);
3916 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3917 && playsink->videochain;
3918 GST_DEBUG_OBJECT (pad,
3919 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3923 gst_caps_unref (caps);
3926 GST_PLAY_SINK_LOCK (playsink);
3927 video_set_blocked (playsink, TRUE);
3928 audio_set_blocked (playsink, TRUE);
3929 text_set_blocked (playsink, TRUE);
3930 GST_PLAY_SINK_UNLOCK (playsink);
3935 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3936 GstPlaySinkType type)
3938 gulong *block_id = NULL;
3940 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3942 GST_PLAY_SINK_LOCK (playsink);
3943 if (pad == playsink->video_pad) {
3944 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3945 type != GST_PLAY_SINK_TYPE_VIDEO)
3947 block_id = &playsink->video_block_id;
3948 } else if (pad == playsink->audio_pad) {
3949 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3950 type != GST_PLAY_SINK_TYPE_AUDIO)
3952 block_id = &playsink->audio_block_id;
3953 } else if (pad == playsink->text_pad) {
3954 if (type != GST_PLAY_SINK_TYPE_TEXT)
3956 block_id = &playsink->text_block_id;
3959 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3961 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3964 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3965 sinkpad_blocked_cb, playsink, NULL);
3966 PENDING_FLAG_SET (playsink, type);
3967 gst_object_unref (blockpad);
3969 GST_PLAY_SINK_UNLOCK (playsink);
3976 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3978 GST_PLAY_SINK_UNLOCK (playsink);
3984 * gst_play_sink_request_pad
3985 * @playsink: a #GstPlaySink
3986 * @type: a #GstPlaySinkType
3988 * Create or return a pad of @type.
3990 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3993 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3996 gboolean created = FALSE;
3997 gboolean activate = TRUE;
3998 const gchar *pad_name = NULL;
3999 gulong *block_id = NULL;
4001 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4003 GST_PLAY_SINK_LOCK (playsink);
4005 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4006 case GST_PLAY_SINK_TYPE_AUDIO:
4007 pad_name = "audio_sink";
4008 if (!playsink->audio_tee) {
4009 GST_LOG_OBJECT (playsink, "creating tee");
4010 /* create tee when needed. This element will feed the audio sink chain
4011 * and the vis chain. */
4012 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4013 if (playsink->audio_tee == NULL) {
4014 post_missing_element_message (playsink, "tee");
4015 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4016 (_("Missing element '%s' - check your GStreamer installation."),
4021 playsink->audio_tee_sink =
4022 gst_element_get_static_pad (playsink->audio_tee, "sink");
4023 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4024 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4027 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4029 if (!playsink->audio_pad) {
4030 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4031 playsink->audio_pad =
4032 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4033 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4034 G_CALLBACK (caps_notify_cb), playsink);
4037 playsink->audio_pad_raw = FALSE;
4038 res = playsink->audio_pad;
4039 block_id = &playsink->audio_block_id;
4041 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4042 case GST_PLAY_SINK_TYPE_VIDEO:
4043 pad_name = "video_sink";
4044 if (!playsink->video_pad) {
4045 GST_LOG_OBJECT (playsink, "ghosting videosink");
4046 playsink->video_pad =
4047 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4048 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4049 G_CALLBACK (caps_notify_cb), playsink);
4052 playsink->video_pad_raw = FALSE;
4053 res = playsink->video_pad;
4054 block_id = &playsink->video_block_id;
4056 case GST_PLAY_SINK_TYPE_TEXT:
4057 GST_LOG_OBJECT (playsink, "ghosting text");
4058 if (!playsink->text_pad) {
4059 playsink->text_pad =
4060 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4063 res = playsink->text_pad;
4064 block_id = &playsink->text_block_id;
4066 case GST_PLAY_SINK_TYPE_FLUSHING:
4070 /* we need a unique padname for the flushing pad. */
4071 padname = g_strdup_printf ("flushing_%u", playsink->count);
4072 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4083 GST_PLAY_SINK_UNLOCK (playsink);
4085 if (created && res) {
4086 /* we have to add the pad when it's active or we get an error when the
4087 * element is 'running' */
4088 gst_pad_set_active (res, TRUE);
4089 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4090 if (block_id && *block_id == 0) {
4092 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4095 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4096 sinkpad_blocked_cb, playsink, NULL);
4097 PENDING_FLAG_SET (playsink, type);
4098 gst_object_unref (blockpad);
4101 gst_pad_set_active (res, activate);
4109 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4110 const gchar * name, const GstCaps * caps)
4114 GstPlaySinkType type;
4115 const gchar *tplname;
4117 g_return_val_if_fail (templ != NULL, NULL);
4119 GST_DEBUG_OBJECT (element, "name:%s", name);
4121 psink = GST_PLAY_SINK (element);
4122 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4124 /* Figure out the GstPlaySinkType based on the template */
4125 if (!strcmp (tplname, "audio_sink"))
4126 type = GST_PLAY_SINK_TYPE_AUDIO;
4127 else if (!strcmp (tplname, "audio_raw_sink"))
4128 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4129 else if (!strcmp (tplname, "video_sink"))
4130 type = GST_PLAY_SINK_TYPE_VIDEO;
4131 else if (!strcmp (tplname, "video_raw_sink"))
4132 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4133 else if (!strcmp (tplname, "text_sink"))
4134 type = GST_PLAY_SINK_TYPE_TEXT;
4136 goto unknown_template;
4138 pad = gst_play_sink_request_pad (psink, type);
4142 GST_WARNING_OBJECT (element, "Unknown pad template");
4147 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4149 GstPad **res = NULL;
4150 gboolean untarget = TRUE;
4152 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4154 GST_PLAY_SINK_LOCK (playsink);
4155 if (pad == playsink->video_pad) {
4156 res = &playsink->video_pad;
4157 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4159 } else if (pad == playsink->audio_pad) {
4160 res = &playsink->audio_pad;
4161 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4163 } else if (pad == playsink->text_pad) {
4164 res = &playsink->text_pad;
4166 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4170 GST_PLAY_SINK_UNLOCK (playsink);
4173 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4174 gst_pad_set_active (*res, FALSE);
4176 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4177 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4179 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4180 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4186 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4188 GstPlaySink *psink = GST_PLAY_SINK (element);
4190 gst_play_sink_release_pad (psink, pad);
4194 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4196 GstPlaySink *playsink;
4198 playsink = GST_PLAY_SINK_CAST (bin);
4200 switch (GST_MESSAGE_TYPE (message)) {
4201 case GST_MESSAGE_STEP_DONE:
4206 gboolean flush, intermediate, eos;
4209 GST_INFO_OBJECT (playsink, "Handling step-done message");
4210 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4211 &intermediate, &duration, &eos);
4213 if (format == GST_FORMAT_BUFFERS) {
4214 /* for the buffer format, we align the other streams */
4215 if (playsink->audiochain) {
4219 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4222 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4223 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4227 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4230 case GST_MESSAGE_ELEMENT:{
4231 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4232 GstVideoOverlay *overlay;
4234 GST_OBJECT_LOCK (playsink);
4235 if (playsink->overlay_element
4236 && GST_OBJECT_CAST (playsink->overlay_element) !=
4237 GST_MESSAGE_SRC (message)) {
4238 gst_object_unref (playsink->overlay_element);
4239 playsink->overlay_element = NULL;
4242 if (!playsink->overlay_element)
4243 playsink->overlay_element =
4244 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4246 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4247 GST_OBJECT_UNLOCK (playsink);
4249 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4251 if (playsink->overlay_handle_set)
4252 gst_video_overlay_set_window_handle (playsink->overlay_element,
4253 playsink->overlay_handle);
4254 if (playsink->overlay_handle_events_set)
4255 gst_video_overlay_handle_events (playsink->overlay_element,
4256 playsink->overlay_handle_events);
4257 if (playsink->overlay_render_rectangle_set)
4258 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4259 playsink->overlay_x, playsink->overlay_y,
4260 playsink->overlay_width, playsink->overlay_height);
4262 gst_object_unref (overlay);
4263 gst_message_unref (message);
4264 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4266 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4272 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4277 /* Send an event to our sinks until one of them works; don't then send to the
4278 * remaining sinks (unlike GstBin)
4279 * Special case: If a text sink is set we need to send the event
4280 * to them in case it's source is different from the a/v stream's source.
4283 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4285 gboolean res = TRUE;
4286 if (playsink->send_event_mode == MODE_FIRST) {
4287 if (playsink->textchain && playsink->textchain->sink) {
4288 gst_event_ref (event);
4290 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4291 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4293 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4297 if (playsink->videochain) {
4298 gst_event_ref (event);
4300 gst_element_send_event (playsink->videochain->chain.bin,
4302 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4305 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4307 if (playsink->audiochain) {
4308 gst_event_ref (event);
4310 gst_element_send_event (playsink->audiochain->chain.bin,
4312 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4315 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4319 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4320 (GST_ELEMENT_CAST (playsink), event);
4324 gst_event_unref (event);
4328 /* We only want to send the event to a single sink (overriding GstBin's
4329 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4330 * events appropriately. So, this is a messy duplication of code. */
4332 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4334 gboolean res = FALSE;
4335 GstEventType event_type = GST_EVENT_TYPE (event);
4336 GstPlaySink *playsink;
4337 playsink = GST_PLAY_SINK_CAST (element);
4338 switch (event_type) {
4339 case GST_EVENT_SEEK:
4340 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4341 res = gst_play_sink_send_event_to_sink (playsink, event);
4343 case GST_EVENT_STEP:
4348 gboolean flush, intermediate;
4349 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4351 if (format == GST_FORMAT_BUFFERS) {
4352 /* for buffers, we will try to step video frames, for other formats we
4353 * send the step to all sinks */
4354 res = gst_play_sink_send_event_to_sink (playsink, event);
4357 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4364 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4371 static GstStateChangeReturn
4372 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4374 GstStateChangeReturn ret;
4375 GstStateChangeReturn bret;
4376 GstPlaySink *playsink;
4377 playsink = GST_PLAY_SINK (element);
4378 switch (transition) {
4379 case GST_STATE_CHANGE_READY_TO_PAUSED:
4380 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4382 playsink->need_async_start = TRUE;
4383 /* we want to go async to PAUSED until we managed to configure and add the
4385 do_async_start (playsink);
4386 ret = GST_STATE_CHANGE_ASYNC;
4388 /* block all pads here */
4389 GST_PLAY_SINK_LOCK (playsink);
4390 if (playsink->video_pad && playsink->video_block_id == 0) {
4392 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4393 (playsink->video_pad)));
4394 playsink->video_block_id =
4395 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4396 sinkpad_blocked_cb, playsink, NULL);
4397 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4398 gst_object_unref (opad);
4401 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4403 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4404 (playsink->audio_pad)));
4405 playsink->audio_block_id =
4406 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4407 sinkpad_blocked_cb, playsink, NULL);
4408 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4409 gst_object_unref (opad);
4412 if (playsink->text_pad && playsink->text_block_id == 0) {
4414 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4415 (playsink->text_pad)));
4416 playsink->text_block_id =
4417 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4418 sinkpad_blocked_cb, playsink, NULL);
4419 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4420 gst_object_unref (opad);
4422 GST_PLAY_SINK_UNLOCK (playsink);
4424 case GST_STATE_CHANGE_PAUSED_TO_READY:
4425 /* unblock all pads here */
4426 GST_PLAY_SINK_LOCK (playsink);
4427 video_set_blocked (playsink, FALSE);
4428 audio_set_blocked (playsink, FALSE);
4429 text_set_blocked (playsink, FALSE);
4430 GST_PLAY_SINK_UNLOCK (playsink);
4432 case GST_STATE_CHANGE_READY_TO_NULL:
4433 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4434 /* remove our links to the mute and volume elements when they were
4435 * provided by a sink */
4436 disconnect_chain (playsink->audiochain, playsink);
4437 playsink->audiochain->volume = NULL;
4438 playsink->audiochain->mute = NULL;
4441 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4442 gst_object_unref (playsink->audiochain->ts_offset);
4443 playsink->audiochain->ts_offset = NULL;
4446 if (playsink->videochain && playsink->videochain->ts_offset) {
4447 gst_object_unref (playsink->videochain->ts_offset);
4448 playsink->videochain->ts_offset = NULL;
4451 GST_OBJECT_LOCK (playsink);
4452 if (playsink->overlay_element)
4453 gst_object_unref (playsink->overlay_element);
4454 playsink->overlay_element = NULL;
4456 if (playsink->colorbalance_element) {
4457 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4458 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4459 gst_object_unref (playsink->colorbalance_element);
4461 playsink->colorbalance_element = NULL;
4462 GST_OBJECT_UNLOCK (playsink);
4464 ret = GST_STATE_CHANGE_SUCCESS;
4467 /* all other state changes return SUCCESS by default, this value can be
4468 * overridden by the result of the children */
4469 ret = GST_STATE_CHANGE_SUCCESS;
4473 /* do the state change of the children */
4475 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4477 /* now look at the result of our children and adjust the return value */
4479 case GST_STATE_CHANGE_FAILURE:
4480 /* failure, we stop */
4481 goto activate_failed;
4482 case GST_STATE_CHANGE_NO_PREROLL:
4483 /* some child returned NO_PREROLL. This is strange but we never know. We
4484 * commit our async state change (if any) and return the NO_PREROLL */
4485 do_async_done (playsink);
4488 case GST_STATE_CHANGE_ASYNC:
4489 /* some child was async, return this */
4493 /* return our previously configured return value */
4497 switch (transition) {
4498 case GST_STATE_CHANGE_READY_TO_PAUSED:
4500 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4501 /* FIXME Release audio device when we implement that */
4502 playsink->need_async_start = TRUE;
4504 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4505 if (playsink->video_sinkpad_stream_synchronizer) {
4506 gst_element_release_request_pad (GST_ELEMENT_CAST
4507 (playsink->stream_synchronizer),
4508 playsink->video_sinkpad_stream_synchronizer);
4509 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4510 playsink->video_sinkpad_stream_synchronizer = NULL;
4511 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4512 playsink->video_srcpad_stream_synchronizer = NULL;
4514 if (playsink->audio_sinkpad_stream_synchronizer) {
4515 gst_element_release_request_pad (GST_ELEMENT_CAST
4516 (playsink->stream_synchronizer),
4517 playsink->audio_sinkpad_stream_synchronizer);
4518 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4519 playsink->audio_sinkpad_stream_synchronizer = NULL;
4520 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4521 playsink->audio_srcpad_stream_synchronizer = NULL;
4523 if (playsink->text_sinkpad_stream_synchronizer) {
4524 gst_element_release_request_pad (GST_ELEMENT_CAST
4525 (playsink->stream_synchronizer),
4526 playsink->text_sinkpad_stream_synchronizer);
4527 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4528 playsink->text_sinkpad_stream_synchronizer = NULL;
4529 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4530 playsink->text_srcpad_stream_synchronizer = NULL;
4534 case GST_STATE_CHANGE_READY_TO_NULL:
4535 /* remove sinks we added */
4536 if (playsink->videodeinterlacechain) {
4537 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4539 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4541 if (playsink->videochain) {
4542 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4543 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4545 if (playsink->audiochain) {
4546 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4547 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4549 if (playsink->vischain) {
4550 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4551 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4553 if (playsink->textchain) {
4554 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4555 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4557 do_async_done (playsink);
4558 /* when going to READY, keep elements around as long as possible,
4559 * so they may be re-used faster next time/url around.
4560 * when really going to NULL, clean up everything completely. */
4561 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4563 /* Unparent the sinks to allow reuse */
4564 if (playsink->videochain && playsink->videochain->sink)
4565 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4566 playsink->videochain->sink);
4567 if (playsink->audiochain && playsink->audiochain->sink)
4568 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4569 playsink->audiochain->sink);
4570 if (playsink->textchain && playsink->textchain->sink)
4571 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4572 playsink->textchain->sink);
4573 if (playsink->audio_sink != NULL)
4574 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4575 if (playsink->video_sink != NULL)
4576 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4577 if (playsink->visualisation != NULL)
4578 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4579 if (playsink->text_sink != NULL)
4580 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4581 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4582 playsink->videodeinterlacechain = NULL;
4583 free_chain ((GstPlayChain *) playsink->videochain);
4584 playsink->videochain = NULL;
4585 free_chain ((GstPlayChain *) playsink->audiochain);
4586 playsink->audiochain = NULL;
4587 free_chain ((GstPlayChain *) playsink->vischain);
4588 playsink->vischain = NULL;
4589 free_chain ((GstPlayChain *) playsink->textchain);
4590 playsink->textchain = NULL;
4600 GST_DEBUG_OBJECT (element,
4601 "element failed to change states -- activation problem?");
4602 return GST_STATE_CHANGE_FAILURE;
4607 gst_play_sink_set_property (GObject * object, guint prop_id,
4608 const GValue * value, GParamSpec * spec)
4610 GstPlaySink *playsink = GST_PLAY_SINK (object);
4613 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4616 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4619 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4621 case PROP_FONT_DESC:
4622 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4624 case PROP_SUBTITLE_ENCODING:
4625 gst_play_sink_set_subtitle_encoding (playsink,
4626 g_value_get_string (value));
4628 case PROP_VIS_PLUGIN:
4629 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4631 case PROP_AV_OFFSET:
4632 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4634 case PROP_VIDEO_SINK:
4635 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4636 g_value_get_object (value));
4638 case PROP_AUDIO_SINK:
4639 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4640 g_value_get_object (value));
4642 case PROP_TEXT_SINK:
4643 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4644 g_value_get_object (value));
4646 case PROP_SEND_EVENT_MODE:
4647 playsink->send_event_mode = g_value_get_enum (value);
4649 case PROP_FORCE_ASPECT_RATIO:{
4650 GstPlayVideoChain *chain;
4653 playsink->force_aspect_ratio = g_value_get_boolean (value);
4655 GST_PLAY_SINK_LOCK (playsink);
4656 if (playsink->videochain) {
4657 chain = (GstPlayVideoChain *) playsink->videochain;
4661 gst_play_sink_find_property_sinks (playsink, chain->sink,
4662 "force-aspect-ratio", G_TYPE_BOOLEAN);
4665 g_object_set (elem, "force-aspect-ratio",
4666 playsink->force_aspect_ratio, NULL);
4669 GST_PLAY_SINK_UNLOCK (playsink);
4673 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4679 gst_play_sink_get_property (GObject * object, guint prop_id,
4680 GValue * value, GParamSpec * spec)
4682 GstPlaySink *playsink = GST_PLAY_SINK (object);
4685 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4688 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4691 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4693 case PROP_FONT_DESC:
4694 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4696 case PROP_SUBTITLE_ENCODING:
4697 g_value_take_string (value,
4698 gst_play_sink_get_subtitle_encoding (playsink));
4700 case PROP_VIS_PLUGIN:
4701 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4704 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4706 case PROP_AV_OFFSET:
4707 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4709 case PROP_VIDEO_SINK:
4710 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4711 GST_PLAY_SINK_TYPE_VIDEO));
4713 case PROP_AUDIO_SINK:
4714 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4715 GST_PLAY_SINK_TYPE_AUDIO));
4717 case PROP_TEXT_SINK:
4718 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4719 GST_PLAY_SINK_TYPE_TEXT));
4721 case PROP_SEND_EVENT_MODE:
4722 g_value_set_enum (value, playsink->send_event_mode);
4724 case PROP_FORCE_ASPECT_RATIO:
4725 g_value_set_boolean (value, playsink->force_aspect_ratio);
4728 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4734 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4736 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4737 GstVideoOverlay *overlay_element;
4739 GST_OBJECT_LOCK (playsink);
4740 if (playsink->overlay_element)
4742 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4744 overlay_element = NULL;
4745 GST_OBJECT_UNLOCK (playsink);
4747 if (overlay_element) {
4748 gst_video_overlay_expose (overlay_element);
4749 gst_object_unref (overlay_element);
4754 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4755 gboolean handle_events)
4757 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4758 GstVideoOverlay *overlay_element;
4760 GST_OBJECT_LOCK (playsink);
4761 if (playsink->overlay_element)
4763 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4765 overlay_element = NULL;
4766 GST_OBJECT_UNLOCK (playsink);
4768 playsink->overlay_handle_events_set = TRUE;
4769 playsink->overlay_handle_events = handle_events;
4771 if (overlay_element) {
4772 gst_video_overlay_handle_events (overlay_element, handle_events);
4773 gst_object_unref (overlay_element);
4778 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4779 gint y, gint width, gint height)
4781 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4782 GstVideoOverlay *overlay_element;
4784 GST_OBJECT_LOCK (playsink);
4785 if (playsink->overlay_element)
4787 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4789 overlay_element = NULL;
4790 GST_OBJECT_UNLOCK (playsink);
4792 playsink->overlay_render_rectangle_set = TRUE;
4793 playsink->overlay_x = x;
4794 playsink->overlay_y = y;
4795 playsink->overlay_width = width;
4796 playsink->overlay_height = height;
4798 if (overlay_element) {
4799 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4801 gst_object_unref (overlay_element);
4806 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4809 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4810 GstVideoOverlay *overlay_element;
4812 GST_OBJECT_LOCK (playsink);
4813 if (playsink->overlay_element)
4815 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4817 overlay_element = NULL;
4818 GST_OBJECT_UNLOCK (playsink);
4820 playsink->overlay_handle_set = TRUE;
4821 playsink->overlay_handle = handle;
4823 if (overlay_element) {
4824 gst_video_overlay_set_window_handle (overlay_element, handle);
4825 gst_object_unref (overlay_element);
4830 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4832 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4833 iface->expose = gst_play_sink_overlay_expose;
4834 iface->handle_events = gst_play_sink_overlay_handle_events;
4835 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4836 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4840 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4841 GstStructure * structure)
4843 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4846 GST_PLAY_SINK_LOCK (playsink);
4847 if (playsink->videochain && playsink->videochain->chain.bin)
4848 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4849 GST_PLAY_SINK_UNLOCK (playsink);
4852 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4855 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4857 gst_object_unref (nav);
4859 GstEvent *event = gst_event_new_navigation (structure);
4861 gst_element_send_event (GST_ELEMENT (bin), event);
4864 gst_object_unref (bin);
4868 gst_structure_free (structure);
4872 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4874 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4876 iface->send_event = gst_play_sink_navigation_send_event;
4879 static const GList *
4880 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4882 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4884 return playsink->colorbalance_channels;
4888 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4889 GstColorBalanceChannel * proxy, gint value)
4891 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4894 GstColorBalance *balance_element = NULL;
4896 GST_OBJECT_LOCK (playsink);
4897 if (playsink->colorbalance_element)
4899 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4900 GST_OBJECT_UNLOCK (playsink);
4902 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4903 GstColorBalanceChannel *proxy_tmp = l->data;
4906 if (proxy_tmp != proxy)
4909 playsink->colorbalance_values[i] = value;
4911 if (balance_element) {
4912 GstColorBalanceChannel *channel = NULL;
4913 const GList *channels, *k;
4915 channels = gst_color_balance_list_channels (balance_element);
4916 for (k = channels; k; k = k->next) {
4917 GstColorBalanceChannel *tmp = l->data;
4919 if (g_strrstr (tmp->label, proxy->label)) {
4927 /* Convert to [0, 1] range */
4930 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4931 (gdouble) proxy->min_value);
4932 /* Convert to channel range */
4934 channel->min_value + new_val * ((gdouble) channel->max_value -
4935 (gdouble) channel->min_value);
4937 gst_color_balance_set_value (balance_element, channel,
4938 (gint) (new_val + 0.5));
4940 gst_object_unref (balance_element);
4943 gst_color_balance_value_changed (balance, proxy, value);
4949 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4950 GstColorBalanceChannel * proxy)
4952 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4956 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4957 GstColorBalanceChannel *proxy_tmp = l->data;
4959 if (proxy_tmp != proxy)
4962 return playsink->colorbalance_values[i];
4965 g_return_val_if_reached (0);
4968 static GstColorBalanceType
4969 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4971 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4972 GstColorBalance *balance_element = NULL;
4973 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4975 GST_OBJECT_LOCK (playsink);
4976 if (playsink->colorbalance_element)
4978 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4979 GST_OBJECT_UNLOCK (playsink);
4981 if (balance_element) {
4982 t = gst_color_balance_get_balance_type (balance_element);
4983 gst_object_unref (balance_element);
4990 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4992 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4994 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4995 iface->set_value = gst_play_sink_colorbalance_set_value;
4996 iface->get_value = gst_play_sink_colorbalance_get_value;
4997 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
5001 gst_play_sink_plugin_init (GstPlugin * plugin)
5003 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5004 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5005 GST_TYPE_PLAY_SINK);