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 GST_OBJECT_UNLOCK (playsink);
1699 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1700 || (!playsink->colorbalance_element
1701 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1702 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1703 gboolean use_balance = !playsink->colorbalance_element
1704 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1706 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1708 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1709 "use-converters", use_converters, "use-balance", use_balance, NULL);
1711 GST_OBJECT_LOCK (playsink);
1712 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1713 playsink->colorbalance_element =
1714 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1715 (chain->conv)->balance));
1716 GST_OBJECT_UNLOCK (playsink);
1718 gst_bin_add (bin, chain->conv);
1720 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1721 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1729 update_colorbalance (playsink);
1732 GST_DEBUG_OBJECT (playsink, "linking to sink");
1733 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1734 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1738 pad = gst_element_get_static_pad (head, "sink");
1739 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1741 /* sending audio/video flushes break stream changes when the pipeline
1742 * is paused and played again in 0.10 */
1744 gst_pad_set_event_function (chain->sinkpad,
1745 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1746 gst_pad_set_chain_function (chain->sinkpad,
1747 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1750 gst_object_unref (pad);
1751 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1758 if (!elem && !playsink->video_sink) {
1759 post_missing_element_message (playsink, "autovideosink");
1760 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1761 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1762 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1763 (_("Both autovideosink and %s elements are missing."),
1764 DEFAULT_VIDEOSINK), (NULL));
1766 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1767 (_("The autovideosink element is missing.")), (NULL));
1770 if (playsink->video_sink) {
1771 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1772 (_("Configured videosink %s is not working."),
1773 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1774 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1775 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1776 (_("Both autovideosink and %s elements are not working."),
1777 DEFAULT_VIDEOSINK), (NULL));
1779 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1780 (_("The autovideosink element is not working.")), (NULL));
1783 free_chain ((GstPlayChain *) chain);
1789 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1790 (NULL), ("Failed to configure the video sink."));
1791 /* checking sink made it READY */
1792 gst_element_set_state (chain->sink, GST_STATE_NULL);
1793 /* Remove chain from the bin to allow reuse later */
1794 gst_bin_remove (bin, chain->sink);
1795 free_chain ((GstPlayChain *) chain);
1801 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1804 GstPlayVideoChain *chain;
1805 GstStateChangeReturn ret;
1807 chain = playsink->videochain;
1809 chain->chain.raw = raw;
1811 /* if the chain was active we don't do anything */
1812 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1815 /* try to set the sink element to READY again */
1816 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1817 if (ret == GST_STATE_CHANGE_FAILURE)
1820 /* Get the VideoOverlay element */
1822 GstVideoOverlay *overlay = NULL;
1824 GST_OBJECT_LOCK (playsink);
1825 if (playsink->overlay_element)
1826 gst_object_unref (playsink->overlay_element);
1827 playsink->overlay_element =
1828 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1829 GST_TYPE_VIDEO_OVERLAY));
1830 if (playsink->overlay_element)
1831 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1832 GST_OBJECT_UNLOCK (playsink);
1835 if (playsink->overlay_handle_set)
1836 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1837 if (playsink->overlay_handle_events_set)
1838 gst_video_overlay_handle_events (overlay,
1839 playsink->overlay_handle_events);
1840 if (playsink->overlay_render_rectangle_set)
1841 gst_video_overlay_set_render_rectangle (overlay,
1842 playsink->overlay_x, playsink->overlay_y,
1843 playsink->overlay_width, playsink->overlay_height);
1844 gst_object_unref (overlay);
1848 /* find ts-offset element */
1849 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1850 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1853 /* if we can disable async behaviour of the sink, we can avoid adding a
1854 * queue for the audio chain. */
1856 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1859 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1860 async, GST_ELEMENT_NAME (elem));
1861 g_object_set (elem, "async", async, NULL);
1862 chain->async = async;
1864 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1865 chain->async = TRUE;
1868 /* Make sure the aspect ratio is kept */
1870 gst_play_sink_find_property_sinks (playsink, chain->sink,
1871 "force-aspect-ratio", G_TYPE_BOOLEAN);
1873 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1876 GST_OBJECT_LOCK (playsink);
1877 if (playsink->colorbalance_element) {
1878 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1879 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1880 gst_object_unref (playsink->colorbalance_element);
1882 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1883 GST_OBJECT_UNLOCK (playsink);
1886 gboolean use_balance = !playsink->colorbalance_element
1887 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1889 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1891 GST_OBJECT_LOCK (playsink);
1892 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1893 playsink->colorbalance_element =
1894 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1895 (chain->conv)->balance));
1896 GST_OBJECT_UNLOCK (playsink);
1899 update_colorbalance (playsink);
1905 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1909 GstStructure *structure;
1910 event = gst_event_new_segment (segment);
1911 structure = gst_event_writable_structure (event);
1912 gst_structure_id_set (structure,
1913 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1918 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1919 const gchar * sink_type,
1920 gboolean * sink_ignore_wrong_state,
1921 gboolean * sink_custom_flush_finished,
1922 gboolean * sink_pending_flush, GstSegment * sink_segment)
1924 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1926 const GstStructure *structure = gst_event_get_structure (event);
1928 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1929 gchar *custom_flush;
1930 gchar *custom_flush_finish;
1932 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1933 custom_flush_finish =
1934 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1935 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1936 GST_DEBUG_OBJECT (pad,
1937 "Custom %s flush event received, marking to flush %s", sink_type,
1939 GST_PLAY_SINK_LOCK (playsink);
1940 *sink_ignore_wrong_state = TRUE;
1941 *sink_custom_flush_finished = FALSE;
1942 GST_PLAY_SINK_UNLOCK (playsink);
1943 } else if (strcmp (gst_structure_get_name (structure),
1944 custom_flush_finish) == 0) {
1945 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1947 GST_PLAY_SINK_LOCK (playsink);
1948 *sink_pending_flush = TRUE;
1949 *sink_custom_flush_finished = TRUE;
1950 GST_PLAY_SINK_UNLOCK (playsink);
1953 g_free (custom_flush);
1954 g_free (custom_flush_finish);
1955 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1956 GST_PLAY_SINK_LOCK (playsink);
1957 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1959 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1960 GST_PLAY_SINK_UNLOCK (playsink);
1963 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1964 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1966 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1967 const GstSegment *segment;
1969 gst_event_parse_segment (event, &segment);
1970 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1972 GST_PLAY_SINK_LOCK (playsink);
1973 if (sink_segment->format != segment->format) {
1974 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1976 gst_format_get_name (sink_segment->format),
1977 gst_format_get_name (segment->format));
1978 gst_segment_init (sink_segment, segment->format);
1981 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1982 sink_type, sink_segment);
1983 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1984 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1985 sink_type, sink_segment);
1986 GST_PLAY_SINK_UNLOCK (playsink);
1989 gst_event_unref (event);
1990 gst_object_unref (playsink);
1994 static GstFlowReturn
1995 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1996 const gchar * sink_type,
1997 gboolean * sink_ignore_wrong_state,
1998 gboolean * sink_custom_flush_finished,
1999 gboolean * sink_pending_flush, GstSegment * sink_segment)
2001 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2002 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2005 GST_PLAY_SINK_LOCK (playsink);
2007 if (*sink_pending_flush) {
2009 GstStructure *structure;
2011 *sink_pending_flush = FALSE;
2013 GST_PLAY_SINK_UNLOCK (playsink);
2015 /* make the bin drop all cached data.
2016 * This event will be dropped on the src pad, if any. */
2017 event = gst_event_new_flush_start ();
2018 structure = gst_event_writable_structure (event);
2019 gst_structure_id_set (structure,
2020 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2022 GST_DEBUG_OBJECT (pad,
2023 "Pushing %s flush-start event with reset segment marker set: %"
2024 GST_PTR_FORMAT, sink_type, event);
2025 gst_pad_send_event (pad, event);
2027 /* make queue drop all cached data.
2028 * This event will be dropped on the src pad. */
2029 event = gst_event_new_flush_stop (TRUE);
2030 structure = gst_event_writable_structure (event);
2031 gst_structure_id_set (structure,
2032 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2034 GST_DEBUG_OBJECT (pad,
2035 "Pushing %s flush-stop event with reset segment marker set: %"
2036 GST_PTR_FORMAT, sink_type, event);
2037 gst_pad_send_event (pad, event);
2039 /* Re-sync queue segment info after flush-stop.
2040 * This event will be dropped on the src pad. */
2041 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2044 _generate_update_newsegment_event (pad, sink_segment, &event1);
2045 GST_DEBUG_OBJECT (playsink,
2046 "Pushing segment event with reset "
2047 "segment marker set: %" GST_PTR_FORMAT, event1);
2048 gst_pad_send_event (pad, event1);
2051 GST_PLAY_SINK_UNLOCK (playsink);
2054 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2056 GST_PLAY_SINK_LOCK (playsink);
2057 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2058 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2060 if (*sink_custom_flush_finished) {
2061 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2062 "wrong state for %s", sink_type);
2063 *sink_ignore_wrong_state = FALSE;
2068 GST_PLAY_SINK_UNLOCK (playsink);
2070 gst_object_unref (playsink);
2071 gst_object_unref (tbin);
2075 /* sending audio/video flushes break stream changes when the pipeline
2076 * is paused and played again in 0.10 */
2079 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2081 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2082 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2085 ret = gst_play_sink_sink_event (pad, event, "video",
2086 &playsink->video_ignore_wrong_state,
2087 &playsink->video_custom_flush_finished,
2088 &playsink->video_pending_flush, &playsink->video_segment);
2090 gst_object_unref (playsink);
2091 gst_object_unref (tbin);
2095 static GstFlowReturn
2096 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2098 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2099 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2102 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2103 &playsink->video_ignore_wrong_state,
2104 &playsink->video_custom_flush_finished,
2105 &playsink->video_pending_flush, &playsink->video_segment);
2107 gst_object_unref (playsink);
2108 gst_object_unref (tbin);
2113 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2115 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2116 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2119 ret = gst_play_sink_sink_event (pad, event, "audio",
2120 &playsink->audio_ignore_wrong_state,
2121 &playsink->audio_custom_flush_finished,
2122 &playsink->audio_pending_flush, &playsink->audio_segment);
2124 gst_object_unref (playsink);
2125 gst_object_unref (tbin);
2129 static GstFlowReturn
2130 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2132 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2133 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2136 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2137 &playsink->audio_ignore_wrong_state,
2138 &playsink->audio_custom_flush_finished,
2139 &playsink->audio_pending_flush, &playsink->audio_segment);
2141 gst_object_unref (playsink);
2142 gst_object_unref (tbin);
2148 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2151 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2154 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2155 &playsink->text_ignore_wrong_state,
2156 &playsink->text_custom_flush_finished,
2157 &playsink->text_pending_flush, &playsink->text_segment);
2159 gst_object_unref (playsink);
2164 static GstFlowReturn
2165 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2169 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2171 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2172 &playsink->text_ignore_wrong_state,
2173 &playsink->text_custom_flush_finished,
2174 &playsink->text_pending_flush, &playsink->text_segment);
2176 gst_object_unref (playsink);
2181 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2185 const GstStructure *structure;
2187 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2189 structure = gst_event_get_structure (event);
2192 gst_structure_id_has_field (structure,
2193 _playsink_reset_segment_event_marker_id)) {
2194 /* the events marked with a reset segment marker
2195 * are sent internally to reset the queue and
2196 * must be dropped here */
2197 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2198 "segment marker set: %" GST_PTR_FORMAT, event);
2203 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2206 gst_event_unref (event);
2210 /* make an element for playback of video with subtitles embedded.
2211 * Only used for *raw* video streams.
2213 * +--------------------------------------------+
2215 * | +--------+ +-----------------+ |
2216 * | | queue | | subtitleoverlay | |
2217 * video--src sink---video_sink | |
2218 * | +--------+ | src--src
2219 * text------------------text_sink | |
2220 * | +-----------------+ |
2221 * +--------------------------------------------+
2224 static GstPlayTextChain *
2225 gen_text_chain (GstPlaySink * playsink)
2227 GstPlayTextChain *chain;
2230 GstPad *videosinkpad, *textsinkpad, *srcpad;
2232 chain = g_new0 (GstPlayTextChain, 1);
2233 chain->chain.playsink = playsink;
2235 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2237 chain->chain.bin = gst_bin_new ("tbin");
2238 bin = GST_BIN_CAST (chain->chain.bin);
2239 gst_object_ref_sink (bin);
2241 videosinkpad = textsinkpad = srcpad = NULL;
2243 /* first try to hook the text pad to the custom sink */
2244 if (playsink->text_sink) {
2245 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2246 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2249 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2252 /* make sure the sparse subtitles don't participate in the preroll */
2253 g_object_set (elem, "async", FALSE, NULL);
2254 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2255 gst_bin_add (bin, chain->sink);
2256 /* NOTE streamsynchronizer needs streams decoupled */
2257 /* make a little queue */
2258 chain->queue = gst_element_factory_make ("queue", "subqueue");
2259 if (chain->queue == NULL) {
2260 post_missing_element_message (playsink, "queue");
2261 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2262 (_("Missing element '%s' - check your GStreamer installation."),
2263 "queue"), ("rendering might be suboptimal"));
2265 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2266 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2267 "silent", TRUE, NULL);
2268 gst_bin_add (bin, chain->queue);
2270 /* we have a custom sink, this will be our textsinkpad */
2271 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2272 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2273 /* we're all fine now and we can add the sink to the chain */
2274 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2275 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2277 GST_WARNING_OBJECT (playsink,
2278 "can't find a sink pad on custom text sink");
2279 gst_bin_remove (bin, chain->sink);
2280 gst_bin_remove (bin, chain->queue);
2282 chain->queue = NULL;
2284 /* try to set sync to true but it's no biggie when we can't */
2285 if (chain->sink && (elem =
2286 gst_play_sink_find_property_sinks (playsink, chain->sink,
2287 "sync", G_TYPE_BOOLEAN)))
2288 g_object_set (elem, "sync", TRUE, NULL);
2291 gst_bin_remove (bin, chain->sink);
2293 GST_WARNING_OBJECT (playsink,
2294 "can't find async property in custom text sink");
2297 if (textsinkpad == NULL) {
2298 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2299 (_("Custom text sink element is not usable.")),
2300 ("fallback to default subtitleoverlay"));
2304 if (textsinkpad == NULL) {
2305 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2306 /* make a little queue */
2307 chain->queue = gst_element_factory_make ("queue", "vqueue");
2308 if (chain->queue == NULL) {
2309 post_missing_element_message (playsink, "queue");
2310 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2311 (_("Missing element '%s' - check your GStreamer installation."),
2312 "queue"), ("video rendering might be suboptimal"));
2314 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2315 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2316 "silent", TRUE, NULL);
2317 gst_bin_add (bin, chain->queue);
2318 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2322 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2323 if (chain->overlay == NULL) {
2324 post_missing_element_message (playsink, "subtitleoverlay");
2325 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2326 (_("Missing element '%s' - check your GStreamer installation."),
2327 "subtitleoverlay"), ("subtitle rendering disabled"));
2329 GstElement *element;
2331 gst_bin_add (bin, chain->overlay);
2333 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2334 if (playsink->font_desc) {
2335 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2336 playsink->font_desc, NULL);
2338 if (playsink->subtitle_encoding) {
2339 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2340 playsink->subtitle_encoding, NULL);
2343 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2344 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2346 /* make another little queue to decouple streams */
2347 element = gst_element_factory_make ("queue", "subqueue");
2348 if (element == NULL) {
2349 post_missing_element_message (playsink, "queue");
2350 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2351 (_("Missing element '%s' - check your GStreamer installation."),
2352 "queue"), ("rendering might be suboptimal"));
2354 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2355 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2356 "silent", TRUE, NULL);
2357 gst_bin_add (bin, element);
2358 if (gst_element_link_pads_full (element, "src", chain->overlay,
2359 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2360 textsinkpad = gst_element_get_static_pad (element, "sink");
2361 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2363 gst_bin_remove (bin, chain->sink);
2364 gst_bin_remove (bin, chain->overlay);
2366 chain->overlay = NULL;
2367 gst_object_unref (videosinkpad);
2368 videosinkpad = NULL;
2375 if (videosinkpad == NULL) {
2376 /* if we still don't have a videosink, we don't have an overlay. the only
2377 * thing we can do is insert an identity and ghost the src
2379 chain->identity = gst_element_factory_make ("identity", "tidentity");
2380 if (chain->identity == NULL) {
2381 post_missing_element_message (playsink, "identity");
2382 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2383 (_("Missing element '%s' - check your GStreamer installation."),
2384 "identity"), (NULL));
2386 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2387 g_object_set (chain->identity, "silent", TRUE, NULL);
2388 gst_bin_add (bin, chain->identity);
2389 srcpad = gst_element_get_static_pad (chain->identity, "src");
2390 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2394 /* expose the ghostpads */
2396 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2397 gst_object_unref (videosinkpad);
2398 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2401 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2402 gst_object_unref (textsinkpad);
2404 gst_pad_set_event_function (chain->textsinkpad,
2405 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2406 gst_pad_set_chain_function (chain->textsinkpad,
2407 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2409 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2412 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2413 gst_object_unref (srcpad);
2415 gst_pad_set_event_function (chain->srcpad,
2416 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2418 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2425 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2429 g_object_get (object, "volume", &vol, NULL);
2430 playsink->volume = vol;
2432 g_object_notify (G_OBJECT (playsink), "volume");
2436 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2440 g_object_get (object, "mute", &mute, NULL);
2441 playsink->mute = mute;
2443 g_object_notify (G_OBJECT (playsink), "mute");
2446 /* make the chain that contains the elements needed to perform
2449 * We add a tee as the first element so that we can link the visualisation chain
2450 * to it when requested.
2452 * +-------------------------------------------------------------+
2454 * | +---------+ +----------+ +---------+ +---------+ |
2455 * | |audioconv| |audioscale| | volume | |audiosink| |
2456 * | +-srck src-sink src-sink src-sink | |
2457 * | | +---------+ +----------+ +---------+ +---------+ |
2459 * +-------------------------------------------------------------+
2461 static GstPlayAudioChain *
2462 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2464 GstPlayAudioChain *chain;
2466 gboolean have_volume;
2468 GstElement *head, *prev, *elem = NULL;
2470 chain = g_new0 (GstPlayAudioChain, 1);
2471 chain->chain.playsink = playsink;
2472 chain->chain.raw = raw;
2474 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2476 if (playsink->audio_sink) {
2477 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2478 playsink->audio_sink);
2479 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2481 /* only try fallback if no specific sink was chosen */
2482 if (chain->sink == NULL) {
2483 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2484 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2485 chain->sink = try_element (playsink, elem, TRUE);
2487 if (chain->sink == NULL) {
2488 /* if default sink from config.h is different then try it too */
2489 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2490 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2491 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2492 chain->sink = try_element (playsink, elem, TRUE);
2496 playsink->audio_sink = gst_object_ref (chain->sink);
2498 if (chain->sink == NULL)
2501 chain->chain.bin = gst_bin_new ("abin");
2502 bin = GST_BIN_CAST (chain->chain.bin);
2503 gst_object_ref_sink (bin);
2504 gst_bin_add (bin, chain->sink);
2506 /* we have to add a queue when we need to decouple for the video sink in
2507 * visualisations and for streamsynchronizer */
2508 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2509 chain->queue = gst_element_factory_make ("queue", "aqueue");
2510 if (chain->queue == NULL) {
2511 post_missing_element_message (playsink, "queue");
2512 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2513 (_("Missing element '%s' - check your GStreamer installation."),
2514 "queue"), ("audio playback and visualizations might not work"));
2518 g_object_set (chain->queue, "silent", TRUE, NULL);
2519 gst_bin_add (bin, chain->queue);
2520 prev = head = chain->queue;
2523 /* find ts-offset element */
2524 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2525 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2528 /* check if the sink, or something within the sink, has the volume property.
2529 * If it does we don't need to add a volume element. */
2531 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2534 chain->volume = elem;
2536 g_signal_connect (chain->volume, "notify::volume",
2537 G_CALLBACK (notify_volume_cb), playsink);
2539 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2541 chain->sink_volume = TRUE;
2542 /* if the sink also has a mute property we can use this as well. We'll only
2543 * use the mute property if there is a volume property. We can simulate the
2544 * mute with the volume otherwise. */
2546 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2549 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2550 g_signal_connect (chain->mute, "notify::mute",
2551 G_CALLBACK (notify_mute_cb), playsink);
2553 /* use the sink to control the volume and mute */
2554 if (playsink->volume_changed) {
2555 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2556 playsink->volume_changed = FALSE;
2558 if (playsink->mute_changed) {
2560 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2563 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2565 playsink->mute_changed = FALSE;
2568 /* no volume, we need to add a volume element when we can */
2569 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2570 have_volume = FALSE;
2571 chain->sink_volume = FALSE;
2574 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2575 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2576 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2577 gboolean use_volume =
2578 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2579 GST_DEBUG_OBJECT (playsink,
2580 "creating audioconvert with use-converters %d, use-volume %d",
2581 use_converters, use_volume);
2583 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2584 "use-converters", use_converters, "use-volume", use_volume, NULL);
2585 gst_bin_add (bin, chain->conv);
2587 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2588 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2595 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2596 GstPlaySinkAudioConvert *conv =
2597 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2600 chain->volume = conv->volume;
2603 g_signal_connect (chain->volume, "notify::volume",
2604 G_CALLBACK (notify_volume_cb), playsink);
2606 /* volume also has the mute property */
2607 chain->mute = chain->volume;
2608 g_signal_connect (chain->mute, "notify::mute",
2609 G_CALLBACK (notify_mute_cb), playsink);
2611 /* configure with the latest volume and mute */
2612 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2614 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2620 /* we only have to link to the previous element if we have something in
2621 * front of the sink */
2622 GST_DEBUG_OBJECT (playsink, "linking to sink");
2623 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2624 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2628 /* post a warning if we have no way to configure the volume */
2630 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2631 (_("No volume control found")), ("Volume/mute is not available"));
2634 /* and ghost the sinkpad of the headmost element */
2635 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2636 pad = gst_element_get_static_pad (head, "sink");
2637 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2639 /* sending audio/video flushes break stream changes when the pipeline
2640 * is paused and played again in 0.10 */
2642 gst_pad_set_event_function (chain->sinkpad,
2643 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2644 gst_pad_set_chain_function (chain->sinkpad,
2645 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2648 gst_object_unref (pad);
2649 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2656 if (!elem && !playsink->audio_sink) {
2657 post_missing_element_message (playsink, "autoaudiosink");
2658 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2659 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2660 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2661 (_("Both autoaudiosink and %s elements are missing."),
2662 DEFAULT_AUDIOSINK), (NULL));
2664 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2665 (_("The autoaudiosink element is missing.")), (NULL));
2668 if (playsink->audio_sink) {
2669 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2670 (_("Configured audiosink %s is not working."),
2671 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2672 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2673 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2674 (_("Both autoaudiosink and %s elements are not working."),
2675 DEFAULT_AUDIOSINK), (NULL));
2677 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2678 (_("The autoaudiosink element is not working.")), (NULL));
2681 free_chain ((GstPlayChain *) chain);
2686 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2687 (NULL), ("Failed to configure the audio sink."));
2688 /* checking sink made it READY */
2689 gst_element_set_state (chain->sink, GST_STATE_NULL);
2690 /* Remove chain from the bin to allow reuse later */
2691 gst_bin_remove (bin, chain->sink);
2692 free_chain ((GstPlayChain *) chain);
2698 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2701 GstPlayAudioChain *chain;
2702 GstStateChangeReturn ret;
2703 GstPlaySinkAudioConvert *conv;
2705 chain = playsink->audiochain;
2706 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2708 chain->chain.raw = raw;
2710 /* if the chain was active we don't do anything */
2711 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2714 /* try to set the sink element to READY again */
2715 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2716 if (ret == GST_STATE_CHANGE_FAILURE)
2719 /* find ts-offset element */
2720 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2721 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2724 /* check if the sink, or something within the sink, has the volume property.
2725 * If it does we don't need to add a volume element. */
2727 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2730 chain->volume = elem;
2732 if (playsink->volume_changed) {
2733 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2735 /* use the sink to control the volume */
2736 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2737 playsink->volume_changed = FALSE;
2740 g_signal_connect (chain->volume, "notify::volume",
2741 G_CALLBACK (notify_volume_cb), playsink);
2742 /* if the sink also has a mute property we can use this as well. We'll only
2743 * use the mute property if there is a volume property. We can simulate the
2744 * mute with the volume otherwise. */
2746 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2749 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2750 g_signal_connect (chain->mute, "notify::mute",
2751 G_CALLBACK (notify_mute_cb), playsink);
2754 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2756 /* no volume, we need to add a volume element when we can */
2757 g_object_set (chain->conv, "use-volume",
2758 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2759 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2761 /* Disconnect signals */
2762 disconnect_chain (chain, playsink);
2764 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2765 chain->volume = conv->volume;
2766 chain->mute = chain->volume;
2768 g_signal_connect (chain->volume, "notify::volume",
2769 G_CALLBACK (notify_volume_cb), playsink);
2771 g_signal_connect (chain->mute, "notify::mute",
2772 G_CALLBACK (notify_mute_cb), playsink);
2774 /* configure with the latest volume and mute */
2775 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2776 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2779 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2785 * +-------------------------------------------------------------------+
2787 * | +----------+ +------------+ +----------+ +-------+ |
2788 * | | visqueue | | audioconv | | audiores | | vis | |
2789 * | +-sink src-sink + samp src-sink src-sink src-+ |
2790 * | | +----------+ +------------+ +----------+ +-------+ | |
2792 * +-------------------------------------------------------------------+
2795 static GstPlayVisChain *
2796 gen_vis_chain (GstPlaySink * playsink)
2798 GstPlayVisChain *chain;
2804 chain = g_new0 (GstPlayVisChain, 1);
2805 chain->chain.playsink = playsink;
2807 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2809 chain->chain.bin = gst_bin_new ("visbin");
2810 bin = GST_BIN_CAST (chain->chain.bin);
2811 gst_object_ref_sink (bin);
2813 /* we're queuing raw audio here, we can remove this queue when we can disable
2814 * async behaviour in the video sink. */
2815 chain->queue = gst_element_factory_make ("queue", "visqueue");
2816 if (chain->queue == NULL)
2818 g_object_set (chain->queue, "silent", TRUE, NULL);
2819 gst_bin_add (bin, chain->queue);
2821 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2822 if (chain->conv == NULL)
2823 goto no_audioconvert;
2824 gst_bin_add (bin, chain->conv);
2826 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2827 if (chain->resample == NULL)
2828 goto no_audioresample;
2829 gst_bin_add (bin, chain->resample);
2831 /* this pad will be used for blocking the dataflow and switching the vis
2833 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2835 if (playsink->visualisation) {
2836 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2837 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2839 if (chain->vis == NULL) {
2840 GST_DEBUG_OBJECT (playsink, "trying goom");
2841 elem = gst_element_factory_make ("goom", "vis");
2842 chain->vis = try_element (playsink, elem, TRUE);
2844 if (chain->vis == NULL)
2847 gst_bin_add (bin, chain->vis);
2849 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2850 GST_PAD_LINK_CHECK_NOTHING);
2852 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2853 GST_PAD_LINK_CHECK_NOTHING);
2855 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2856 GST_PAD_LINK_CHECK_NOTHING);
2860 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2861 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2863 pad = gst_element_get_static_pad (chain->queue, "sink");
2864 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2865 gst_object_unref (pad);
2866 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2868 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2869 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2876 post_missing_element_message (playsink, "queue");
2877 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2878 (_("Missing element '%s' - check your GStreamer installation."),
2880 free_chain ((GstPlayChain *) chain);
2885 post_missing_element_message (playsink, "audioconvert");
2886 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2887 (_("Missing element '%s' - check your GStreamer installation."),
2888 "audioconvert"), ("possibly a liboil version mismatch?"));
2889 free_chain ((GstPlayChain *) chain);
2894 post_missing_element_message (playsink, "audioresample");
2895 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2896 (_("Missing element '%s' - check your GStreamer installation."),
2897 "audioresample"), (NULL));
2898 free_chain ((GstPlayChain *) chain);
2903 post_missing_element_message (playsink, "goom");
2904 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2905 (_("Missing element '%s' - check your GStreamer installation."),
2907 free_chain ((GstPlayChain *) chain);
2912 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2913 (NULL), ("Failed to configure the visualisation element."));
2914 /* element made it to READY */
2915 gst_element_set_state (chain->vis, GST_STATE_NULL);
2916 free_chain ((GstPlayChain *) chain);
2921 /* this function is called when all the request pads are requested and when we
2922 * have to construct the final pipeline. Based on the flags we construct the
2923 * final output pipelines.
2926 gst_play_sink_reconfigure (GstPlaySink * playsink)
2929 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2931 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2933 /* assume we need nothing */
2934 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2936 GST_PLAY_SINK_LOCK (playsink);
2937 GST_OBJECT_LOCK (playsink);
2938 /* get flags, there are protected with the object lock */
2939 flags = playsink->flags;
2940 GST_OBJECT_UNLOCK (playsink);
2942 /* figure out which components we need */
2943 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2944 /* we have subtitles and we are requested to show it */
2948 if (((flags & GST_PLAY_FLAG_VIDEO)
2949 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2950 /* we have video and we are requested to show it */
2953 /* we only deinterlace if native video is not requested and
2954 * we have raw video */
2955 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2956 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2957 need_deinterlace = TRUE;
2960 if (playsink->audio_pad) {
2961 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2964 if (playsink->audio_pad_raw) {
2965 /* only can do vis with raw uncompressed audio */
2966 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2967 /* also add video when we add visualisation */
2974 /* we have a text_pad and we need text rendering, in this case we need a
2975 * video_pad to combine the video with the text or visualizations */
2976 if (need_text && !need_video && !playsink->text_sink) {
2977 if (playsink->video_pad) {
2979 } else if (need_audio) {
2980 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2981 (_("Can't play a text file without video or visualizations.")),
2982 ("Have text pad but no video pad or visualizations"));
2985 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2986 (_("Can't play a text file without video or visualizations.")),
2987 ("Have text pad but no video pad or visualizations"));
2988 GST_PLAY_SINK_UNLOCK (playsink);
2993 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2994 need_video, need_vis, need_text);
2996 /* set up video pipeline */
2998 gboolean raw, async;
3000 /* we need a raw sink when we do vis or when we have a raw pad */
3001 raw = need_vis ? TRUE : playsink->video_pad_raw;
3002 /* we try to set the sink async=FALSE when we need vis, this way we can
3003 * avoid a queue in the audio chain. */
3006 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3007 playsink->video_pad_raw);
3009 if (playsink->videochain) {
3010 /* try to reactivate the chain */
3011 if (!setup_video_chain (playsink, raw, async)) {
3012 if (playsink->video_sinkpad_stream_synchronizer) {
3013 gst_element_release_request_pad (GST_ELEMENT_CAST
3014 (playsink->stream_synchronizer),
3015 playsink->video_sinkpad_stream_synchronizer);
3016 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3017 playsink->video_sinkpad_stream_synchronizer = NULL;
3018 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3019 playsink->video_srcpad_stream_synchronizer = NULL;
3022 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3024 /* Remove the sink from the bin to keep its state
3025 * and unparent it to allow reuse */
3026 if (playsink->videochain->sink)
3027 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3028 playsink->videochain->sink);
3030 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3031 free_chain ((GstPlayChain *) playsink->videochain);
3032 playsink->videochain = NULL;
3034 GST_OBJECT_LOCK (playsink);
3035 if (playsink->overlay_element)
3036 gst_object_unref (playsink->overlay_element);
3037 playsink->overlay_element = NULL;
3039 if (playsink->colorbalance_element) {
3040 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3041 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3042 gst_object_unref (playsink->colorbalance_element);
3044 playsink->colorbalance_element = NULL;
3045 GST_OBJECT_UNLOCK (playsink);
3049 if (!playsink->videochain)
3050 playsink->videochain = gen_video_chain (playsink, raw, async);
3051 if (!playsink->videochain)
3054 if (!playsink->video_sinkpad_stream_synchronizer) {
3055 GValue item = { 0, };
3058 playsink->video_sinkpad_stream_synchronizer =
3059 gst_element_get_request_pad (GST_ELEMENT_CAST
3060 (playsink->stream_synchronizer), "sink_%u");
3061 it = gst_pad_iterate_internal_links
3062 (playsink->video_sinkpad_stream_synchronizer);
3064 gst_iterator_next (it, &item);
3065 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3066 g_value_unset (&item);
3067 g_assert (playsink->video_srcpad_stream_synchronizer);
3068 gst_iterator_free (it);
3071 if (playsink->video_pad)
3072 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3073 playsink->video_sinkpad_stream_synchronizer);
3075 if (need_deinterlace) {
3076 if (!playsink->videodeinterlacechain)
3077 playsink->videodeinterlacechain =
3078 gen_video_deinterlace_chain (playsink);
3079 if (!playsink->videodeinterlacechain)
3082 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3084 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3086 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3087 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3089 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3090 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3092 if (playsink->videodeinterlacechain) {
3093 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3094 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3099 GST_DEBUG_OBJECT (playsink, "adding video chain");
3100 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3101 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3102 /* if we are not part of vis or subtitles, set the ghostpad target */
3103 if (!need_vis && !need_text && (!playsink->textchain
3104 || !playsink->text_pad)) {
3105 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3106 if (need_deinterlace)
3107 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3108 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3110 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3111 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3114 GST_DEBUG_OBJECT (playsink, "no video needed");
3115 if (playsink->videochain) {
3116 GST_DEBUG_OBJECT (playsink, "removing video chain");
3117 if (playsink->vischain) {
3120 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3122 /* also had visualisation, release the tee srcpad before we then
3123 * unlink the video from it */
3124 if (playsink->audio_tee_vissrc) {
3125 gst_element_release_request_pad (playsink->audio_tee,
3126 playsink->audio_tee_vissrc);
3127 gst_object_unref (playsink->audio_tee_vissrc);
3128 playsink->audio_tee_vissrc = NULL;
3131 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3132 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3135 if (playsink->video_sinkpad_stream_synchronizer) {
3136 gst_element_release_request_pad (GST_ELEMENT_CAST
3137 (playsink->stream_synchronizer),
3138 playsink->video_sinkpad_stream_synchronizer);
3139 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3140 playsink->video_sinkpad_stream_synchronizer = NULL;
3141 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3142 playsink->video_srcpad_stream_synchronizer = NULL;
3145 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3146 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3147 if (playsink->videochain->ts_offset)
3148 gst_object_unref (playsink->videochain->ts_offset);
3149 playsink->videochain->ts_offset = NULL;
3152 if (playsink->videodeinterlacechain) {
3153 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3154 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3157 if (playsink->video_pad)
3158 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3160 GST_OBJECT_LOCK (playsink);
3161 if (playsink->overlay_element)
3162 gst_object_unref (playsink->overlay_element);
3163 playsink->overlay_element = NULL;
3165 if (playsink->colorbalance_element) {
3166 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3167 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3168 gst_object_unref (playsink->colorbalance_element);
3170 playsink->colorbalance_element = NULL;
3171 GST_OBJECT_UNLOCK (playsink);
3178 GST_DEBUG_OBJECT (playsink, "adding audio");
3180 /* get a raw sink if we are asked for a raw pad */
3181 raw = playsink->audio_pad_raw;
3183 if (playsink->audiochain) {
3184 /* try to reactivate the chain */
3185 if (!setup_audio_chain (playsink, raw)) {
3186 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3187 if (playsink->audio_tee_asrc) {
3188 gst_element_release_request_pad (playsink->audio_tee,
3189 playsink->audio_tee_asrc);
3190 gst_object_unref (playsink->audio_tee_asrc);
3191 playsink->audio_tee_asrc = NULL;
3194 if (playsink->audio_sinkpad_stream_synchronizer) {
3195 gst_element_release_request_pad (GST_ELEMENT_CAST
3196 (playsink->stream_synchronizer),
3197 playsink->audio_sinkpad_stream_synchronizer);
3198 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3199 playsink->audio_sinkpad_stream_synchronizer = NULL;
3200 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3201 playsink->audio_srcpad_stream_synchronizer = NULL;
3204 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3206 /* Remove the sink from the bin to keep its state
3207 * and unparent it to allow reuse */
3208 if (playsink->audiochain->sink)
3209 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3210 playsink->audiochain->sink);
3212 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3213 disconnect_chain (playsink->audiochain, playsink);
3214 playsink->audiochain->volume = NULL;
3215 playsink->audiochain->mute = NULL;
3216 if (playsink->audiochain->ts_offset)
3217 gst_object_unref (playsink->audiochain->ts_offset);
3218 playsink->audiochain->ts_offset = NULL;
3219 free_chain ((GstPlayChain *) playsink->audiochain);
3220 playsink->audiochain = NULL;
3221 playsink->volume_changed = playsink->mute_changed = FALSE;
3225 if (!playsink->audiochain) {
3226 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3227 playsink->audiochain = gen_audio_chain (playsink, raw);
3230 if (!playsink->audio_sinkpad_stream_synchronizer) {
3231 GValue item = { 0, };
3234 playsink->audio_sinkpad_stream_synchronizer =
3235 gst_element_get_request_pad (GST_ELEMENT_CAST
3236 (playsink->stream_synchronizer), "sink_%u");
3237 it = gst_pad_iterate_internal_links
3238 (playsink->audio_sinkpad_stream_synchronizer);
3240 gst_iterator_next (it, &item);
3241 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3242 g_value_unset (&item);
3243 g_assert (playsink->audio_srcpad_stream_synchronizer);
3244 gst_iterator_free (it);
3247 if (playsink->audiochain) {
3248 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3249 if (playsink->audio_tee_asrc == NULL) {
3250 playsink->audio_tee_asrc =
3251 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3253 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3254 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3255 gst_pad_link_full (playsink->audio_tee_asrc,
3256 playsink->audio_sinkpad_stream_synchronizer,
3257 GST_PAD_LINK_CHECK_NOTHING);
3258 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3259 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3262 GST_DEBUG_OBJECT (playsink, "no audio needed");
3263 /* we have no audio or we are requested to not play audio */
3264 if (playsink->audiochain) {
3265 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3266 /* release the audio pad */
3267 if (playsink->audio_tee_asrc) {
3268 gst_element_release_request_pad (playsink->audio_tee,
3269 playsink->audio_tee_asrc);
3270 gst_object_unref (playsink->audio_tee_asrc);
3271 playsink->audio_tee_asrc = NULL;
3274 if (playsink->audio_sinkpad_stream_synchronizer) {
3275 gst_element_release_request_pad (GST_ELEMENT_CAST
3276 (playsink->stream_synchronizer),
3277 playsink->audio_sinkpad_stream_synchronizer);
3278 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3279 playsink->audio_sinkpad_stream_synchronizer = NULL;
3280 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3281 playsink->audio_srcpad_stream_synchronizer = NULL;
3284 if (playsink->audiochain->sink_volume) {
3285 disconnect_chain (playsink->audiochain, playsink);
3286 playsink->audiochain->volume = NULL;
3287 playsink->audiochain->mute = NULL;
3288 if (playsink->audiochain->ts_offset)
3289 gst_object_unref (playsink->audiochain->ts_offset);
3290 playsink->audiochain->ts_offset = NULL;
3292 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3293 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3300 if (!playsink->vischain)
3301 playsink->vischain = gen_vis_chain (playsink);
3303 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3305 if (playsink->vischain) {
3306 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3308 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3309 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3310 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3311 if (playsink->audio_tee_vissrc == NULL) {
3312 playsink->audio_tee_vissrc =
3313 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3315 gst_pad_link_full (playsink->audio_tee_vissrc,
3316 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3317 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3318 GST_PAD_LINK_CHECK_NOTHING);
3319 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3320 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3321 gst_object_unref (srcpad);
3324 GST_DEBUG_OBJECT (playsink, "no vis needed");
3325 if (playsink->vischain) {
3326 if (playsink->audio_tee_vissrc) {
3327 gst_element_release_request_pad (playsink->audio_tee,
3328 playsink->audio_tee_vissrc);
3329 gst_object_unref (playsink->audio_tee_vissrc);
3330 playsink->audio_tee_vissrc = NULL;
3332 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3333 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3334 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3339 GST_DEBUG_OBJECT (playsink, "adding text");
3340 if (!playsink->textchain) {
3341 GST_DEBUG_OBJECT (playsink, "creating text chain");
3342 playsink->textchain = gen_text_chain (playsink);
3344 if (playsink->textchain) {
3347 GST_DEBUG_OBJECT (playsink, "adding text chain");
3348 if (playsink->textchain->overlay)
3349 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3351 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3353 if (!playsink->text_sinkpad_stream_synchronizer) {
3354 GValue item = { 0, };
3356 playsink->text_sinkpad_stream_synchronizer =
3357 gst_element_get_request_pad (GST_ELEMENT_CAST
3358 (playsink->stream_synchronizer), "sink_%u");
3359 it = gst_pad_iterate_internal_links
3360 (playsink->text_sinkpad_stream_synchronizer);
3362 gst_iterator_next (it, &item);
3363 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3364 g_value_unset (&item);
3365 g_assert (playsink->text_srcpad_stream_synchronizer);
3366 gst_iterator_free (it);
3368 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3369 playsink->text_sinkpad_stream_synchronizer);
3370 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3371 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3374 if (need_vis || need_video) {
3379 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3380 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3381 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3382 GST_PAD_LINK_CHECK_NOTHING);
3383 gst_object_unref (srcpad);
3385 if (need_deinterlace)
3386 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3387 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3389 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3390 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3392 gst_pad_link_full (playsink->textchain->srcpad,
3393 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3396 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3399 GST_DEBUG_OBJECT (playsink, "no text needed");
3400 /* we have no subtitles/text or we are requested to not show them */
3402 if (playsink->text_sinkpad_stream_synchronizer) {
3403 gst_element_release_request_pad (GST_ELEMENT_CAST
3404 (playsink->stream_synchronizer),
3405 playsink->text_sinkpad_stream_synchronizer);
3406 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3407 playsink->text_sinkpad_stream_synchronizer = NULL;
3408 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3409 playsink->text_srcpad_stream_synchronizer = NULL;
3412 if (playsink->textchain) {
3413 if (playsink->text_pad == NULL) {
3414 /* no text pad, remove the chain entirely */
3415 GST_DEBUG_OBJECT (playsink, "removing text chain");
3416 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3417 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3419 /* we have a chain and a textpad, turn the subtitles off */
3420 GST_DEBUG_OBJECT (playsink, "turning off the text");
3421 if (playsink->textchain->overlay)
3422 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3426 if (!need_video && playsink->video_pad) {
3427 if (playsink->video_sinkpad_stream_synchronizer) {
3428 gst_element_release_request_pad (GST_ELEMENT_CAST
3429 (playsink->stream_synchronizer),
3430 playsink->video_sinkpad_stream_synchronizer);
3431 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3432 playsink->video_sinkpad_stream_synchronizer = NULL;
3433 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3434 playsink->video_srcpad_stream_synchronizer = NULL;
3437 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3440 if (playsink->text_pad && !playsink->textchain)
3441 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3443 update_av_offset (playsink);
3444 do_async_done (playsink);
3445 GST_PLAY_SINK_UNLOCK (playsink);
3452 /* gen_ chain already posted error */
3453 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3454 GST_PLAY_SINK_UNLOCK (playsink);
3460 * gst_play_sink_set_flags:
3461 * @playsink: a #GstPlaySink
3462 * @flags: #GstPlayFlags
3464 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3465 * when constructing the sink pipelins.
3467 * Returns: TRUE if the flags could be configured.
3470 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3472 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3474 GST_OBJECT_LOCK (playsink);
3475 playsink->flags = flags;
3476 GST_OBJECT_UNLOCK (playsink);
3482 * gst_play_sink_get_flags:
3483 * @playsink: a #GstPlaySink
3485 * Get the flags of @playsink. That flags control the behaviour of the sink when
3486 * it constructs the sink pipelines.
3488 * Returns: the currently configured #GstPlayFlags.
3491 gst_play_sink_get_flags (GstPlaySink * playsink)
3495 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3497 GST_OBJECT_LOCK (playsink);
3498 res = playsink->flags;
3499 GST_OBJECT_UNLOCK (playsink);
3505 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3507 GstPlayTextChain *chain;
3509 GST_PLAY_SINK_LOCK (playsink);
3510 chain = (GstPlayTextChain *) playsink->textchain;
3511 g_free (playsink->font_desc);
3512 playsink->font_desc = g_strdup (desc);
3513 if (chain && chain->overlay) {
3514 g_object_set (chain->overlay, "font-desc", desc, NULL);
3516 GST_PLAY_SINK_UNLOCK (playsink);
3520 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3522 gchar *result = NULL;
3523 GstPlayTextChain *chain;
3525 GST_PLAY_SINK_LOCK (playsink);
3526 chain = (GstPlayTextChain *) playsink->textchain;
3527 if (chain && chain->overlay) {
3528 g_object_get (chain->overlay, "font-desc", &result, NULL);
3529 playsink->font_desc = g_strdup (result);
3531 result = g_strdup (playsink->font_desc);
3533 GST_PLAY_SINK_UNLOCK (playsink);
3539 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3540 const gchar * encoding)
3542 GstPlayTextChain *chain;
3544 GST_PLAY_SINK_LOCK (playsink);
3545 chain = (GstPlayTextChain *) playsink->textchain;
3546 g_free (playsink->subtitle_encoding);
3547 playsink->subtitle_encoding = g_strdup (encoding);
3548 if (chain && chain->overlay) {
3549 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3551 GST_PLAY_SINK_UNLOCK (playsink);
3555 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3557 gchar *result = NULL;
3558 GstPlayTextChain *chain;
3560 GST_PLAY_SINK_LOCK (playsink);
3561 chain = (GstPlayTextChain *) playsink->textchain;
3562 if (chain && chain->overlay) {
3563 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3564 playsink->subtitle_encoding = g_strdup (result);
3566 result = g_strdup (playsink->subtitle_encoding);
3568 GST_PLAY_SINK_UNLOCK (playsink);
3574 update_av_offset (GstPlaySink * playsink)
3577 GstPlayAudioChain *achain;
3578 GstPlayVideoChain *vchain;
3580 av_offset = playsink->av_offset;
3581 achain = (GstPlayAudioChain *) playsink->audiochain;
3582 vchain = (GstPlayVideoChain *) playsink->videochain;
3584 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3585 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3586 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3588 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3593 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3595 GST_PLAY_SINK_LOCK (playsink);
3596 playsink->av_offset = av_offset;
3597 update_av_offset (playsink);
3598 GST_PLAY_SINK_UNLOCK (playsink);
3602 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3606 GST_PLAY_SINK_LOCK (playsink);
3607 result = playsink->av_offset;
3608 GST_PLAY_SINK_UNLOCK (playsink);
3614 * gst_play_sink_get_last_sample:
3615 * @playsink: a #GstPlaySink
3617 * Get the last displayed sample from @playsink. This sample is in the native
3618 * format of the sink element, the caps in the result sample contain the format
3619 * of the frame data.
3621 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3625 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3627 GstSample *result = NULL;
3628 GstPlayVideoChain *chain;
3630 GST_PLAY_SINK_LOCK (playsink);
3631 GST_DEBUG_OBJECT (playsink, "taking last sample");
3632 /* get the video chain if we can */
3633 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3634 GST_DEBUG_OBJECT (playsink, "found video chain");
3635 /* see if the chain is active */
3636 if (chain->chain.activated && chain->sink) {
3639 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3641 /* find and get the last-buffer property now */
3643 gst_play_sink_find_property (playsink, chain->sink,
3644 "last-sample", GST_TYPE_SAMPLE))) {
3645 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3646 g_object_get (elem, "last-sample", &result, NULL);
3647 gst_object_unref (elem);
3651 GST_PLAY_SINK_UNLOCK (playsink);
3657 * gst_play_sink_convert_sample:
3658 * @playsink: a #GstPlaySink
3661 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3662 * be in the native format of the sink element and the caps on the buffer
3663 * describe the format of the frame. If @caps is not %NULL, the video
3664 * frame will be converted to the format of the caps.
3666 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3667 * available or when the conversion failed.
3670 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3675 result = gst_play_sink_get_last_sample (playsink);
3676 if (result != NULL && caps != NULL) {
3679 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3680 if (temp == NULL && err)
3683 gst_sample_unref (result);
3691 /* I'm really uncertain whether we should make playsink post an error
3692 * on the bus or not. It's not like it's a critical issue regarding
3693 * playsink behaviour. */
3694 GST_ERROR ("Error converting frame: %s", err->message);
3695 gst_sample_unref (result);
3702 is_raw_structure (GstStructure * s)
3706 name = gst_structure_get_name (s);
3708 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3714 is_raw_pad (GstPad * pad)
3716 GstPad *peer = gst_pad_get_peer (pad);
3718 gboolean raw = TRUE;
3723 caps = gst_pad_get_current_caps (peer);
3727 caps = gst_pad_query_caps (peer, NULL);
3729 n = gst_caps_get_size (caps);
3730 for (i = 0; i < n; i++) {
3731 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3735 } else if (raw != r) {
3736 GST_ERROR_OBJECT (pad,
3737 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3743 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3745 gst_caps_unref (caps);
3746 gst_object_unref (peer);
3751 static GstPadProbeReturn
3752 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3753 gpointer user_data);
3756 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3758 if (playsink->video_pad) {
3760 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3761 (playsink->video_pad)));
3762 if (blocked && playsink->video_block_id == 0) {
3763 playsink->video_block_id =
3764 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3765 sinkpad_blocked_cb, playsink, NULL);
3766 } else if (!blocked && playsink->video_block_id) {
3767 gst_pad_remove_probe (opad, playsink->video_block_id);
3768 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3769 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3770 playsink->video_block_id = 0;
3771 playsink->video_pad_blocked = FALSE;
3773 gst_object_unref (opad);
3778 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3780 if (playsink->audio_pad) {
3782 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3783 (playsink->audio_pad)));
3784 if (blocked && playsink->audio_block_id == 0) {
3785 playsink->audio_block_id =
3786 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3787 sinkpad_blocked_cb, playsink, NULL);
3788 } else if (!blocked && playsink->audio_block_id) {
3789 gst_pad_remove_probe (opad, playsink->audio_block_id);
3790 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3791 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3792 playsink->audio_block_id = 0;
3793 playsink->audio_pad_blocked = FALSE;
3795 gst_object_unref (opad);
3800 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3802 if (playsink->text_pad) {
3804 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3805 (playsink->text_pad)));
3806 if (blocked && playsink->text_block_id == 0) {
3807 playsink->text_block_id =
3808 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3809 sinkpad_blocked_cb, playsink, NULL);
3810 } else if (!blocked && playsink->text_block_id) {
3811 gst_pad_remove_probe (opad, playsink->text_block_id);
3812 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3813 playsink->text_block_id = 0;
3814 playsink->text_pad_blocked = FALSE;
3816 gst_object_unref (opad);
3820 static GstPadProbeReturn
3821 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3824 GstPlaySink *playsink = (GstPlaySink *) user_data;
3827 GST_PLAY_SINK_LOCK (playsink);
3829 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3830 if (pad == playsink->video_pad) {
3831 playsink->video_pad_blocked = TRUE;
3832 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3833 } else if (pad == playsink->audio_pad) {
3834 playsink->audio_pad_blocked = TRUE;
3835 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3836 } else if (pad == playsink->text_pad) {
3837 playsink->text_pad_blocked = TRUE;
3838 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3841 /* We reconfigure when for ALL streams:
3842 * * there isn't a pad
3843 * * OR the pad is blocked
3844 * * OR there are no pending blocks on that pad
3847 if ((!playsink->video_pad || playsink->video_pad_blocked
3848 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3849 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3850 && (!playsink->text_pad || playsink->text_pad_blocked
3851 || !PENDING_TEXT_BLOCK (playsink))) {
3852 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3854 if (playsink->video_pad) {
3855 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3856 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3857 playsink->video_pad_raw);
3860 if (playsink->audio_pad) {
3861 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3862 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3863 playsink->audio_pad_raw);
3866 gst_play_sink_reconfigure (playsink);
3868 video_set_blocked (playsink, FALSE);
3869 audio_set_blocked (playsink, FALSE);
3870 text_set_blocked (playsink, FALSE);
3873 gst_object_unref (pad);
3875 GST_PLAY_SINK_UNLOCK (playsink);
3877 return GST_PAD_PROBE_OK;
3881 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3883 gboolean reconfigure = FALSE;
3887 g_object_get (pad, "caps", &caps, NULL);
3891 if (pad == playsink->audio_pad) {
3892 raw = is_raw_pad (pad);
3893 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3894 && playsink->audiochain;
3895 GST_DEBUG_OBJECT (pad,
3896 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3898 } else if (pad == playsink->video_pad) {
3899 raw = is_raw_pad (pad);
3900 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3901 && playsink->videochain;
3902 GST_DEBUG_OBJECT (pad,
3903 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3907 gst_caps_unref (caps);
3910 GST_PLAY_SINK_LOCK (playsink);
3911 video_set_blocked (playsink, TRUE);
3912 audio_set_blocked (playsink, TRUE);
3913 text_set_blocked (playsink, TRUE);
3914 GST_PLAY_SINK_UNLOCK (playsink);
3919 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3920 GstPlaySinkType type)
3922 gulong *block_id = NULL;
3924 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3926 GST_PLAY_SINK_LOCK (playsink);
3927 if (pad == playsink->video_pad) {
3928 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3929 type != GST_PLAY_SINK_TYPE_VIDEO)
3931 block_id = &playsink->video_block_id;
3932 } else if (pad == playsink->audio_pad) {
3933 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3934 type != GST_PLAY_SINK_TYPE_AUDIO)
3936 block_id = &playsink->audio_block_id;
3937 } else if (pad == playsink->text_pad) {
3938 if (type != GST_PLAY_SINK_TYPE_TEXT)
3940 block_id = &playsink->text_block_id;
3943 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3945 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3948 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3949 sinkpad_blocked_cb, playsink, NULL);
3950 PENDING_FLAG_SET (playsink, type);
3951 gst_object_unref (blockpad);
3953 GST_PLAY_SINK_UNLOCK (playsink);
3960 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3962 GST_PLAY_SINK_UNLOCK (playsink);
3968 * gst_play_sink_request_pad
3969 * @playsink: a #GstPlaySink
3970 * @type: a #GstPlaySinkType
3972 * Create or return a pad of @type.
3974 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3977 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3980 gboolean created = FALSE;
3981 gboolean activate = TRUE;
3982 const gchar *pad_name = NULL;
3983 gulong *block_id = NULL;
3985 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3987 GST_PLAY_SINK_LOCK (playsink);
3989 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3990 case GST_PLAY_SINK_TYPE_AUDIO:
3991 pad_name = "audio_sink";
3992 if (!playsink->audio_tee) {
3993 GST_LOG_OBJECT (playsink, "creating tee");
3994 /* create tee when needed. This element will feed the audio sink chain
3995 * and the vis chain. */
3996 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3997 if (playsink->audio_tee == NULL) {
3998 post_missing_element_message (playsink, "tee");
3999 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4000 (_("Missing element '%s' - check your GStreamer installation."),
4005 playsink->audio_tee_sink =
4006 gst_element_get_static_pad (playsink->audio_tee, "sink");
4007 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4008 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4011 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4013 if (!playsink->audio_pad) {
4014 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4015 playsink->audio_pad =
4016 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4017 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4018 G_CALLBACK (caps_notify_cb), playsink);
4021 playsink->audio_pad_raw = FALSE;
4022 res = playsink->audio_pad;
4023 block_id = &playsink->audio_block_id;
4025 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4026 case GST_PLAY_SINK_TYPE_VIDEO:
4027 pad_name = "video_sink";
4028 if (!playsink->video_pad) {
4029 GST_LOG_OBJECT (playsink, "ghosting videosink");
4030 playsink->video_pad =
4031 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4032 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4033 G_CALLBACK (caps_notify_cb), playsink);
4036 playsink->video_pad_raw = FALSE;
4037 res = playsink->video_pad;
4038 block_id = &playsink->video_block_id;
4040 case GST_PLAY_SINK_TYPE_TEXT:
4041 GST_LOG_OBJECT (playsink, "ghosting text");
4042 if (!playsink->text_pad) {
4043 playsink->text_pad =
4044 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4047 res = playsink->text_pad;
4048 block_id = &playsink->text_block_id;
4050 case GST_PLAY_SINK_TYPE_FLUSHING:
4054 /* we need a unique padname for the flushing pad. */
4055 padname = g_strdup_printf ("flushing_%u", playsink->count);
4056 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4067 GST_PLAY_SINK_UNLOCK (playsink);
4069 if (created && res) {
4070 /* we have to add the pad when it's active or we get an error when the
4071 * element is 'running' */
4072 gst_pad_set_active (res, TRUE);
4073 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4074 if (block_id && *block_id == 0) {
4076 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4079 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4080 sinkpad_blocked_cb, playsink, NULL);
4081 PENDING_FLAG_SET (playsink, type);
4082 gst_object_unref (blockpad);
4085 gst_pad_set_active (res, activate);
4093 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4094 const gchar * name, const GstCaps * caps)
4098 GstPlaySinkType type;
4099 const gchar *tplname;
4101 g_return_val_if_fail (templ != NULL, NULL);
4103 GST_DEBUG_OBJECT (element, "name:%s", name);
4105 psink = GST_PLAY_SINK (element);
4106 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4108 /* Figure out the GstPlaySinkType based on the template */
4109 if (!strcmp (tplname, "audio_sink"))
4110 type = GST_PLAY_SINK_TYPE_AUDIO;
4111 else if (!strcmp (tplname, "audio_raw_sink"))
4112 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4113 else if (!strcmp (tplname, "video_sink"))
4114 type = GST_PLAY_SINK_TYPE_VIDEO;
4115 else if (!strcmp (tplname, "video_raw_sink"))
4116 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4117 else if (!strcmp (tplname, "text_sink"))
4118 type = GST_PLAY_SINK_TYPE_TEXT;
4120 goto unknown_template;
4122 pad = gst_play_sink_request_pad (psink, type);
4126 GST_WARNING_OBJECT (element, "Unknown pad template");
4131 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4133 GstPad **res = NULL;
4134 gboolean untarget = TRUE;
4136 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4138 GST_PLAY_SINK_LOCK (playsink);
4139 if (pad == playsink->video_pad) {
4140 res = &playsink->video_pad;
4141 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4143 } else if (pad == playsink->audio_pad) {
4144 res = &playsink->audio_pad;
4145 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4147 } else if (pad == playsink->text_pad) {
4148 res = &playsink->text_pad;
4150 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4154 GST_PLAY_SINK_UNLOCK (playsink);
4157 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4158 gst_pad_set_active (*res, FALSE);
4160 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4161 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4163 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4164 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4170 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4172 GstPlaySink *psink = GST_PLAY_SINK (element);
4174 gst_play_sink_release_pad (psink, pad);
4178 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4180 GstPlaySink *playsink;
4182 playsink = GST_PLAY_SINK_CAST (bin);
4184 switch (GST_MESSAGE_TYPE (message)) {
4185 case GST_MESSAGE_STEP_DONE:
4190 gboolean flush, intermediate, eos;
4193 GST_INFO_OBJECT (playsink, "Handling step-done message");
4194 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4195 &intermediate, &duration, &eos);
4197 if (format == GST_FORMAT_BUFFERS) {
4198 /* for the buffer format, we align the other streams */
4199 if (playsink->audiochain) {
4203 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4206 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4207 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4211 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4214 case GST_MESSAGE_ELEMENT:{
4215 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4216 GstVideoOverlay *overlay;
4218 GST_OBJECT_LOCK (playsink);
4219 if (playsink->overlay_element
4220 && GST_OBJECT_CAST (playsink->overlay_element) !=
4221 GST_MESSAGE_SRC (message)) {
4222 gst_object_unref (playsink->overlay_element);
4223 playsink->overlay_element = NULL;
4226 if (!playsink->overlay_element)
4227 playsink->overlay_element =
4228 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4230 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4231 GST_OBJECT_UNLOCK (playsink);
4233 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4235 if (playsink->overlay_handle_set)
4236 gst_video_overlay_set_window_handle (playsink->overlay_element,
4237 playsink->overlay_handle);
4238 if (playsink->overlay_handle_events_set)
4239 gst_video_overlay_handle_events (playsink->overlay_element,
4240 playsink->overlay_handle_events);
4241 if (playsink->overlay_render_rectangle_set)
4242 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4243 playsink->overlay_x, playsink->overlay_y,
4244 playsink->overlay_width, playsink->overlay_height);
4246 gst_object_unref (overlay);
4247 gst_message_unref (message);
4248 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4250 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4256 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4261 /* Send an event to our sinks until one of them works; don't then send to the
4262 * remaining sinks (unlike GstBin)
4263 * Special case: If a text sink is set we need to send the event
4264 * to them in case it's source is different from the a/v stream's source.
4267 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4269 gboolean res = TRUE;
4270 if (playsink->send_event_mode == MODE_FIRST) {
4271 if (playsink->textchain && playsink->textchain->sink) {
4272 gst_event_ref (event);
4274 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4275 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4277 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4281 if (playsink->videochain) {
4282 gst_event_ref (event);
4284 gst_element_send_event (playsink->videochain->chain.bin,
4286 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4289 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4291 if (playsink->audiochain) {
4292 gst_event_ref (event);
4294 gst_element_send_event (playsink->audiochain->chain.bin,
4296 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4299 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4303 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4304 (GST_ELEMENT_CAST (playsink), event);
4308 gst_event_unref (event);
4312 /* We only want to send the event to a single sink (overriding GstBin's
4313 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4314 * events appropriately. So, this is a messy duplication of code. */
4316 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4318 gboolean res = FALSE;
4319 GstEventType event_type = GST_EVENT_TYPE (event);
4320 GstPlaySink *playsink;
4321 playsink = GST_PLAY_SINK_CAST (element);
4322 switch (event_type) {
4323 case GST_EVENT_SEEK:
4324 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4325 res = gst_play_sink_send_event_to_sink (playsink, event);
4327 case GST_EVENT_STEP:
4332 gboolean flush, intermediate;
4333 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4335 if (format == GST_FORMAT_BUFFERS) {
4336 /* for buffers, we will try to step video frames, for other formats we
4337 * send the step to all sinks */
4338 res = gst_play_sink_send_event_to_sink (playsink, event);
4341 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4348 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4355 static GstStateChangeReturn
4356 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4358 GstStateChangeReturn ret;
4359 GstStateChangeReturn bret;
4360 GstPlaySink *playsink;
4361 playsink = GST_PLAY_SINK (element);
4362 switch (transition) {
4363 case GST_STATE_CHANGE_READY_TO_PAUSED:
4364 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4366 playsink->need_async_start = TRUE;
4367 /* we want to go async to PAUSED until we managed to configure and add the
4369 do_async_start (playsink);
4370 ret = GST_STATE_CHANGE_ASYNC;
4372 /* block all pads here */
4373 GST_PLAY_SINK_LOCK (playsink);
4374 if (playsink->video_pad && playsink->video_block_id == 0) {
4376 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4377 (playsink->video_pad)));
4378 playsink->video_block_id =
4379 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4380 sinkpad_blocked_cb, playsink, NULL);
4381 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4382 gst_object_unref (opad);
4385 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4387 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4388 (playsink->audio_pad)));
4389 playsink->audio_block_id =
4390 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4391 sinkpad_blocked_cb, playsink, NULL);
4392 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4393 gst_object_unref (opad);
4396 if (playsink->text_pad && playsink->text_block_id == 0) {
4398 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4399 (playsink->text_pad)));
4400 playsink->text_block_id =
4401 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4402 sinkpad_blocked_cb, playsink, NULL);
4403 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4404 gst_object_unref (opad);
4406 GST_PLAY_SINK_UNLOCK (playsink);
4408 case GST_STATE_CHANGE_PAUSED_TO_READY:
4409 /* unblock all pads here */
4410 GST_PLAY_SINK_LOCK (playsink);
4411 video_set_blocked (playsink, FALSE);
4412 audio_set_blocked (playsink, FALSE);
4413 text_set_blocked (playsink, FALSE);
4414 GST_PLAY_SINK_UNLOCK (playsink);
4416 case GST_STATE_CHANGE_READY_TO_NULL:
4417 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4418 /* remove our links to the mute and volume elements when they were
4419 * provided by a sink */
4420 disconnect_chain (playsink->audiochain, playsink);
4421 playsink->audiochain->volume = NULL;
4422 playsink->audiochain->mute = NULL;
4425 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4426 gst_object_unref (playsink->audiochain->ts_offset);
4427 playsink->audiochain->ts_offset = NULL;
4430 if (playsink->videochain && playsink->videochain->ts_offset) {
4431 gst_object_unref (playsink->videochain->ts_offset);
4432 playsink->videochain->ts_offset = NULL;
4435 GST_OBJECT_LOCK (playsink);
4436 if (playsink->overlay_element)
4437 gst_object_unref (playsink->overlay_element);
4438 playsink->overlay_element = NULL;
4440 if (playsink->colorbalance_element) {
4441 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4442 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4443 gst_object_unref (playsink->colorbalance_element);
4445 playsink->colorbalance_element = NULL;
4446 GST_OBJECT_UNLOCK (playsink);
4448 ret = GST_STATE_CHANGE_SUCCESS;
4451 /* all other state changes return SUCCESS by default, this value can be
4452 * overridden by the result of the children */
4453 ret = GST_STATE_CHANGE_SUCCESS;
4457 /* do the state change of the children */
4459 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4461 /* now look at the result of our children and adjust the return value */
4463 case GST_STATE_CHANGE_FAILURE:
4464 /* failure, we stop */
4465 goto activate_failed;
4466 case GST_STATE_CHANGE_NO_PREROLL:
4467 /* some child returned NO_PREROLL. This is strange but we never know. We
4468 * commit our async state change (if any) and return the NO_PREROLL */
4469 do_async_done (playsink);
4472 case GST_STATE_CHANGE_ASYNC:
4473 /* some child was async, return this */
4477 /* return our previously configured return value */
4481 switch (transition) {
4482 case GST_STATE_CHANGE_READY_TO_PAUSED:
4484 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4485 /* FIXME Release audio device when we implement that */
4486 playsink->need_async_start = TRUE;
4488 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4489 if (playsink->video_sinkpad_stream_synchronizer) {
4490 gst_element_release_request_pad (GST_ELEMENT_CAST
4491 (playsink->stream_synchronizer),
4492 playsink->video_sinkpad_stream_synchronizer);
4493 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4494 playsink->video_sinkpad_stream_synchronizer = NULL;
4495 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4496 playsink->video_srcpad_stream_synchronizer = NULL;
4498 if (playsink->audio_sinkpad_stream_synchronizer) {
4499 gst_element_release_request_pad (GST_ELEMENT_CAST
4500 (playsink->stream_synchronizer),
4501 playsink->audio_sinkpad_stream_synchronizer);
4502 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4503 playsink->audio_sinkpad_stream_synchronizer = NULL;
4504 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4505 playsink->audio_srcpad_stream_synchronizer = NULL;
4507 if (playsink->text_sinkpad_stream_synchronizer) {
4508 gst_element_release_request_pad (GST_ELEMENT_CAST
4509 (playsink->stream_synchronizer),
4510 playsink->text_sinkpad_stream_synchronizer);
4511 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4512 playsink->text_sinkpad_stream_synchronizer = NULL;
4513 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4514 playsink->text_srcpad_stream_synchronizer = NULL;
4518 case GST_STATE_CHANGE_READY_TO_NULL:
4519 /* remove sinks we added */
4520 if (playsink->videodeinterlacechain) {
4521 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4523 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4525 if (playsink->videochain) {
4526 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4527 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4529 if (playsink->audiochain) {
4530 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4531 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4533 if (playsink->vischain) {
4534 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4535 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4537 if (playsink->textchain) {
4538 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4539 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4541 do_async_done (playsink);
4542 /* when going to READY, keep elements around as long as possible,
4543 * so they may be re-used faster next time/url around.
4544 * when really going to NULL, clean up everything completely. */
4545 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4547 /* Unparent the sinks to allow reuse */
4548 if (playsink->videochain && playsink->videochain->sink)
4549 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4550 playsink->videochain->sink);
4551 if (playsink->audiochain && playsink->audiochain->sink)
4552 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4553 playsink->audiochain->sink);
4554 if (playsink->textchain && playsink->textchain->sink)
4555 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4556 playsink->textchain->sink);
4557 if (playsink->audio_sink != NULL)
4558 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4559 if (playsink->video_sink != NULL)
4560 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4561 if (playsink->visualisation != NULL)
4562 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4563 if (playsink->text_sink != NULL)
4564 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4565 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4566 playsink->videodeinterlacechain = NULL;
4567 free_chain ((GstPlayChain *) playsink->videochain);
4568 playsink->videochain = NULL;
4569 free_chain ((GstPlayChain *) playsink->audiochain);
4570 playsink->audiochain = NULL;
4571 free_chain ((GstPlayChain *) playsink->vischain);
4572 playsink->vischain = NULL;
4573 free_chain ((GstPlayChain *) playsink->textchain);
4574 playsink->textchain = NULL;
4584 GST_DEBUG_OBJECT (element,
4585 "element failed to change states -- activation problem?");
4586 return GST_STATE_CHANGE_FAILURE;
4591 gst_play_sink_set_property (GObject * object, guint prop_id,
4592 const GValue * value, GParamSpec * spec)
4594 GstPlaySink *playsink = GST_PLAY_SINK (object);
4597 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4600 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4603 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4605 case PROP_FONT_DESC:
4606 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4608 case PROP_SUBTITLE_ENCODING:
4609 gst_play_sink_set_subtitle_encoding (playsink,
4610 g_value_get_string (value));
4612 case PROP_VIS_PLUGIN:
4613 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4615 case PROP_AV_OFFSET:
4616 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4618 case PROP_VIDEO_SINK:
4619 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4620 g_value_get_object (value));
4622 case PROP_AUDIO_SINK:
4623 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4624 g_value_get_object (value));
4626 case PROP_TEXT_SINK:
4627 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4628 g_value_get_object (value));
4630 case PROP_SEND_EVENT_MODE:
4631 playsink->send_event_mode = g_value_get_enum (value);
4633 case PROP_FORCE_ASPECT_RATIO:{
4634 GstPlayVideoChain *chain;
4637 playsink->force_aspect_ratio = g_value_get_boolean (value);
4639 GST_PLAY_SINK_LOCK (playsink);
4640 if (playsink->videochain) {
4641 chain = (GstPlayVideoChain *) playsink->videochain;
4645 gst_play_sink_find_property_sinks (playsink, chain->sink,
4646 "force-aspect-ratio", G_TYPE_BOOLEAN);
4649 g_object_set (elem, "force-aspect-ratio",
4650 playsink->force_aspect_ratio, NULL);
4653 GST_PLAY_SINK_UNLOCK (playsink);
4657 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4663 gst_play_sink_get_property (GObject * object, guint prop_id,
4664 GValue * value, GParamSpec * spec)
4666 GstPlaySink *playsink = GST_PLAY_SINK (object);
4669 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4672 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4675 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4677 case PROP_FONT_DESC:
4678 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4680 case PROP_SUBTITLE_ENCODING:
4681 g_value_take_string (value,
4682 gst_play_sink_get_subtitle_encoding (playsink));
4684 case PROP_VIS_PLUGIN:
4685 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4688 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4690 case PROP_AV_OFFSET:
4691 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4693 case PROP_VIDEO_SINK:
4694 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4695 GST_PLAY_SINK_TYPE_VIDEO));
4697 case PROP_AUDIO_SINK:
4698 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4699 GST_PLAY_SINK_TYPE_AUDIO));
4701 case PROP_TEXT_SINK:
4702 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4703 GST_PLAY_SINK_TYPE_TEXT));
4705 case PROP_SEND_EVENT_MODE:
4706 g_value_set_enum (value, playsink->send_event_mode);
4708 case PROP_FORCE_ASPECT_RATIO:
4709 g_value_set_boolean (value, playsink->force_aspect_ratio);
4712 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4718 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4720 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4721 GstVideoOverlay *overlay_element;
4723 GST_OBJECT_LOCK (playsink);
4724 if (playsink->overlay_element)
4726 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4728 overlay_element = NULL;
4729 GST_OBJECT_UNLOCK (playsink);
4731 if (overlay_element) {
4732 gst_video_overlay_expose (overlay_element);
4733 gst_object_unref (overlay_element);
4738 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4739 gboolean handle_events)
4741 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4742 GstVideoOverlay *overlay_element;
4744 GST_OBJECT_LOCK (playsink);
4745 if (playsink->overlay_element)
4747 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4749 overlay_element = NULL;
4750 GST_OBJECT_UNLOCK (playsink);
4752 playsink->overlay_handle_events_set = TRUE;
4753 playsink->overlay_handle_events = handle_events;
4755 if (overlay_element) {
4756 gst_video_overlay_handle_events (overlay_element, handle_events);
4757 gst_object_unref (overlay_element);
4762 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4763 gint y, gint width, gint height)
4765 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4766 GstVideoOverlay *overlay_element;
4768 GST_OBJECT_LOCK (playsink);
4769 if (playsink->overlay_element)
4771 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4773 overlay_element = NULL;
4774 GST_OBJECT_UNLOCK (playsink);
4776 playsink->overlay_render_rectangle_set = TRUE;
4777 playsink->overlay_x = x;
4778 playsink->overlay_y = y;
4779 playsink->overlay_width = width;
4780 playsink->overlay_height = height;
4782 if (overlay_element) {
4783 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4785 gst_object_unref (overlay_element);
4790 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4793 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4794 GstVideoOverlay *overlay_element;
4796 GST_OBJECT_LOCK (playsink);
4797 if (playsink->overlay_element)
4799 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4801 overlay_element = NULL;
4802 GST_OBJECT_UNLOCK (playsink);
4804 playsink->overlay_handle_set = TRUE;
4805 playsink->overlay_handle = handle;
4807 if (overlay_element) {
4808 gst_video_overlay_set_window_handle (overlay_element, handle);
4809 gst_object_unref (overlay_element);
4814 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4816 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4817 iface->expose = gst_play_sink_overlay_expose;
4818 iface->handle_events = gst_play_sink_overlay_handle_events;
4819 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4820 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4824 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4825 GstStructure * structure)
4827 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4830 GST_PLAY_SINK_LOCK (playsink);
4831 if (playsink->videochain && playsink->videochain->chain.bin)
4832 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4833 GST_PLAY_SINK_UNLOCK (playsink);
4836 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4839 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4841 gst_object_unref (nav);
4843 GstEvent *event = gst_event_new_navigation (structure);
4845 gst_element_send_event (GST_ELEMENT (bin), event);
4848 gst_object_unref (bin);
4852 gst_structure_free (structure);
4856 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4858 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4860 iface->send_event = gst_play_sink_navigation_send_event;
4863 static const GList *
4864 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4866 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4868 return playsink->colorbalance_channels;
4872 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4873 GstColorBalanceChannel * proxy, gint value)
4875 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4878 GstColorBalance *balance_element = NULL;
4880 GST_OBJECT_LOCK (playsink);
4881 if (playsink->colorbalance_element)
4883 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4884 GST_OBJECT_UNLOCK (playsink);
4886 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4887 GstColorBalanceChannel *proxy_tmp = l->data;
4890 if (proxy_tmp != proxy)
4893 playsink->colorbalance_values[i] = value;
4895 if (balance_element) {
4896 GstColorBalanceChannel *channel = NULL;
4897 const GList *channels, *k;
4899 channels = gst_color_balance_list_channels (balance_element);
4900 for (k = channels; k; k = k->next) {
4901 GstColorBalanceChannel *tmp = l->data;
4903 if (g_strrstr (tmp->label, proxy->label)) {
4911 /* Convert to [0, 1] range */
4914 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4915 (gdouble) proxy->min_value);
4916 /* Convert to channel range */
4918 channel->min_value + new_val * ((gdouble) channel->max_value -
4919 (gdouble) channel->min_value);
4921 gst_color_balance_set_value (balance_element, channel,
4922 (gint) (new_val + 0.5));
4924 gst_object_unref (balance_element);
4927 gst_color_balance_value_changed (balance, proxy, value);
4933 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4934 GstColorBalanceChannel * proxy)
4936 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4940 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4941 GstColorBalanceChannel *proxy_tmp = l->data;
4943 if (proxy_tmp != proxy)
4946 return playsink->colorbalance_values[i];
4949 g_return_val_if_reached (0);
4952 static GstColorBalanceType
4953 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4955 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4956 GstColorBalance *balance_element = NULL;
4957 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4959 GST_OBJECT_LOCK (playsink);
4960 if (playsink->colorbalance_element)
4962 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4963 GST_OBJECT_UNLOCK (playsink);
4965 if (balance_element) {
4966 t = gst_color_balance_get_balance_type (balance_element);
4967 gst_object_unref (balance_element);
4974 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4976 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4978 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4979 iface->set_value = gst_play_sink_colorbalance_set_value;
4980 iface->get_value = gst_play_sink_colorbalance_get_value;
4981 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4985 gst_play_sink_plugin_init (GstPlugin * plugin)
4987 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4988 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4989 GST_TYPE_PLAY_SINK);