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 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3119 playsink->videochain->sinkpad);
3120 if (need_deinterlace)
3121 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3122 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3124 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3125 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3128 GST_DEBUG_OBJECT (playsink, "no video needed");
3129 if (playsink->videochain) {
3130 GST_DEBUG_OBJECT (playsink, "removing video chain");
3131 if (playsink->vischain) {
3134 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3136 /* also had visualisation, release the tee srcpad before we then
3137 * unlink the video from it */
3138 if (playsink->audio_tee_vissrc) {
3139 gst_element_release_request_pad (playsink->audio_tee,
3140 playsink->audio_tee_vissrc);
3141 gst_object_unref (playsink->audio_tee_vissrc);
3142 playsink->audio_tee_vissrc = NULL;
3145 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3146 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3149 if (playsink->video_sinkpad_stream_synchronizer) {
3150 gst_element_release_request_pad (GST_ELEMENT_CAST
3151 (playsink->stream_synchronizer),
3152 playsink->video_sinkpad_stream_synchronizer);
3153 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3154 playsink->video_sinkpad_stream_synchronizer = NULL;
3155 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3156 playsink->video_srcpad_stream_synchronizer = NULL;
3159 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3160 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3161 if (playsink->videochain->ts_offset)
3162 gst_object_unref (playsink->videochain->ts_offset);
3163 playsink->videochain->ts_offset = NULL;
3166 if (playsink->videodeinterlacechain) {
3167 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3168 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3171 if (playsink->video_pad)
3172 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3174 GST_OBJECT_LOCK (playsink);
3175 if (playsink->overlay_element)
3176 gst_object_unref (playsink->overlay_element);
3177 playsink->overlay_element = NULL;
3179 if (playsink->colorbalance_element) {
3180 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3181 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3182 gst_object_unref (playsink->colorbalance_element);
3184 playsink->colorbalance_element = NULL;
3185 GST_OBJECT_UNLOCK (playsink);
3192 GST_DEBUG_OBJECT (playsink, "adding audio");
3194 /* get a raw sink if we are asked for a raw pad */
3195 raw = playsink->audio_pad_raw;
3197 if (playsink->audiochain) {
3198 /* try to reactivate the chain */
3199 if (!setup_audio_chain (playsink, raw)) {
3200 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3201 if (playsink->audio_tee_asrc) {
3202 gst_element_release_request_pad (playsink->audio_tee,
3203 playsink->audio_tee_asrc);
3204 gst_object_unref (playsink->audio_tee_asrc);
3205 playsink->audio_tee_asrc = NULL;
3208 if (playsink->audio_sinkpad_stream_synchronizer) {
3209 gst_element_release_request_pad (GST_ELEMENT_CAST
3210 (playsink->stream_synchronizer),
3211 playsink->audio_sinkpad_stream_synchronizer);
3212 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3213 playsink->audio_sinkpad_stream_synchronizer = NULL;
3214 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3215 playsink->audio_srcpad_stream_synchronizer = NULL;
3218 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3220 /* Remove the sink from the bin to keep its state
3221 * and unparent it to allow reuse */
3222 if (playsink->audiochain->sink)
3223 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3224 playsink->audiochain->sink);
3226 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3227 disconnect_chain (playsink->audiochain, playsink);
3228 playsink->audiochain->volume = NULL;
3229 playsink->audiochain->mute = NULL;
3230 if (playsink->audiochain->ts_offset)
3231 gst_object_unref (playsink->audiochain->ts_offset);
3232 playsink->audiochain->ts_offset = NULL;
3233 free_chain ((GstPlayChain *) playsink->audiochain);
3234 playsink->audiochain = NULL;
3235 playsink->volume_changed = playsink->mute_changed = FALSE;
3239 if (!playsink->audiochain) {
3240 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3241 playsink->audiochain = gen_audio_chain (playsink, raw);
3244 if (!playsink->audio_sinkpad_stream_synchronizer) {
3245 GValue item = { 0, };
3248 playsink->audio_sinkpad_stream_synchronizer =
3249 gst_element_get_request_pad (GST_ELEMENT_CAST
3250 (playsink->stream_synchronizer), "sink_%u");
3251 it = gst_pad_iterate_internal_links
3252 (playsink->audio_sinkpad_stream_synchronizer);
3254 gst_iterator_next (it, &item);
3255 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3256 g_value_unset (&item);
3257 g_assert (playsink->audio_srcpad_stream_synchronizer);
3258 gst_iterator_free (it);
3261 if (playsink->audiochain) {
3262 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3263 if (playsink->audio_tee_asrc == NULL) {
3264 playsink->audio_tee_asrc =
3265 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3267 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3268 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3269 gst_pad_link_full (playsink->audio_tee_asrc,
3270 playsink->audio_sinkpad_stream_synchronizer,
3271 GST_PAD_LINK_CHECK_NOTHING);
3272 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3273 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3276 GST_DEBUG_OBJECT (playsink, "no audio needed");
3277 /* we have no audio or we are requested to not play audio */
3278 if (playsink->audiochain) {
3279 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3280 /* release the audio pad */
3281 if (playsink->audio_tee_asrc) {
3282 gst_element_release_request_pad (playsink->audio_tee,
3283 playsink->audio_tee_asrc);
3284 gst_object_unref (playsink->audio_tee_asrc);
3285 playsink->audio_tee_asrc = NULL;
3288 if (playsink->audio_sinkpad_stream_synchronizer) {
3289 gst_element_release_request_pad (GST_ELEMENT_CAST
3290 (playsink->stream_synchronizer),
3291 playsink->audio_sinkpad_stream_synchronizer);
3292 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3293 playsink->audio_sinkpad_stream_synchronizer = NULL;
3294 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3295 playsink->audio_srcpad_stream_synchronizer = NULL;
3298 if (playsink->audiochain->sink_volume) {
3299 disconnect_chain (playsink->audiochain, playsink);
3300 playsink->audiochain->volume = NULL;
3301 playsink->audiochain->mute = NULL;
3302 if (playsink->audiochain->ts_offset)
3303 gst_object_unref (playsink->audiochain->ts_offset);
3304 playsink->audiochain->ts_offset = NULL;
3306 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3307 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3314 if (!playsink->vischain)
3315 playsink->vischain = gen_vis_chain (playsink);
3317 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3319 if (playsink->vischain) {
3320 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3322 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3323 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3324 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3325 if (playsink->audio_tee_vissrc == NULL) {
3326 playsink->audio_tee_vissrc =
3327 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3329 gst_pad_link_full (playsink->audio_tee_vissrc,
3330 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3331 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3332 GST_PAD_LINK_CHECK_NOTHING);
3333 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3334 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3335 gst_object_unref (srcpad);
3338 GST_DEBUG_OBJECT (playsink, "no vis needed");
3339 if (playsink->vischain) {
3340 if (playsink->audio_tee_vissrc) {
3341 gst_element_release_request_pad (playsink->audio_tee,
3342 playsink->audio_tee_vissrc);
3343 gst_object_unref (playsink->audio_tee_vissrc);
3344 playsink->audio_tee_vissrc = NULL;
3346 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3347 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3348 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3353 GST_DEBUG_OBJECT (playsink, "adding text");
3354 if (!playsink->textchain) {
3355 GST_DEBUG_OBJECT (playsink, "creating text chain");
3356 playsink->textchain = gen_text_chain (playsink);
3358 if (playsink->textchain) {
3361 GST_DEBUG_OBJECT (playsink, "adding text chain");
3362 if (playsink->textchain->overlay)
3363 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3365 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3367 if (!playsink->text_sinkpad_stream_synchronizer) {
3368 GValue item = { 0, };
3370 playsink->text_sinkpad_stream_synchronizer =
3371 gst_element_get_request_pad (GST_ELEMENT_CAST
3372 (playsink->stream_synchronizer), "sink_%u");
3373 it = gst_pad_iterate_internal_links
3374 (playsink->text_sinkpad_stream_synchronizer);
3376 gst_iterator_next (it, &item);
3377 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3378 g_value_unset (&item);
3379 g_assert (playsink->text_srcpad_stream_synchronizer);
3380 gst_iterator_free (it);
3382 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3383 playsink->text_sinkpad_stream_synchronizer);
3384 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3385 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3388 if (need_vis || need_video) {
3393 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3394 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3395 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3396 GST_PAD_LINK_CHECK_NOTHING);
3397 gst_object_unref (srcpad);
3399 if (need_deinterlace)
3400 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3401 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3403 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3404 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3406 gst_pad_link_full (playsink->textchain->srcpad,
3407 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3410 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3413 GST_DEBUG_OBJECT (playsink, "no text needed");
3414 /* we have no subtitles/text or we are requested to not show them */
3416 if (playsink->text_sinkpad_stream_synchronizer) {
3417 gst_element_release_request_pad (GST_ELEMENT_CAST
3418 (playsink->stream_synchronizer),
3419 playsink->text_sinkpad_stream_synchronizer);
3420 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3421 playsink->text_sinkpad_stream_synchronizer = NULL;
3422 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3423 playsink->text_srcpad_stream_synchronizer = NULL;
3426 if (playsink->textchain) {
3427 if (playsink->text_pad == NULL) {
3428 /* no text pad, remove the chain entirely */
3429 GST_DEBUG_OBJECT (playsink, "removing text chain");
3430 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3431 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3433 /* we have a chain and a textpad, turn the subtitles off */
3434 GST_DEBUG_OBJECT (playsink, "turning off the text");
3435 if (playsink->textchain->overlay)
3436 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3440 if (!need_video && playsink->video_pad) {
3441 if (playsink->video_sinkpad_stream_synchronizer) {
3442 gst_element_release_request_pad (GST_ELEMENT_CAST
3443 (playsink->stream_synchronizer),
3444 playsink->video_sinkpad_stream_synchronizer);
3445 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3446 playsink->video_sinkpad_stream_synchronizer = NULL;
3447 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3448 playsink->video_srcpad_stream_synchronizer = NULL;
3451 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3454 if (playsink->text_pad && !playsink->textchain)
3455 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3457 update_av_offset (playsink);
3458 do_async_done (playsink);
3459 GST_PLAY_SINK_UNLOCK (playsink);
3466 /* gen_ chain already posted error */
3467 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3468 GST_PLAY_SINK_UNLOCK (playsink);
3474 * gst_play_sink_set_flags:
3475 * @playsink: a #GstPlaySink
3476 * @flags: #GstPlayFlags
3478 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3479 * when constructing the sink pipelins.
3481 * Returns: TRUE if the flags could be configured.
3484 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3486 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3488 GST_OBJECT_LOCK (playsink);
3489 playsink->flags = flags;
3490 GST_OBJECT_UNLOCK (playsink);
3496 * gst_play_sink_get_flags:
3497 * @playsink: a #GstPlaySink
3499 * Get the flags of @playsink. That flags control the behaviour of the sink when
3500 * it constructs the sink pipelines.
3502 * Returns: the currently configured #GstPlayFlags.
3505 gst_play_sink_get_flags (GstPlaySink * playsink)
3509 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3511 GST_OBJECT_LOCK (playsink);
3512 res = playsink->flags;
3513 GST_OBJECT_UNLOCK (playsink);
3519 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3521 GstPlayTextChain *chain;
3523 GST_PLAY_SINK_LOCK (playsink);
3524 chain = (GstPlayTextChain *) playsink->textchain;
3525 g_free (playsink->font_desc);
3526 playsink->font_desc = g_strdup (desc);
3527 if (chain && chain->overlay) {
3528 g_object_set (chain->overlay, "font-desc", desc, NULL);
3530 GST_PLAY_SINK_UNLOCK (playsink);
3534 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3536 gchar *result = NULL;
3537 GstPlayTextChain *chain;
3539 GST_PLAY_SINK_LOCK (playsink);
3540 chain = (GstPlayTextChain *) playsink->textchain;
3541 if (chain && chain->overlay) {
3542 g_object_get (chain->overlay, "font-desc", &result, NULL);
3543 playsink->font_desc = g_strdup (result);
3545 result = g_strdup (playsink->font_desc);
3547 GST_PLAY_SINK_UNLOCK (playsink);
3553 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3554 const gchar * encoding)
3556 GstPlayTextChain *chain;
3558 GST_PLAY_SINK_LOCK (playsink);
3559 chain = (GstPlayTextChain *) playsink->textchain;
3560 g_free (playsink->subtitle_encoding);
3561 playsink->subtitle_encoding = g_strdup (encoding);
3562 if (chain && chain->overlay) {
3563 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3565 GST_PLAY_SINK_UNLOCK (playsink);
3569 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3571 gchar *result = NULL;
3572 GstPlayTextChain *chain;
3574 GST_PLAY_SINK_LOCK (playsink);
3575 chain = (GstPlayTextChain *) playsink->textchain;
3576 if (chain && chain->overlay) {
3577 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3578 playsink->subtitle_encoding = g_strdup (result);
3580 result = g_strdup (playsink->subtitle_encoding);
3582 GST_PLAY_SINK_UNLOCK (playsink);
3588 update_av_offset (GstPlaySink * playsink)
3591 GstPlayAudioChain *achain;
3592 GstPlayVideoChain *vchain;
3594 av_offset = playsink->av_offset;
3595 achain = (GstPlayAudioChain *) playsink->audiochain;
3596 vchain = (GstPlayVideoChain *) playsink->videochain;
3598 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3599 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3600 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3602 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3607 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3609 GST_PLAY_SINK_LOCK (playsink);
3610 playsink->av_offset = av_offset;
3611 update_av_offset (playsink);
3612 GST_PLAY_SINK_UNLOCK (playsink);
3616 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3620 GST_PLAY_SINK_LOCK (playsink);
3621 result = playsink->av_offset;
3622 GST_PLAY_SINK_UNLOCK (playsink);
3628 * gst_play_sink_get_last_sample:
3629 * @playsink: a #GstPlaySink
3631 * Get the last displayed sample from @playsink. This sample is in the native
3632 * format of the sink element, the caps in the result sample contain the format
3633 * of the frame data.
3635 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3639 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3641 GstSample *result = NULL;
3642 GstPlayVideoChain *chain;
3644 GST_PLAY_SINK_LOCK (playsink);
3645 GST_DEBUG_OBJECT (playsink, "taking last sample");
3646 /* get the video chain if we can */
3647 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3648 GST_DEBUG_OBJECT (playsink, "found video chain");
3649 /* see if the chain is active */
3650 if (chain->chain.activated && chain->sink) {
3653 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3655 /* find and get the last-buffer property now */
3657 gst_play_sink_find_property (playsink, chain->sink,
3658 "last-sample", GST_TYPE_SAMPLE))) {
3659 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3660 g_object_get (elem, "last-sample", &result, NULL);
3661 gst_object_unref (elem);
3665 GST_PLAY_SINK_UNLOCK (playsink);
3671 * gst_play_sink_convert_sample:
3672 * @playsink: a #GstPlaySink
3675 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3676 * be in the native format of the sink element and the caps on the buffer
3677 * describe the format of the frame. If @caps is not %NULL, the video
3678 * frame will be converted to the format of the caps.
3680 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3681 * available or when the conversion failed.
3684 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3689 result = gst_play_sink_get_last_sample (playsink);
3690 if (result != NULL && caps != NULL) {
3693 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3694 if (temp == NULL && err)
3697 gst_sample_unref (result);
3705 /* I'm really uncertain whether we should make playsink post an error
3706 * on the bus or not. It's not like it's a critical issue regarding
3707 * playsink behaviour. */
3708 GST_ERROR ("Error converting frame: %s", err->message);
3709 gst_sample_unref (result);
3716 is_raw_structure (GstStructure * s)
3720 name = gst_structure_get_name (s);
3722 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3728 is_raw_pad (GstPad * pad)
3730 GstPad *peer = gst_pad_get_peer (pad);
3732 gboolean raw = TRUE;
3737 caps = gst_pad_get_current_caps (peer);
3741 caps = gst_pad_query_caps (peer, NULL);
3743 n = gst_caps_get_size (caps);
3744 for (i = 0; i < n; i++) {
3745 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3749 } else if (raw != r) {
3750 GST_ERROR_OBJECT (pad,
3751 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3757 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3759 gst_caps_unref (caps);
3760 gst_object_unref (peer);
3765 static GstPadProbeReturn
3766 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3767 gpointer user_data);
3770 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3772 if (playsink->video_pad) {
3774 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3775 (playsink->video_pad)));
3776 if (blocked && playsink->video_block_id == 0) {
3777 playsink->video_block_id =
3778 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3779 sinkpad_blocked_cb, playsink, NULL);
3780 } else if (!blocked && playsink->video_block_id) {
3781 gst_pad_remove_probe (opad, playsink->video_block_id);
3782 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3783 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3784 playsink->video_block_id = 0;
3785 playsink->video_pad_blocked = FALSE;
3787 gst_object_unref (opad);
3792 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3794 if (playsink->audio_pad) {
3796 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3797 (playsink->audio_pad)));
3798 if (blocked && playsink->audio_block_id == 0) {
3799 playsink->audio_block_id =
3800 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3801 sinkpad_blocked_cb, playsink, NULL);
3802 } else if (!blocked && playsink->audio_block_id) {
3803 gst_pad_remove_probe (opad, playsink->audio_block_id);
3804 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3805 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3806 playsink->audio_block_id = 0;
3807 playsink->audio_pad_blocked = FALSE;
3809 gst_object_unref (opad);
3814 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3816 if (playsink->text_pad) {
3818 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3819 (playsink->text_pad)));
3820 if (blocked && playsink->text_block_id == 0) {
3821 playsink->text_block_id =
3822 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3823 sinkpad_blocked_cb, playsink, NULL);
3824 } else if (!blocked && playsink->text_block_id) {
3825 gst_pad_remove_probe (opad, playsink->text_block_id);
3826 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3827 playsink->text_block_id = 0;
3828 playsink->text_pad_blocked = FALSE;
3830 gst_object_unref (opad);
3834 static GstPadProbeReturn
3835 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3838 GstPlaySink *playsink = (GstPlaySink *) user_data;
3841 GST_PLAY_SINK_LOCK (playsink);
3843 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3844 if (pad == playsink->video_pad) {
3845 playsink->video_pad_blocked = TRUE;
3846 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3847 } else if (pad == playsink->audio_pad) {
3848 playsink->audio_pad_blocked = TRUE;
3849 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3850 } else if (pad == playsink->text_pad) {
3851 playsink->text_pad_blocked = TRUE;
3852 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3855 /* We reconfigure when for ALL streams:
3856 * * there isn't a pad
3857 * * OR the pad is blocked
3858 * * OR there are no pending blocks on that pad
3861 if ((!playsink->video_pad || playsink->video_pad_blocked
3862 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3863 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3864 && (!playsink->text_pad || playsink->text_pad_blocked
3865 || !PENDING_TEXT_BLOCK (playsink))) {
3866 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3868 if (playsink->video_pad) {
3869 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3870 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3871 playsink->video_pad_raw);
3874 if (playsink->audio_pad) {
3875 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3876 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3877 playsink->audio_pad_raw);
3880 gst_play_sink_reconfigure (playsink);
3882 video_set_blocked (playsink, FALSE);
3883 audio_set_blocked (playsink, FALSE);
3884 text_set_blocked (playsink, FALSE);
3887 gst_object_unref (pad);
3889 GST_PLAY_SINK_UNLOCK (playsink);
3891 return GST_PAD_PROBE_OK;
3895 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3897 gboolean reconfigure = FALSE;
3901 g_object_get (pad, "caps", &caps, NULL);
3905 if (pad == playsink->audio_pad) {
3906 raw = is_raw_pad (pad);
3907 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3908 && playsink->audiochain;
3909 GST_DEBUG_OBJECT (pad,
3910 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3912 } else if (pad == playsink->video_pad) {
3913 raw = is_raw_pad (pad);
3914 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3915 && playsink->videochain;
3916 GST_DEBUG_OBJECT (pad,
3917 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3921 gst_caps_unref (caps);
3924 GST_PLAY_SINK_LOCK (playsink);
3925 video_set_blocked (playsink, TRUE);
3926 audio_set_blocked (playsink, TRUE);
3927 text_set_blocked (playsink, TRUE);
3928 GST_PLAY_SINK_UNLOCK (playsink);
3933 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3934 GstPlaySinkType type)
3936 gulong *block_id = NULL;
3938 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3940 GST_PLAY_SINK_LOCK (playsink);
3941 if (pad == playsink->video_pad) {
3942 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3943 type != GST_PLAY_SINK_TYPE_VIDEO)
3945 block_id = &playsink->video_block_id;
3946 } else if (pad == playsink->audio_pad) {
3947 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3948 type != GST_PLAY_SINK_TYPE_AUDIO)
3950 block_id = &playsink->audio_block_id;
3951 } else if (pad == playsink->text_pad) {
3952 if (type != GST_PLAY_SINK_TYPE_TEXT)
3954 block_id = &playsink->text_block_id;
3957 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3959 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3962 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3963 sinkpad_blocked_cb, playsink, NULL);
3964 PENDING_FLAG_SET (playsink, type);
3965 gst_object_unref (blockpad);
3967 GST_PLAY_SINK_UNLOCK (playsink);
3974 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3976 GST_PLAY_SINK_UNLOCK (playsink);
3982 * gst_play_sink_request_pad
3983 * @playsink: a #GstPlaySink
3984 * @type: a #GstPlaySinkType
3986 * Create or return a pad of @type.
3988 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3991 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3994 gboolean created = FALSE;
3995 gboolean activate = TRUE;
3996 const gchar *pad_name = NULL;
3997 gulong *block_id = NULL;
3999 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4001 GST_PLAY_SINK_LOCK (playsink);
4003 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4004 case GST_PLAY_SINK_TYPE_AUDIO:
4005 pad_name = "audio_sink";
4006 if (!playsink->audio_tee) {
4007 GST_LOG_OBJECT (playsink, "creating tee");
4008 /* create tee when needed. This element will feed the audio sink chain
4009 * and the vis chain. */
4010 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4011 if (playsink->audio_tee == NULL) {
4012 post_missing_element_message (playsink, "tee");
4013 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4014 (_("Missing element '%s' - check your GStreamer installation."),
4019 playsink->audio_tee_sink =
4020 gst_element_get_static_pad (playsink->audio_tee, "sink");
4021 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4022 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4025 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4027 if (!playsink->audio_pad) {
4028 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4029 playsink->audio_pad =
4030 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4031 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4032 G_CALLBACK (caps_notify_cb), playsink);
4035 playsink->audio_pad_raw = FALSE;
4036 res = playsink->audio_pad;
4037 block_id = &playsink->audio_block_id;
4039 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4040 case GST_PLAY_SINK_TYPE_VIDEO:
4041 pad_name = "video_sink";
4042 if (!playsink->video_pad) {
4043 GST_LOG_OBJECT (playsink, "ghosting videosink");
4044 playsink->video_pad =
4045 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4046 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4047 G_CALLBACK (caps_notify_cb), playsink);
4050 playsink->video_pad_raw = FALSE;
4051 res = playsink->video_pad;
4052 block_id = &playsink->video_block_id;
4054 case GST_PLAY_SINK_TYPE_TEXT:
4055 GST_LOG_OBJECT (playsink, "ghosting text");
4056 if (!playsink->text_pad) {
4057 playsink->text_pad =
4058 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4061 res = playsink->text_pad;
4062 block_id = &playsink->text_block_id;
4064 case GST_PLAY_SINK_TYPE_FLUSHING:
4068 /* we need a unique padname for the flushing pad. */
4069 padname = g_strdup_printf ("flushing_%u", playsink->count);
4070 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4081 GST_PLAY_SINK_UNLOCK (playsink);
4083 if (created && res) {
4084 /* we have to add the pad when it's active or we get an error when the
4085 * element is 'running' */
4086 gst_pad_set_active (res, TRUE);
4087 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4088 if (block_id && *block_id == 0) {
4090 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4093 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4094 sinkpad_blocked_cb, playsink, NULL);
4095 PENDING_FLAG_SET (playsink, type);
4096 gst_object_unref (blockpad);
4099 gst_pad_set_active (res, activate);
4107 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4108 const gchar * name, const GstCaps * caps)
4112 GstPlaySinkType type;
4113 const gchar *tplname;
4115 g_return_val_if_fail (templ != NULL, NULL);
4117 GST_DEBUG_OBJECT (element, "name:%s", name);
4119 psink = GST_PLAY_SINK (element);
4120 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4122 /* Figure out the GstPlaySinkType based on the template */
4123 if (!strcmp (tplname, "audio_sink"))
4124 type = GST_PLAY_SINK_TYPE_AUDIO;
4125 else if (!strcmp (tplname, "audio_raw_sink"))
4126 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4127 else if (!strcmp (tplname, "video_sink"))
4128 type = GST_PLAY_SINK_TYPE_VIDEO;
4129 else if (!strcmp (tplname, "video_raw_sink"))
4130 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4131 else if (!strcmp (tplname, "text_sink"))
4132 type = GST_PLAY_SINK_TYPE_TEXT;
4134 goto unknown_template;
4136 pad = gst_play_sink_request_pad (psink, type);
4140 GST_WARNING_OBJECT (element, "Unknown pad template");
4145 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4147 GstPad **res = NULL;
4148 gboolean untarget = TRUE;
4150 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4152 GST_PLAY_SINK_LOCK (playsink);
4153 if (pad == playsink->video_pad) {
4154 res = &playsink->video_pad;
4155 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4157 } else if (pad == playsink->audio_pad) {
4158 res = &playsink->audio_pad;
4159 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4161 } else if (pad == playsink->text_pad) {
4162 res = &playsink->text_pad;
4164 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4168 GST_PLAY_SINK_UNLOCK (playsink);
4171 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4172 gst_pad_set_active (*res, FALSE);
4174 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4175 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4177 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4178 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4184 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4186 GstPlaySink *psink = GST_PLAY_SINK (element);
4188 gst_play_sink_release_pad (psink, pad);
4192 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4194 GstPlaySink *playsink;
4196 playsink = GST_PLAY_SINK_CAST (bin);
4198 switch (GST_MESSAGE_TYPE (message)) {
4199 case GST_MESSAGE_STEP_DONE:
4204 gboolean flush, intermediate, eos;
4207 GST_INFO_OBJECT (playsink, "Handling step-done message");
4208 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4209 &intermediate, &duration, &eos);
4211 if (format == GST_FORMAT_BUFFERS) {
4212 /* for the buffer format, we align the other streams */
4213 if (playsink->audiochain) {
4217 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4220 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4221 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4225 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4228 case GST_MESSAGE_ELEMENT:{
4229 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4230 GstVideoOverlay *overlay;
4232 GST_OBJECT_LOCK (playsink);
4233 if (playsink->overlay_element
4234 && GST_OBJECT_CAST (playsink->overlay_element) !=
4235 GST_MESSAGE_SRC (message)) {
4236 gst_object_unref (playsink->overlay_element);
4237 playsink->overlay_element = NULL;
4240 if (!playsink->overlay_element)
4241 playsink->overlay_element =
4242 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4244 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4245 GST_OBJECT_UNLOCK (playsink);
4247 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4249 if (playsink->overlay_handle_set)
4250 gst_video_overlay_set_window_handle (playsink->overlay_element,
4251 playsink->overlay_handle);
4252 if (playsink->overlay_handle_events_set)
4253 gst_video_overlay_handle_events (playsink->overlay_element,
4254 playsink->overlay_handle_events);
4255 if (playsink->overlay_render_rectangle_set)
4256 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4257 playsink->overlay_x, playsink->overlay_y,
4258 playsink->overlay_width, playsink->overlay_height);
4260 gst_object_unref (overlay);
4261 gst_message_unref (message);
4262 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4264 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4270 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4275 /* Send an event to our sinks until one of them works; don't then send to the
4276 * remaining sinks (unlike GstBin)
4277 * Special case: If a text sink is set we need to send the event
4278 * to them in case it's source is different from the a/v stream's source.
4281 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4283 gboolean res = TRUE;
4284 if (playsink->send_event_mode == MODE_FIRST) {
4285 if (playsink->textchain && playsink->textchain->sink) {
4286 gst_event_ref (event);
4288 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4289 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4291 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4295 if (playsink->videochain) {
4296 gst_event_ref (event);
4298 gst_element_send_event (playsink->videochain->chain.bin,
4300 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4303 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4305 if (playsink->audiochain) {
4306 gst_event_ref (event);
4308 gst_element_send_event (playsink->audiochain->chain.bin,
4310 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4313 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4317 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4318 (GST_ELEMENT_CAST (playsink), event);
4322 gst_event_unref (event);
4326 /* We only want to send the event to a single sink (overriding GstBin's
4327 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4328 * events appropriately. So, this is a messy duplication of code. */
4330 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4332 gboolean res = FALSE;
4333 GstEventType event_type = GST_EVENT_TYPE (event);
4334 GstPlaySink *playsink;
4335 playsink = GST_PLAY_SINK_CAST (element);
4336 switch (event_type) {
4337 case GST_EVENT_SEEK:
4338 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4339 res = gst_play_sink_send_event_to_sink (playsink, event);
4341 case GST_EVENT_STEP:
4346 gboolean flush, intermediate;
4347 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4349 if (format == GST_FORMAT_BUFFERS) {
4350 /* for buffers, we will try to step video frames, for other formats we
4351 * send the step to all sinks */
4352 res = gst_play_sink_send_event_to_sink (playsink, event);
4355 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4362 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4369 static GstStateChangeReturn
4370 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4372 GstStateChangeReturn ret;
4373 GstStateChangeReturn bret;
4374 GstPlaySink *playsink;
4375 playsink = GST_PLAY_SINK (element);
4376 switch (transition) {
4377 case GST_STATE_CHANGE_READY_TO_PAUSED:
4378 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4380 playsink->need_async_start = TRUE;
4381 /* we want to go async to PAUSED until we managed to configure and add the
4383 do_async_start (playsink);
4384 ret = GST_STATE_CHANGE_ASYNC;
4386 /* block all pads here */
4387 GST_PLAY_SINK_LOCK (playsink);
4388 if (playsink->video_pad && playsink->video_block_id == 0) {
4390 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4391 (playsink->video_pad)));
4392 playsink->video_block_id =
4393 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4394 sinkpad_blocked_cb, playsink, NULL);
4395 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4396 gst_object_unref (opad);
4399 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4401 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4402 (playsink->audio_pad)));
4403 playsink->audio_block_id =
4404 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4405 sinkpad_blocked_cb, playsink, NULL);
4406 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4407 gst_object_unref (opad);
4410 if (playsink->text_pad && playsink->text_block_id == 0) {
4412 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4413 (playsink->text_pad)));
4414 playsink->text_block_id =
4415 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4416 sinkpad_blocked_cb, playsink, NULL);
4417 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4418 gst_object_unref (opad);
4420 GST_PLAY_SINK_UNLOCK (playsink);
4422 case GST_STATE_CHANGE_PAUSED_TO_READY:
4423 /* unblock all pads here */
4424 GST_PLAY_SINK_LOCK (playsink);
4425 video_set_blocked (playsink, FALSE);
4426 audio_set_blocked (playsink, FALSE);
4427 text_set_blocked (playsink, FALSE);
4428 GST_PLAY_SINK_UNLOCK (playsink);
4430 case GST_STATE_CHANGE_READY_TO_NULL:
4431 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4432 /* remove our links to the mute and volume elements when they were
4433 * provided by a sink */
4434 disconnect_chain (playsink->audiochain, playsink);
4435 playsink->audiochain->volume = NULL;
4436 playsink->audiochain->mute = NULL;
4439 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4440 gst_object_unref (playsink->audiochain->ts_offset);
4441 playsink->audiochain->ts_offset = NULL;
4444 if (playsink->videochain && playsink->videochain->ts_offset) {
4445 gst_object_unref (playsink->videochain->ts_offset);
4446 playsink->videochain->ts_offset = NULL;
4449 GST_OBJECT_LOCK (playsink);
4450 if (playsink->overlay_element)
4451 gst_object_unref (playsink->overlay_element);
4452 playsink->overlay_element = NULL;
4454 if (playsink->colorbalance_element) {
4455 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4456 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4457 gst_object_unref (playsink->colorbalance_element);
4459 playsink->colorbalance_element = NULL;
4460 GST_OBJECT_UNLOCK (playsink);
4462 ret = GST_STATE_CHANGE_SUCCESS;
4465 /* all other state changes return SUCCESS by default, this value can be
4466 * overridden by the result of the children */
4467 ret = GST_STATE_CHANGE_SUCCESS;
4471 /* do the state change of the children */
4473 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4475 /* now look at the result of our children and adjust the return value */
4477 case GST_STATE_CHANGE_FAILURE:
4478 /* failure, we stop */
4479 goto activate_failed;
4480 case GST_STATE_CHANGE_NO_PREROLL:
4481 /* some child returned NO_PREROLL. This is strange but we never know. We
4482 * commit our async state change (if any) and return the NO_PREROLL */
4483 do_async_done (playsink);
4486 case GST_STATE_CHANGE_ASYNC:
4487 /* some child was async, return this */
4491 /* return our previously configured return value */
4495 switch (transition) {
4496 case GST_STATE_CHANGE_READY_TO_PAUSED:
4498 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4499 /* FIXME Release audio device when we implement that */
4500 playsink->need_async_start = TRUE;
4502 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4503 if (playsink->video_sinkpad_stream_synchronizer) {
4504 gst_element_release_request_pad (GST_ELEMENT_CAST
4505 (playsink->stream_synchronizer),
4506 playsink->video_sinkpad_stream_synchronizer);
4507 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4508 playsink->video_sinkpad_stream_synchronizer = NULL;
4509 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4510 playsink->video_srcpad_stream_synchronizer = NULL;
4512 if (playsink->audio_sinkpad_stream_synchronizer) {
4513 gst_element_release_request_pad (GST_ELEMENT_CAST
4514 (playsink->stream_synchronizer),
4515 playsink->audio_sinkpad_stream_synchronizer);
4516 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4517 playsink->audio_sinkpad_stream_synchronizer = NULL;
4518 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4519 playsink->audio_srcpad_stream_synchronizer = NULL;
4521 if (playsink->text_sinkpad_stream_synchronizer) {
4522 gst_element_release_request_pad (GST_ELEMENT_CAST
4523 (playsink->stream_synchronizer),
4524 playsink->text_sinkpad_stream_synchronizer);
4525 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4526 playsink->text_sinkpad_stream_synchronizer = NULL;
4527 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4528 playsink->text_srcpad_stream_synchronizer = NULL;
4532 case GST_STATE_CHANGE_READY_TO_NULL:
4533 /* remove sinks we added */
4534 if (playsink->videodeinterlacechain) {
4535 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4537 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4539 if (playsink->videochain) {
4540 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4541 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4543 if (playsink->audiochain) {
4544 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4545 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4547 if (playsink->vischain) {
4548 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4549 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4551 if (playsink->textchain) {
4552 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4553 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4555 do_async_done (playsink);
4556 /* when going to READY, keep elements around as long as possible,
4557 * so they may be re-used faster next time/url around.
4558 * when really going to NULL, clean up everything completely. */
4559 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4561 /* Unparent the sinks to allow reuse */
4562 if (playsink->videochain && playsink->videochain->sink)
4563 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4564 playsink->videochain->sink);
4565 if (playsink->audiochain && playsink->audiochain->sink)
4566 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4567 playsink->audiochain->sink);
4568 if (playsink->textchain && playsink->textchain->sink)
4569 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4570 playsink->textchain->sink);
4571 if (playsink->audio_sink != NULL)
4572 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4573 if (playsink->video_sink != NULL)
4574 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4575 if (playsink->visualisation != NULL)
4576 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4577 if (playsink->text_sink != NULL)
4578 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4579 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4580 playsink->videodeinterlacechain = NULL;
4581 free_chain ((GstPlayChain *) playsink->videochain);
4582 playsink->videochain = NULL;
4583 free_chain ((GstPlayChain *) playsink->audiochain);
4584 playsink->audiochain = NULL;
4585 free_chain ((GstPlayChain *) playsink->vischain);
4586 playsink->vischain = NULL;
4587 free_chain ((GstPlayChain *) playsink->textchain);
4588 playsink->textchain = NULL;
4598 GST_DEBUG_OBJECT (element,
4599 "element failed to change states -- activation problem?");
4600 return GST_STATE_CHANGE_FAILURE;
4605 gst_play_sink_set_property (GObject * object, guint prop_id,
4606 const GValue * value, GParamSpec * spec)
4608 GstPlaySink *playsink = GST_PLAY_SINK (object);
4611 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4614 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4617 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4619 case PROP_FONT_DESC:
4620 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4622 case PROP_SUBTITLE_ENCODING:
4623 gst_play_sink_set_subtitle_encoding (playsink,
4624 g_value_get_string (value));
4626 case PROP_VIS_PLUGIN:
4627 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4629 case PROP_AV_OFFSET:
4630 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4632 case PROP_VIDEO_SINK:
4633 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4634 g_value_get_object (value));
4636 case PROP_AUDIO_SINK:
4637 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4638 g_value_get_object (value));
4640 case PROP_TEXT_SINK:
4641 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4642 g_value_get_object (value));
4644 case PROP_SEND_EVENT_MODE:
4645 playsink->send_event_mode = g_value_get_enum (value);
4647 case PROP_FORCE_ASPECT_RATIO:{
4648 GstPlayVideoChain *chain;
4651 playsink->force_aspect_ratio = g_value_get_boolean (value);
4653 GST_PLAY_SINK_LOCK (playsink);
4654 if (playsink->videochain) {
4655 chain = (GstPlayVideoChain *) playsink->videochain;
4659 gst_play_sink_find_property_sinks (playsink, chain->sink,
4660 "force-aspect-ratio", G_TYPE_BOOLEAN);
4663 g_object_set (elem, "force-aspect-ratio",
4664 playsink->force_aspect_ratio, NULL);
4667 GST_PLAY_SINK_UNLOCK (playsink);
4671 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4677 gst_play_sink_get_property (GObject * object, guint prop_id,
4678 GValue * value, GParamSpec * spec)
4680 GstPlaySink *playsink = GST_PLAY_SINK (object);
4683 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4686 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4689 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4691 case PROP_FONT_DESC:
4692 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4694 case PROP_SUBTITLE_ENCODING:
4695 g_value_take_string (value,
4696 gst_play_sink_get_subtitle_encoding (playsink));
4698 case PROP_VIS_PLUGIN:
4699 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4702 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4704 case PROP_AV_OFFSET:
4705 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4707 case PROP_VIDEO_SINK:
4708 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4709 GST_PLAY_SINK_TYPE_VIDEO));
4711 case PROP_AUDIO_SINK:
4712 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4713 GST_PLAY_SINK_TYPE_AUDIO));
4715 case PROP_TEXT_SINK:
4716 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4717 GST_PLAY_SINK_TYPE_TEXT));
4719 case PROP_SEND_EVENT_MODE:
4720 g_value_set_enum (value, playsink->send_event_mode);
4722 case PROP_FORCE_ASPECT_RATIO:
4723 g_value_set_boolean (value, playsink->force_aspect_ratio);
4726 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4732 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4734 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4735 GstVideoOverlay *overlay_element;
4737 GST_OBJECT_LOCK (playsink);
4738 if (playsink->overlay_element)
4740 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4742 overlay_element = NULL;
4743 GST_OBJECT_UNLOCK (playsink);
4745 if (overlay_element) {
4746 gst_video_overlay_expose (overlay_element);
4747 gst_object_unref (overlay_element);
4752 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4753 gboolean handle_events)
4755 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4756 GstVideoOverlay *overlay_element;
4758 GST_OBJECT_LOCK (playsink);
4759 if (playsink->overlay_element)
4761 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4763 overlay_element = NULL;
4764 GST_OBJECT_UNLOCK (playsink);
4766 playsink->overlay_handle_events_set = TRUE;
4767 playsink->overlay_handle_events = handle_events;
4769 if (overlay_element) {
4770 gst_video_overlay_handle_events (overlay_element, handle_events);
4771 gst_object_unref (overlay_element);
4776 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4777 gint y, gint width, gint height)
4779 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4780 GstVideoOverlay *overlay_element;
4782 GST_OBJECT_LOCK (playsink);
4783 if (playsink->overlay_element)
4785 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4787 overlay_element = NULL;
4788 GST_OBJECT_UNLOCK (playsink);
4790 playsink->overlay_render_rectangle_set = TRUE;
4791 playsink->overlay_x = x;
4792 playsink->overlay_y = y;
4793 playsink->overlay_width = width;
4794 playsink->overlay_height = height;
4796 if (overlay_element) {
4797 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4799 gst_object_unref (overlay_element);
4804 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4807 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4808 GstVideoOverlay *overlay_element;
4810 GST_OBJECT_LOCK (playsink);
4811 if (playsink->overlay_element)
4813 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4815 overlay_element = NULL;
4816 GST_OBJECT_UNLOCK (playsink);
4818 playsink->overlay_handle_set = TRUE;
4819 playsink->overlay_handle = handle;
4821 if (overlay_element) {
4822 gst_video_overlay_set_window_handle (overlay_element, handle);
4823 gst_object_unref (overlay_element);
4828 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4830 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4831 iface->expose = gst_play_sink_overlay_expose;
4832 iface->handle_events = gst_play_sink_overlay_handle_events;
4833 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4834 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4838 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4839 GstStructure * structure)
4841 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4844 GST_PLAY_SINK_LOCK (playsink);
4845 if (playsink->videochain && playsink->videochain->chain.bin)
4846 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4847 GST_PLAY_SINK_UNLOCK (playsink);
4850 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4853 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4855 gst_object_unref (nav);
4857 GstEvent *event = gst_event_new_navigation (structure);
4859 gst_element_send_event (GST_ELEMENT (bin), event);
4862 gst_object_unref (bin);
4866 gst_structure_free (structure);
4870 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4872 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4874 iface->send_event = gst_play_sink_navigation_send_event;
4877 static const GList *
4878 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4880 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4882 return playsink->colorbalance_channels;
4886 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4887 GstColorBalanceChannel * proxy, gint value)
4889 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4892 GstColorBalance *balance_element = NULL;
4894 GST_OBJECT_LOCK (playsink);
4895 if (playsink->colorbalance_element)
4897 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4898 GST_OBJECT_UNLOCK (playsink);
4900 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4901 GstColorBalanceChannel *proxy_tmp = l->data;
4904 if (proxy_tmp != proxy)
4907 playsink->colorbalance_values[i] = value;
4909 if (balance_element) {
4910 GstColorBalanceChannel *channel = NULL;
4911 const GList *channels, *k;
4913 channels = gst_color_balance_list_channels (balance_element);
4914 for (k = channels; k; k = k->next) {
4915 GstColorBalanceChannel *tmp = l->data;
4917 if (g_strrstr (tmp->label, proxy->label)) {
4925 /* Convert to [0, 1] range */
4928 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4929 (gdouble) proxy->min_value);
4930 /* Convert to channel range */
4932 channel->min_value + new_val * ((gdouble) channel->max_value -
4933 (gdouble) channel->min_value);
4935 gst_color_balance_set_value (balance_element, channel,
4936 (gint) (new_val + 0.5));
4938 gst_object_unref (balance_element);
4941 gst_color_balance_value_changed (balance, proxy, value);
4947 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4948 GstColorBalanceChannel * proxy)
4950 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4954 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4955 GstColorBalanceChannel *proxy_tmp = l->data;
4957 if (proxy_tmp != proxy)
4960 return playsink->colorbalance_values[i];
4963 g_return_val_if_reached (0);
4966 static GstColorBalanceType
4967 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4969 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4970 GstColorBalance *balance_element = NULL;
4971 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4973 GST_OBJECT_LOCK (playsink);
4974 if (playsink->colorbalance_element)
4976 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4977 GST_OBJECT_UNLOCK (playsink);
4979 if (balance_element) {
4980 t = gst_color_balance_get_balance_type (balance_element);
4981 gst_object_unref (balance_element);
4988 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4990 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4992 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4993 iface->set_value = gst_play_sink_colorbalance_set_value;
4994 iface->get_value = gst_play_sink_colorbalance_get_value;
4995 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4999 gst_play_sink_plugin_init (GstPlugin * plugin)
5001 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
5002 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
5003 GST_TYPE_PLAY_SINK);