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 gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
387 static GQuark _playsink_reset_segment_event_marker_id = 0;
389 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
391 static void gst_play_sink_overlay_init (gpointer g_iface,
392 gpointer g_iface_data);
393 static void gst_play_sink_navigation_init (gpointer g_iface,
394 gpointer g_iface_data);
395 static void gst_play_sink_colorbalance_init (gpointer g_iface,
396 gpointer g_iface_data);
399 _do_init (GType type)
401 static const GInterfaceInfo svol_info = {
404 static const GInterfaceInfo ov_info = {
405 gst_play_sink_overlay_init,
408 static const GInterfaceInfo nav_info = {
409 gst_play_sink_navigation_init,
412 static const GInterfaceInfo col_info = {
413 gst_play_sink_colorbalance_init,
417 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
418 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
419 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
420 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
423 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
424 _do_init (g_define_type_id));
427 gst_play_sink_class_init (GstPlaySinkClass * klass)
429 GObjectClass *gobject_klass;
430 GstElementClass *gstelement_klass;
431 GstBinClass *gstbin_klass;
433 gobject_klass = (GObjectClass *) klass;
434 gstelement_klass = (GstElementClass *) klass;
435 gstbin_klass = (GstBinClass *) klass;
437 gobject_klass->dispose = gst_play_sink_dispose;
438 gobject_klass->finalize = gst_play_sink_finalize;
439 gobject_klass->set_property = gst_play_sink_set_property;
440 gobject_klass->get_property = gst_play_sink_get_property;
446 * Control the behaviour of playsink.
448 g_object_class_install_property (gobject_klass, PROP_FLAGS,
449 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
450 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
451 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
454 * GstPlaySink:volume:
456 * Get or set the current audio stream volume. 1.0 means 100%,
457 * 0.0 means mute. This uses a linear volume scale.
460 g_object_class_install_property (gobject_klass, PROP_VOLUME,
461 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
462 0.0, VOLUME_MAX_DOUBLE, 1.0,
463 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (gobject_klass, PROP_MUTE,
465 g_param_spec_boolean ("mute", "Mute",
466 "Mute the audio channel without changing the volume", FALSE,
467 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
468 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
469 g_param_spec_string ("subtitle-font-desc",
470 "Subtitle font description",
471 "Pango font description of font "
472 "to be used for subtitle rendering", NULL,
473 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
474 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
475 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
476 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
477 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
478 "be checked for an encoding to use. If that is not set either, "
479 "ISO-8859-15 will be assumed.", NULL,
480 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
481 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
482 g_param_spec_object ("vis-plugin", "Vis plugin",
483 "the visualization element to use (NULL = default)",
484 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
486 * GstPlaySink:sample:
488 * Get the currently rendered or prerolled sample in the video sink.
489 * The #GstCaps in the sample will describe the format of the buffer.
491 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
492 g_param_spec_boxed ("sample", "Sample",
493 "The last sample (NULL = no video available)",
494 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
496 * GstPlaySink:av-offset:
498 * Control the synchronisation offset between the audio and video streams.
499 * Positive values make the audio ahead of the video and negative values make
500 * the audio go behind the video.
504 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
505 g_param_spec_int64 ("av-offset", "AV Offset",
506 "The synchronisation offset between audio and video in nanoseconds",
507 G_MININT64, G_MAXINT64, 0,
508 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
511 * GstPlaySink:video-sink:
513 * Set the used video sink element. NULL will use the default sink. playsink
514 * must be in %GST_STATE_NULL
518 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
519 g_param_spec_object ("video-sink", "Video Sink",
520 "the video output element to use (NULL = default sink)",
521 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523 * GstPlaySink:audio-sink:
525 * Set the used audio sink element. NULL will use the default sink. playsink
526 * must be in %GST_STATE_NULL
530 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
531 g_param_spec_object ("audio-sink", "Audio Sink",
532 "the audio output element to use (NULL = default sink)",
533 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
536 * GstPlaySink:text-sink:
538 * Set the used text sink element. NULL will use the default sink. playsink
539 * must be in %GST_STATE_NULL
543 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
544 g_param_spec_object ("text-sink", "Text sink",
545 "the text output element to use (NULL = default subtitleoverlay)",
546 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
549 * GstPlaySink::send-event-mode:
551 * Sets the handling method used for events received from send_event
552 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
553 * handling (push the event to all internal sinks).
557 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
558 g_param_spec_enum ("send-event-mode", "Send event mode",
559 "How to send events received in send_event function",
560 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
561 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
564 * GstPlaySink::force-aspect-ratio:
566 * Requests the video sink to enforce the video display aspect ratio.
570 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
571 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
572 "When enabled, scaling will respect original aspect ratio", TRUE,
573 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
575 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
576 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
577 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
580 * GstPlaySink::convert-sample
581 * @playsink: a #GstPlaySink
582 * @caps: the target format of the sample
584 * Action signal to retrieve the currently playing video sample in the format
585 * specified by @caps.
586 * If @caps is %NULL, no conversion will be performed and this function is
587 * equivalent to the #GstPlaySink::sample property.
589 * Returns: a #GstSample of the current video sample converted to #caps.
590 * The caps in the sample will describe the final layout of the buffer data.
591 * %NULL is returned when no current sample can be retrieved or when the
594 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
595 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
596 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
597 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
599 gst_element_class_add_pad_template (gstelement_klass,
600 gst_static_pad_template_get (&audiorawtemplate));
601 gst_element_class_add_pad_template (gstelement_klass,
602 gst_static_pad_template_get (&audiotemplate));
603 gst_element_class_add_pad_template (gstelement_klass,
604 gst_static_pad_template_get (&videorawtemplate));
605 gst_element_class_add_pad_template (gstelement_klass,
606 gst_static_pad_template_get (&videotemplate));
607 gst_element_class_add_pad_template (gstelement_klass,
608 gst_static_pad_template_get (&texttemplate));
609 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
611 "Convenience sink for multiple streams",
612 "Wim Taymans <wim.taymans@gmail.com>");
614 gstelement_klass->change_state =
615 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
616 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
617 gstelement_klass->request_new_pad =
618 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
619 gstelement_klass->release_pad =
620 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
622 gstbin_klass->handle_message =
623 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
625 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
626 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
628 _playsink_reset_segment_event_marker_id =
629 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
633 gst_play_sink_init (GstPlaySink * playsink)
635 GstColorBalanceChannel *channel;
638 playsink->video_sink = NULL;
639 playsink->audio_sink = NULL;
640 playsink->visualisation = NULL;
641 playsink->text_sink = NULL;
642 playsink->volume = 1.0;
643 playsink->font_desc = NULL;
644 playsink->subtitle_encoding = NULL;
645 playsink->flags = DEFAULT_FLAGS;
646 playsink->send_event_mode = MODE_DEFAULT;
647 playsink->force_aspect_ratio = TRUE;
649 playsink->stream_synchronizer =
650 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
651 gst_bin_add (GST_BIN_CAST (playsink),
652 GST_ELEMENT_CAST (playsink->stream_synchronizer));
654 g_rec_mutex_init (&playsink->lock);
655 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
658 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
660 channel->label = g_strdup ("CONTRAST");
661 channel->min_value = -1000;
662 channel->max_value = 1000;
663 playsink->colorbalance_channels =
664 g_list_append (playsink->colorbalance_channels, channel);
665 playsink->colorbalance_values[0] = 0;
668 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
670 channel->label = g_strdup ("BRIGHTNESS");
671 channel->min_value = -1000;
672 channel->max_value = 1000;
673 playsink->colorbalance_channels =
674 g_list_append (playsink->colorbalance_channels, channel);
675 playsink->colorbalance_values[1] = 0;
678 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
680 channel->label = g_strdup ("HUE");
681 channel->min_value = -1000;
682 channel->max_value = 1000;
683 playsink->colorbalance_channels =
684 g_list_append (playsink->colorbalance_channels, channel);
685 playsink->colorbalance_values[2] = 0;
688 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
690 channel->label = g_strdup ("SATURATION");
691 channel->min_value = -1000;
692 channel->max_value = 1000;
693 playsink->colorbalance_channels =
694 g_list_append (playsink->colorbalance_channels, channel);
695 playsink->colorbalance_values[3] = 0;
699 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
703 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
706 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
712 free_chain (GstPlayChain * chain)
716 gst_object_unref (chain->bin);
722 gst_play_sink_dispose (GObject * object)
724 GstPlaySink *playsink;
726 playsink = GST_PLAY_SINK (object);
728 if (playsink->audio_sink != NULL) {
729 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
730 gst_object_unref (playsink->audio_sink);
731 playsink->audio_sink = NULL;
733 if (playsink->video_sink != NULL) {
734 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
735 gst_object_unref (playsink->video_sink);
736 playsink->video_sink = NULL;
738 if (playsink->visualisation != NULL) {
739 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
740 gst_object_unref (playsink->visualisation);
741 playsink->visualisation = NULL;
743 if (playsink->text_sink != NULL) {
744 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
745 gst_object_unref (playsink->text_sink);
746 playsink->text_sink = NULL;
749 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
750 playsink->videodeinterlacechain = NULL;
751 free_chain ((GstPlayChain *) playsink->videochain);
752 playsink->videochain = NULL;
753 free_chain ((GstPlayChain *) playsink->audiochain);
754 playsink->audiochain = NULL;
755 free_chain ((GstPlayChain *) playsink->vischain);
756 playsink->vischain = NULL;
757 free_chain ((GstPlayChain *) playsink->textchain);
758 playsink->textchain = NULL;
760 if (playsink->audio_tee_sink) {
761 gst_object_unref (playsink->audio_tee_sink);
762 playsink->audio_tee_sink = NULL;
765 if (playsink->audio_tee_vissrc) {
766 gst_element_release_request_pad (playsink->audio_tee,
767 playsink->audio_tee_vissrc);
768 gst_object_unref (playsink->audio_tee_vissrc);
769 playsink->audio_tee_vissrc = NULL;
772 if (playsink->audio_tee_asrc) {
773 gst_element_release_request_pad (playsink->audio_tee,
774 playsink->audio_tee_asrc);
775 gst_object_unref (playsink->audio_tee_asrc);
776 playsink->audio_tee_asrc = NULL;
779 g_free (playsink->font_desc);
780 playsink->font_desc = NULL;
782 g_free (playsink->subtitle_encoding);
783 playsink->subtitle_encoding = NULL;
785 playsink->stream_synchronizer = NULL;
787 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
789 g_list_free (playsink->colorbalance_channels);
790 playsink->colorbalance_channels = NULL;
792 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
796 gst_play_sink_finalize (GObject * object)
798 GstPlaySink *playsink;
800 playsink = GST_PLAY_SINK (object);
802 g_rec_mutex_clear (&playsink->lock);
804 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
808 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
811 GstElement **elem = NULL, *old = NULL;
813 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
815 GST_PLAY_SINK_LOCK (playsink);
817 case GST_PLAY_SINK_TYPE_AUDIO:
818 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
819 elem = &playsink->audio_sink;
821 case GST_PLAY_SINK_TYPE_VIDEO:
822 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
823 elem = &playsink->video_sink;
825 case GST_PLAY_SINK_TYPE_TEXT:
826 elem = &playsink->text_sink;
834 gst_object_ref (sink);
837 GST_PLAY_SINK_UNLOCK (playsink);
841 gst_element_set_state (old, GST_STATE_NULL);
842 gst_object_unref (old);
847 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
849 GstElement *result = NULL;
850 GstElement *elem = NULL, *chainp = NULL;
852 GST_PLAY_SINK_LOCK (playsink);
854 case GST_PLAY_SINK_TYPE_AUDIO:
855 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
857 GstPlayAudioChain *chain;
858 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
859 chainp = chain->sink;
860 elem = playsink->audio_sink;
863 case GST_PLAY_SINK_TYPE_VIDEO:
864 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
866 GstPlayVideoChain *chain;
867 if ((chain = (GstPlayVideoChain *) playsink->videochain))
868 chainp = chain->sink;
869 elem = playsink->video_sink;
872 case GST_PLAY_SINK_TYPE_TEXT:
874 GstPlayTextChain *chain;
875 if ((chain = (GstPlayTextChain *) playsink->textchain))
876 chainp = chain->sink;
877 elem = playsink->text_sink;
884 /* we have an active chain with a sink, get the sink */
885 result = gst_object_ref (chainp);
887 /* nothing found, return last configured sink */
888 if (result == NULL && elem)
889 result = gst_object_ref (elem);
890 GST_PLAY_SINK_UNLOCK (playsink);
895 static GstPadProbeReturn
896 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
899 GstPlaySink *playsink;
900 GstPlayVisChain *chain;
902 playsink = GST_PLAY_SINK (user_data);
904 GST_PLAY_SINK_LOCK (playsink);
905 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
906 /* now try to change the plugin in the running vis chain */
907 if (!(chain = (GstPlayVisChain *) playsink->vischain))
910 /* unlink the old plugin and unghost the pad */
911 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
912 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
914 /* set the old plugin to NULL and remove */
915 gst_element_set_state (chain->vis, GST_STATE_NULL);
916 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
918 /* add new plugin and set state to playing */
919 chain->vis = playsink->visualisation;
920 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
921 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
924 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
925 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
928 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
929 GST_PAD_LINK_CHECK_NOTHING);
930 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
934 GST_PLAY_SINK_UNLOCK (playsink);
936 /* remove the probe and unblock the pad */
937 return GST_PAD_PROBE_REMOVE;
941 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
943 GstPlayVisChain *chain;
945 /* setting NULL means creating the default vis plugin */
947 vis = gst_element_factory_make ("goom", "vis");
949 /* simply return if we don't have a vis plugin here */
953 GST_PLAY_SINK_LOCK (playsink);
954 /* first store the new vis */
955 if (playsink->visualisation)
956 gst_object_unref (playsink->visualisation);
958 gst_object_ref_sink (vis);
959 playsink->visualisation = vis;
961 /* now try to change the plugin in the running vis chain, if we have no chain,
962 * we don't bother, any future vis chain will be created with the new vis
964 if (!(chain = (GstPlayVisChain *) playsink->vischain))
967 /* block the pad, the next time the callback is called we can change the
968 * visualisation. It's possible that this never happens or that the pad was
969 * already blocked. If the callback never happens, we don't have new data so
970 * we don't need the new vis plugin. If the pad was already blocked, the
971 * function returns FALSE but the previous pad block will do the right thing
973 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
974 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
975 gst_play_sink_vis_blocked, playsink, NULL);
977 GST_PLAY_SINK_UNLOCK (playsink);
983 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
985 GstElement *result = NULL;
986 GstPlayVisChain *chain;
988 GST_PLAY_SINK_LOCK (playsink);
989 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
990 /* we have an active chain, get the sink */
992 result = gst_object_ref (chain->vis);
994 /* nothing found, return last configured sink */
995 if (result == NULL && playsink->visualisation)
996 result = gst_object_ref (playsink->visualisation);
997 GST_PLAY_SINK_UNLOCK (playsink);
1003 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1005 GstPlayAudioChain *chain;
1007 GST_PLAY_SINK_LOCK (playsink);
1008 playsink->volume = volume;
1009 chain = (GstPlayAudioChain *) playsink->audiochain;
1010 if (chain && chain->volume) {
1011 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
1012 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
1013 chain->mute, volume, playsink->mute);
1014 /* if there is a mute element or we are not muted, set the volume */
1015 if (chain->mute || !playsink->mute)
1016 g_object_set (chain->volume, "volume", volume, NULL);
1018 GST_LOG_OBJECT (playsink, "no volume element");
1019 playsink->volume_changed = TRUE;
1021 GST_PLAY_SINK_UNLOCK (playsink);
1025 gst_play_sink_get_volume (GstPlaySink * playsink)
1028 GstPlayAudioChain *chain;
1030 GST_PLAY_SINK_LOCK (playsink);
1031 chain = (GstPlayAudioChain *) playsink->audiochain;
1032 result = playsink->volume;
1033 if (chain && chain->volume) {
1034 if (chain->mute || !playsink->mute) {
1035 g_object_get (chain->volume, "volume", &result, NULL);
1036 playsink->volume = result;
1039 GST_PLAY_SINK_UNLOCK (playsink);
1045 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1047 GstPlayAudioChain *chain;
1049 GST_PLAY_SINK_LOCK (playsink);
1050 playsink->mute = mute;
1051 chain = (GstPlayAudioChain *) playsink->audiochain;
1054 g_object_set (chain->mute, "mute", mute, NULL);
1055 } else if (chain->volume) {
1057 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1059 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1063 playsink->mute_changed = TRUE;
1065 GST_PLAY_SINK_UNLOCK (playsink);
1069 gst_play_sink_get_mute (GstPlaySink * playsink)
1072 GstPlayAudioChain *chain;
1074 GST_PLAY_SINK_LOCK (playsink);
1075 chain = (GstPlayAudioChain *) playsink->audiochain;
1076 if (chain && chain->mute) {
1077 g_object_get (chain->mute, "mute", &result, NULL);
1078 playsink->mute = result;
1080 result = playsink->mute;
1082 GST_PLAY_SINK_UNLOCK (playsink);
1088 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1092 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1093 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1097 add_chain (GstPlayChain * chain, gboolean add)
1099 if (chain->added == add)
1103 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1105 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1106 /* we don't want to lose our sink status */
1107 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1116 activate_chain (GstPlayChain * chain, gboolean activate)
1120 if (chain->activated == activate)
1123 GST_OBJECT_LOCK (chain->playsink);
1124 state = GST_STATE_TARGET (chain->playsink);
1125 GST_OBJECT_UNLOCK (chain->playsink);
1128 gst_element_set_state (chain->bin, state);
1130 gst_element_set_state (chain->bin, GST_STATE_NULL);
1132 chain->activated = activate;
1138 element_is_sink (GstElement * element)
1142 GST_OBJECT_LOCK (element);
1143 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1144 GST_OBJECT_UNLOCK (element);
1146 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1151 element_has_property (GstElement * element, const gchar * pname, GType type)
1155 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1157 if (pspec == NULL) {
1158 GST_DEBUG_OBJECT (element, "no %s property", pname);
1162 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1163 g_type_is_a (pspec->value_type, type)) {
1164 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1165 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1169 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1170 "and we expected it to be of type %s", pname,
1171 g_type_name (pspec->value_type), g_type_name (type));
1178 const gchar *prop_name;
1181 } FindPropertyHelper;
1184 find_property (const GValue * item, FindPropertyHelper * helper)
1186 GstElement *element = g_value_get_object (item);
1187 if (helper->need_sink && !element_is_sink (element)) {
1191 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1195 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1196 (helper->need_sink) ? "sink" : "element");
1197 return 0; /* keep it */
1200 /* FIXME: why not move these functions into core? */
1201 /* find a sink in the hierarchy with a property named @name. This function does
1202 * not increase the refcount of the returned object and thus remains valid as
1203 * long as the bin is valid. */
1205 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1206 const gchar * name, GType expected_type)
1208 GstElement *result = NULL;
1211 if (element_has_property (obj, name, expected_type)) {
1213 } else if (GST_IS_BIN (obj)) {
1215 GValue item = { 0, };
1216 FindPropertyHelper helper = { name, expected_type, TRUE };
1218 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1219 found = gst_iterator_find_custom (it,
1220 (GCompareFunc) find_property, &item, &helper);
1221 gst_iterator_free (it);
1223 result = g_value_get_object (&item);
1224 /* we don't need the extra ref */
1225 g_value_unset (&item);
1231 /* find an object in the hierarchy with a property named @name */
1233 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1234 const gchar * name, GType expected_type)
1236 GstElement *result = NULL;
1239 if (GST_IS_BIN (obj)) {
1241 GValue item = { 0, };
1242 FindPropertyHelper helper = { name, expected_type, FALSE };
1244 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1245 found = gst_iterator_find_custom (it,
1246 (GCompareFunc) find_property, &item, &helper);
1247 gst_iterator_free (it);
1249 result = g_value_dup_object (&item);
1250 g_value_unset (&item);
1253 if (element_has_property (obj, name, expected_type)) {
1255 gst_object_ref (obj);
1262 do_async_start (GstPlaySink * playsink)
1264 GstMessage *message;
1266 if (!playsink->need_async_start) {
1267 GST_INFO_OBJECT (playsink, "no async_start needed");
1271 playsink->async_pending = TRUE;
1273 GST_INFO_OBJECT (playsink, "Sending async_start message");
1274 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1275 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1276 (playsink), message);
1280 do_async_done (GstPlaySink * playsink)
1282 GstMessage *message;
1284 if (playsink->async_pending) {
1285 GST_INFO_OBJECT (playsink, "Sending async_done message");
1287 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1288 GST_CLOCK_TIME_NONE);
1289 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1290 (playsink), message);
1292 playsink->async_pending = FALSE;
1295 playsink->need_async_start = FALSE;
1298 /* try to change the state of an element. This function returns the element when
1299 * the state change could be performed. When this function returns NULL an error
1300 * occured and the element is unreffed if @unref is TRUE. */
1302 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1304 GstStateChangeReturn ret;
1307 ret = gst_element_set_state (element, GST_STATE_READY);
1308 if (ret == GST_STATE_CHANGE_FAILURE) {
1309 GST_DEBUG_OBJECT (playsink, "failed state change..");
1310 gst_element_set_state (element, GST_STATE_NULL);
1312 gst_object_unref (element);
1319 /* make the element (bin) that contains the elements needed to perform
1320 * video display. Only used for *raw* video streams.
1322 * +------------------------------------------------------------+
1324 * | +-------+ +----------+ +----------+ +---------+ |
1325 * | | queue | |colorspace| |videoscale| |videosink| |
1326 * | +-sink src-sink src-sink src-sink | |
1327 * | | +-------+ +----------+ +----------+ +---------+ |
1329 * +------------------------------------------------------------+
1332 static GstPlayVideoDeinterlaceChain *
1333 gen_video_deinterlace_chain (GstPlaySink * playsink)
1335 GstPlayVideoDeinterlaceChain *chain;
1338 GstElement *head = NULL, *prev = NULL;
1340 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1341 chain->chain.playsink = playsink;
1343 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1345 /* create a bin to hold objects, as we create them we add them to this bin so
1346 * that when something goes wrong we only need to unref the bin */
1347 chain->chain.bin = gst_bin_new ("vdbin");
1348 bin = GST_BIN_CAST (chain->chain.bin);
1349 gst_object_ref_sink (bin);
1351 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1352 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1353 if (chain->conv == NULL) {
1354 post_missing_element_message (playsink, COLORSPACE);
1355 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1356 (_("Missing element '%s' - check your GStreamer installation."),
1357 COLORSPACE), ("video rendering might fail"));
1359 gst_bin_add (bin, chain->conv);
1364 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1365 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1366 if (chain->deinterlace == NULL) {
1367 post_missing_element_message (playsink, "deinterlace");
1368 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1369 (_("Missing element '%s' - check your GStreamer installation."),
1370 "deinterlace"), ("deinterlacing won't work"));
1372 gst_bin_add (bin, chain->deinterlace);
1374 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1375 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1378 head = chain->deinterlace;
1380 prev = chain->deinterlace;
1384 pad = gst_element_get_static_pad (head, "sink");
1385 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1386 gst_object_unref (pad);
1388 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1392 pad = gst_element_get_static_pad (prev, "src");
1393 chain->srcpad = gst_ghost_pad_new ("src", pad);
1394 gst_object_unref (pad);
1396 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1399 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1400 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1406 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1407 (NULL), ("Failed to configure the video deinterlace chain."));
1408 free_chain ((GstPlayChain *) chain);
1414 is_valid_color_balance_element (GstColorBalance * bal)
1416 gboolean have_brightness = FALSE;
1417 gboolean have_contrast = FALSE;
1418 gboolean have_hue = FALSE;
1419 gboolean have_saturation = FALSE;
1420 const GList *channels, *l;
1422 channels = gst_color_balance_list_channels (bal);
1423 for (l = channels; l; l = l->next) {
1424 GstColorBalanceChannel *ch = l->data;
1426 if (g_strrstr (ch->label, "BRIGHTNESS"))
1427 have_brightness = TRUE;
1428 else if (g_strrstr (ch->label, "CONTRAST"))
1429 have_contrast = TRUE;
1430 else if (g_strrstr (ch->label, "HUE"))
1432 else if (g_strrstr (ch->label, "SATURATION"))
1433 have_saturation = TRUE;
1436 return have_brightness && have_contrast && have_hue && have_saturation;
1440 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1443 GstColorBalance *cb, **cb_out = user_data;
1445 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1446 valid = is_valid_color_balance_element (cb);
1449 && gst_color_balance_get_balance_type (*cb_out) ==
1450 GST_COLOR_BALANCE_SOFTWARE) {
1451 gst_object_unref (*cb_out);
1452 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1453 } else if (!*cb_out) {
1454 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1459 static GstColorBalance *
1460 find_color_balance_element (GstElement * element)
1463 GstColorBalance *cb = NULL;
1465 if (GST_IS_COLOR_BALANCE (element)
1466 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1467 return GST_COLOR_BALANCE (gst_object_ref (element));
1468 else if (!GST_IS_BIN (element))
1471 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1472 GST_TYPE_COLOR_BALANCE);
1473 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1474 &cb) == GST_ITERATOR_RESYNC)
1475 gst_iterator_resync (it);
1476 gst_iterator_free (it);
1482 colorbalance_value_changed_cb (GstColorBalance * balance,
1483 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1488 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1489 GstColorBalanceChannel *proxy = l->data;
1491 if (g_strrstr (channel->label, proxy->label)) {
1494 /* Convert to [0, 1] range */
1497 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1498 (gdouble) channel->min_value);
1499 /* Convert to proxy range */
1501 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1502 (gdouble) proxy->min_value);
1503 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1505 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1506 playsink->colorbalance_values[i]);
1513 update_colorbalance (GstPlaySink * playsink)
1515 GstColorBalance *balance = NULL;
1519 GST_OBJECT_LOCK (playsink);
1520 if (playsink->colorbalance_element) {
1522 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1524 GST_OBJECT_UNLOCK (playsink);
1528 g_signal_handlers_block_by_func (balance,
1529 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1531 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1532 GstColorBalanceChannel *proxy = l->data;
1533 GstColorBalanceChannel *channel = NULL;
1534 const GList *channels, *k;
1536 channels = gst_color_balance_list_channels (balance);
1537 for (k = channels; k; k = k->next) {
1538 GstColorBalanceChannel *tmp = k->data;
1540 if (g_strrstr (tmp->label, proxy->label)) {
1548 gst_color_balance_set_value (balance, channel,
1549 playsink->colorbalance_values[i]);
1552 g_signal_handlers_unblock_by_func (balance,
1553 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1555 gst_object_unref (balance);
1558 /* make the element (bin) that contains the elements needed to perform
1561 * +------------------------------------------------------------+
1563 * | +-------+ +----------+ +----------+ +---------+ |
1564 * | | queue | |colorspace| |videoscale| |videosink| |
1565 * | +-sink src-sink src-sink src-sink | |
1566 * | | +-------+ +----------+ +----------+ +---------+ |
1568 * +------------------------------------------------------------+
1571 static GstPlayVideoChain *
1572 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1574 GstPlayVideoChain *chain;
1577 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1579 chain = g_new0 (GstPlayVideoChain, 1);
1580 chain->chain.playsink = playsink;
1581 chain->chain.raw = raw;
1583 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1585 if (playsink->video_sink) {
1586 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1587 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1589 /* only try fallback if no specific sink was chosen */
1590 if (chain->sink == NULL) {
1591 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1592 elem = gst_element_factory_make ("autovideosink", "videosink");
1593 chain->sink = try_element (playsink, elem, TRUE);
1595 if (chain->sink == NULL) {
1596 /* if default sink from config.h is different then try it too */
1597 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1598 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1599 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1600 chain->sink = try_element (playsink, elem, TRUE);
1604 playsink->video_sink = gst_object_ref (chain->sink);
1606 if (chain->sink == NULL)
1610 /* if we can disable async behaviour of the sink, we can avoid adding a
1611 * queue for the audio chain. */
1613 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1616 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1617 async, GST_ELEMENT_NAME (elem));
1618 g_object_set (elem, "async", async, NULL);
1619 chain->async = async;
1621 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1622 chain->async = TRUE;
1625 /* Make sure the aspect ratio is kept */
1627 gst_play_sink_find_property_sinks (playsink, chain->sink,
1628 "force-aspect-ratio", G_TYPE_BOOLEAN);
1630 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1633 /* find ts-offset element */
1634 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1635 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1638 /* create a bin to hold objects, as we create them we add them to this bin so
1639 * that when something goes wrong we only need to unref the bin */
1640 chain->chain.bin = gst_bin_new ("vbin");
1641 bin = GST_BIN_CAST (chain->chain.bin);
1642 gst_object_ref_sink (bin);
1643 gst_bin_add (bin, chain->sink);
1645 /* Get the VideoOverlay element */
1647 GstVideoOverlay *overlay = NULL;
1649 GST_OBJECT_LOCK (playsink);
1650 if (playsink->overlay_element)
1651 gst_object_unref (playsink->overlay_element);
1652 playsink->overlay_element =
1653 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1654 GST_TYPE_VIDEO_OVERLAY));
1655 if (playsink->overlay_element)
1656 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1657 GST_OBJECT_UNLOCK (playsink);
1660 if (playsink->overlay_handle_set)
1661 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1662 if (playsink->overlay_handle_events_set)
1663 gst_video_overlay_handle_events (overlay,
1664 playsink->overlay_handle_events);
1665 if (playsink->overlay_render_rectangle_set)
1666 gst_video_overlay_set_render_rectangle (overlay,
1667 playsink->overlay_x, playsink->overlay_y,
1668 playsink->overlay_width, playsink->overlay_height);
1669 gst_object_unref (overlay);
1673 /* decouple decoder from sink, this improves playback quite a lot since the
1674 * decoder can continue while the sink blocks for synchronisation. We don't
1675 * need a lot of buffers as this consumes a lot of memory and we don't want
1676 * too little because else we would be context switching too quickly. */
1677 chain->queue = gst_element_factory_make ("queue", "vqueue");
1678 if (chain->queue == NULL) {
1679 post_missing_element_message (playsink, "queue");
1680 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1681 (_("Missing element '%s' - check your GStreamer installation."),
1682 "queue"), ("video rendering might be suboptimal"));
1686 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1687 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1688 gst_bin_add (bin, chain->queue);
1689 head = prev = chain->queue;
1692 GST_OBJECT_LOCK (playsink);
1693 if (playsink->colorbalance_element) {
1694 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1695 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1696 gst_object_unref (playsink->colorbalance_element);
1698 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1699 if (playsink->colorbalance_element) {
1700 g_signal_connect (playsink->colorbalance_element, "value-changed",
1701 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1703 GST_OBJECT_UNLOCK (playsink);
1705 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1706 || (!playsink->colorbalance_element
1707 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1708 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1709 gboolean use_balance = !playsink->colorbalance_element
1710 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1712 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1714 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1715 "use-converters", use_converters, "use-balance", use_balance, NULL);
1717 GST_OBJECT_LOCK (playsink);
1718 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1719 playsink->colorbalance_element =
1720 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1721 (chain->conv)->balance));
1722 GST_OBJECT_UNLOCK (playsink);
1724 gst_bin_add (bin, chain->conv);
1726 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1727 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1735 update_colorbalance (playsink);
1738 GST_DEBUG_OBJECT (playsink, "linking to sink");
1739 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1740 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1744 pad = gst_element_get_static_pad (head, "sink");
1745 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1747 /* sending audio/video flushes break stream changes when the pipeline
1748 * is paused and played again in 0.10 */
1750 gst_pad_set_event_function (chain->sinkpad,
1751 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1752 gst_pad_set_chain_function (chain->sinkpad,
1753 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1756 gst_object_unref (pad);
1757 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1764 if (!elem && !playsink->video_sink) {
1765 post_missing_element_message (playsink, "autovideosink");
1766 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1767 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1768 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1769 (_("Both autovideosink and %s elements are missing."),
1770 DEFAULT_VIDEOSINK), (NULL));
1772 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1773 (_("The autovideosink element is missing.")), (NULL));
1776 if (playsink->video_sink) {
1777 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1778 (_("Configured videosink %s is not working."),
1779 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1780 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1781 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1782 (_("Both autovideosink and %s elements are not working."),
1783 DEFAULT_VIDEOSINK), (NULL));
1785 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1786 (_("The autovideosink element is not working.")), (NULL));
1789 free_chain ((GstPlayChain *) chain);
1795 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1796 (NULL), ("Failed to configure the video sink."));
1797 /* checking sink made it READY */
1798 gst_element_set_state (chain->sink, GST_STATE_NULL);
1799 /* Remove chain from the bin to allow reuse later */
1800 gst_bin_remove (bin, chain->sink);
1801 free_chain ((GstPlayChain *) chain);
1807 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1810 GstPlayVideoChain *chain;
1811 GstStateChangeReturn ret;
1813 chain = playsink->videochain;
1815 chain->chain.raw = raw;
1817 /* if the chain was active we don't do anything */
1818 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1821 /* try to set the sink element to READY again */
1822 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1823 if (ret == GST_STATE_CHANGE_FAILURE)
1826 /* Get the VideoOverlay element */
1828 GstVideoOverlay *overlay = NULL;
1830 GST_OBJECT_LOCK (playsink);
1831 if (playsink->overlay_element)
1832 gst_object_unref (playsink->overlay_element);
1833 playsink->overlay_element =
1834 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1835 GST_TYPE_VIDEO_OVERLAY));
1836 if (playsink->overlay_element)
1837 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1838 GST_OBJECT_UNLOCK (playsink);
1841 if (playsink->overlay_handle_set)
1842 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1843 if (playsink->overlay_handle_events_set)
1844 gst_video_overlay_handle_events (overlay,
1845 playsink->overlay_handle_events);
1846 if (playsink->overlay_render_rectangle_set)
1847 gst_video_overlay_set_render_rectangle (overlay,
1848 playsink->overlay_x, playsink->overlay_y,
1849 playsink->overlay_width, playsink->overlay_height);
1850 gst_object_unref (overlay);
1854 /* find ts-offset element */
1855 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1856 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1859 /* if we can disable async behaviour of the sink, we can avoid adding a
1860 * queue for the audio chain. */
1862 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1865 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1866 async, GST_ELEMENT_NAME (elem));
1867 g_object_set (elem, "async", async, NULL);
1868 chain->async = async;
1870 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1871 chain->async = TRUE;
1874 /* Make sure the aspect ratio is kept */
1876 gst_play_sink_find_property_sinks (playsink, chain->sink,
1877 "force-aspect-ratio", G_TYPE_BOOLEAN);
1879 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1882 GST_OBJECT_LOCK (playsink);
1883 if (playsink->colorbalance_element) {
1884 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1885 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1886 gst_object_unref (playsink->colorbalance_element);
1888 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1889 if (playsink->colorbalance_element) {
1890 g_signal_connect (playsink->colorbalance_element, "value-changed",
1891 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1893 GST_OBJECT_UNLOCK (playsink);
1896 gboolean use_balance = !playsink->colorbalance_element
1897 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1899 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1901 GST_OBJECT_LOCK (playsink);
1902 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1903 playsink->colorbalance_element =
1904 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1905 (chain->conv)->balance));
1906 GST_OBJECT_UNLOCK (playsink);
1909 update_colorbalance (playsink);
1915 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1919 GstStructure *structure;
1920 event = gst_event_new_segment (segment);
1921 structure = gst_event_writable_structure (event);
1922 gst_structure_id_set (structure,
1923 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1928 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1929 const gchar * sink_type,
1930 gboolean * sink_ignore_wrong_state,
1931 gboolean * sink_custom_flush_finished,
1932 gboolean * sink_pending_flush, GstSegment * sink_segment)
1934 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1936 const GstStructure *structure = gst_event_get_structure (event);
1938 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1939 gchar *custom_flush;
1940 gchar *custom_flush_finish;
1942 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1943 custom_flush_finish =
1944 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1945 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1946 GST_DEBUG_OBJECT (pad,
1947 "Custom %s flush event received, marking to flush %s", sink_type,
1949 GST_PLAY_SINK_LOCK (playsink);
1950 *sink_ignore_wrong_state = TRUE;
1951 *sink_custom_flush_finished = FALSE;
1952 GST_PLAY_SINK_UNLOCK (playsink);
1953 } else if (strcmp (gst_structure_get_name (structure),
1954 custom_flush_finish) == 0) {
1955 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1957 GST_PLAY_SINK_LOCK (playsink);
1958 *sink_pending_flush = TRUE;
1959 *sink_custom_flush_finished = TRUE;
1960 GST_PLAY_SINK_UNLOCK (playsink);
1963 g_free (custom_flush);
1964 g_free (custom_flush_finish);
1965 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1966 GST_PLAY_SINK_LOCK (playsink);
1967 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1969 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1970 GST_PLAY_SINK_UNLOCK (playsink);
1973 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1974 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1976 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1977 const GstSegment *segment;
1979 gst_event_parse_segment (event, &segment);
1980 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1982 GST_PLAY_SINK_LOCK (playsink);
1983 if (sink_segment->format != segment->format) {
1984 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1986 gst_format_get_name (sink_segment->format),
1987 gst_format_get_name (segment->format));
1988 gst_segment_init (sink_segment, segment->format);
1991 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1992 sink_type, sink_segment);
1993 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1994 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1995 sink_type, sink_segment);
1996 GST_PLAY_SINK_UNLOCK (playsink);
1999 gst_event_unref (event);
2000 gst_object_unref (playsink);
2004 static GstFlowReturn
2005 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2006 const gchar * sink_type,
2007 gboolean * sink_ignore_wrong_state,
2008 gboolean * sink_custom_flush_finished,
2009 gboolean * sink_pending_flush, GstSegment * sink_segment)
2011 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2012 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2015 GST_PLAY_SINK_LOCK (playsink);
2017 if (*sink_pending_flush) {
2019 GstStructure *structure;
2021 *sink_pending_flush = FALSE;
2023 GST_PLAY_SINK_UNLOCK (playsink);
2025 /* make the bin drop all cached data.
2026 * This event will be dropped on the src pad, if any. */
2027 event = gst_event_new_flush_start ();
2028 structure = gst_event_writable_structure (event);
2029 gst_structure_id_set (structure,
2030 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2032 GST_DEBUG_OBJECT (pad,
2033 "Pushing %s flush-start event with reset segment marker set: %"
2034 GST_PTR_FORMAT, sink_type, event);
2035 gst_pad_send_event (pad, event);
2037 /* make queue drop all cached data.
2038 * This event will be dropped on the src pad. */
2039 event = gst_event_new_flush_stop (TRUE);
2040 structure = gst_event_writable_structure (event);
2041 gst_structure_id_set (structure,
2042 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2044 GST_DEBUG_OBJECT (pad,
2045 "Pushing %s flush-stop event with reset segment marker set: %"
2046 GST_PTR_FORMAT, sink_type, event);
2047 gst_pad_send_event (pad, event);
2049 /* Re-sync queue segment info after flush-stop.
2050 * This event will be dropped on the src pad. */
2051 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2054 _generate_update_newsegment_event (pad, sink_segment, &event1);
2055 GST_DEBUG_OBJECT (playsink,
2056 "Pushing segment event with reset "
2057 "segment marker set: %" GST_PTR_FORMAT, event1);
2058 gst_pad_send_event (pad, event1);
2061 GST_PLAY_SINK_UNLOCK (playsink);
2064 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2066 GST_PLAY_SINK_LOCK (playsink);
2067 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2068 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2070 if (*sink_custom_flush_finished) {
2071 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2072 "wrong state for %s", sink_type);
2073 *sink_ignore_wrong_state = FALSE;
2078 GST_PLAY_SINK_UNLOCK (playsink);
2080 gst_object_unref (playsink);
2081 gst_object_unref (tbin);
2085 /* sending audio/video flushes break stream changes when the pipeline
2086 * is paused and played again in 0.10 */
2089 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2091 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2092 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2095 ret = gst_play_sink_sink_event (pad, event, "video",
2096 &playsink->video_ignore_wrong_state,
2097 &playsink->video_custom_flush_finished,
2098 &playsink->video_pending_flush, &playsink->video_segment);
2100 gst_object_unref (playsink);
2101 gst_object_unref (tbin);
2105 static GstFlowReturn
2106 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2108 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2109 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2112 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2113 &playsink->video_ignore_wrong_state,
2114 &playsink->video_custom_flush_finished,
2115 &playsink->video_pending_flush, &playsink->video_segment);
2117 gst_object_unref (playsink);
2118 gst_object_unref (tbin);
2123 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2125 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2126 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2129 ret = gst_play_sink_sink_event (pad, event, "audio",
2130 &playsink->audio_ignore_wrong_state,
2131 &playsink->audio_custom_flush_finished,
2132 &playsink->audio_pending_flush, &playsink->audio_segment);
2134 gst_object_unref (playsink);
2135 gst_object_unref (tbin);
2139 static GstFlowReturn
2140 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2142 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2143 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2146 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2147 &playsink->audio_ignore_wrong_state,
2148 &playsink->audio_custom_flush_finished,
2149 &playsink->audio_pending_flush, &playsink->audio_segment);
2151 gst_object_unref (playsink);
2152 gst_object_unref (tbin);
2158 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2161 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2164 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2165 &playsink->text_ignore_wrong_state,
2166 &playsink->text_custom_flush_finished,
2167 &playsink->text_pending_flush, &playsink->text_segment);
2169 gst_object_unref (playsink);
2174 static GstFlowReturn
2175 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2179 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2181 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2182 &playsink->text_ignore_wrong_state,
2183 &playsink->text_custom_flush_finished,
2184 &playsink->text_pending_flush, &playsink->text_segment);
2186 gst_object_unref (playsink);
2191 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2195 const GstStructure *structure;
2197 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2199 structure = gst_event_get_structure (event);
2202 gst_structure_id_has_field (structure,
2203 _playsink_reset_segment_event_marker_id)) {
2204 /* the events marked with a reset segment marker
2205 * are sent internally to reset the queue and
2206 * must be dropped here */
2207 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2208 "segment marker set: %" GST_PTR_FORMAT, event);
2213 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2216 gst_event_unref (event);
2220 /* make an element for playback of video with subtitles embedded.
2221 * Only used for *raw* video streams.
2223 * +--------------------------------------------+
2225 * | +--------+ +-----------------+ |
2226 * | | queue | | subtitleoverlay | |
2227 * video--src sink---video_sink | |
2228 * | +--------+ | src--src
2229 * text------------------text_sink | |
2230 * | +-----------------+ |
2231 * +--------------------------------------------+
2234 static GstPlayTextChain *
2235 gen_text_chain (GstPlaySink * playsink)
2237 GstPlayTextChain *chain;
2240 GstPad *videosinkpad, *textsinkpad, *srcpad;
2242 chain = g_new0 (GstPlayTextChain, 1);
2243 chain->chain.playsink = playsink;
2245 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2247 chain->chain.bin = gst_bin_new ("tbin");
2248 bin = GST_BIN_CAST (chain->chain.bin);
2249 gst_object_ref_sink (bin);
2251 videosinkpad = textsinkpad = srcpad = NULL;
2253 /* first try to hook the text pad to the custom sink */
2254 if (playsink->text_sink) {
2255 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2256 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2259 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2262 /* make sure the sparse subtitles don't participate in the preroll */
2263 g_object_set (elem, "async", FALSE, NULL);
2264 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2265 gst_bin_add (bin, chain->sink);
2266 /* NOTE streamsynchronizer needs streams decoupled */
2267 /* make a little queue */
2268 chain->queue = gst_element_factory_make ("queue", "subqueue");
2269 if (chain->queue == NULL) {
2270 post_missing_element_message (playsink, "queue");
2271 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2272 (_("Missing element '%s' - check your GStreamer installation."),
2273 "queue"), ("rendering might be suboptimal"));
2275 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2276 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2277 "silent", TRUE, NULL);
2278 gst_bin_add (bin, chain->queue);
2280 /* we have a custom sink, this will be our textsinkpad */
2281 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2282 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2283 /* we're all fine now and we can add the sink to the chain */
2284 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2285 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2287 GST_WARNING_OBJECT (playsink,
2288 "can't find a sink pad on custom text sink");
2289 gst_bin_remove (bin, chain->sink);
2290 gst_bin_remove (bin, chain->queue);
2292 chain->queue = NULL;
2294 /* try to set sync to true but it's no biggie when we can't */
2295 if (chain->sink && (elem =
2296 gst_play_sink_find_property_sinks (playsink, chain->sink,
2297 "sync", G_TYPE_BOOLEAN)))
2298 g_object_set (elem, "sync", TRUE, NULL);
2301 gst_bin_remove (bin, chain->sink);
2303 GST_WARNING_OBJECT (playsink,
2304 "can't find async property in custom text sink");
2307 if (textsinkpad == NULL) {
2308 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2309 (_("Custom text sink element is not usable.")),
2310 ("fallback to default subtitleoverlay"));
2314 if (textsinkpad == NULL) {
2315 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2316 /* make a little queue */
2317 chain->queue = gst_element_factory_make ("queue", "vqueue");
2318 if (chain->queue == NULL) {
2319 post_missing_element_message (playsink, "queue");
2320 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2321 (_("Missing element '%s' - check your GStreamer installation."),
2322 "queue"), ("video rendering might be suboptimal"));
2324 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2325 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2326 "silent", TRUE, NULL);
2327 gst_bin_add (bin, chain->queue);
2328 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2332 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2333 if (chain->overlay == NULL) {
2334 post_missing_element_message (playsink, "subtitleoverlay");
2335 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2336 (_("Missing element '%s' - check your GStreamer installation."),
2337 "subtitleoverlay"), ("subtitle rendering disabled"));
2339 GstElement *element;
2341 gst_bin_add (bin, chain->overlay);
2343 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2344 if (playsink->font_desc) {
2345 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2346 playsink->font_desc, NULL);
2348 if (playsink->subtitle_encoding) {
2349 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2350 playsink->subtitle_encoding, NULL);
2353 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2354 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2356 /* make another little queue to decouple streams */
2357 element = gst_element_factory_make ("queue", "subqueue");
2358 if (element == NULL) {
2359 post_missing_element_message (playsink, "queue");
2360 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2361 (_("Missing element '%s' - check your GStreamer installation."),
2362 "queue"), ("rendering might be suboptimal"));
2364 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2365 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2366 "silent", TRUE, NULL);
2367 gst_bin_add (bin, element);
2368 if (gst_element_link_pads_full (element, "src", chain->overlay,
2369 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2370 textsinkpad = gst_element_get_static_pad (element, "sink");
2371 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2373 gst_bin_remove (bin, chain->sink);
2374 gst_bin_remove (bin, chain->overlay);
2376 chain->overlay = NULL;
2377 gst_object_unref (videosinkpad);
2378 videosinkpad = NULL;
2385 if (videosinkpad == NULL) {
2386 /* if we still don't have a videosink, we don't have an overlay. the only
2387 * thing we can do is insert an identity and ghost the src
2389 chain->identity = gst_element_factory_make ("identity", "tidentity");
2390 if (chain->identity == NULL) {
2391 post_missing_element_message (playsink, "identity");
2392 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2393 (_("Missing element '%s' - check your GStreamer installation."),
2394 "identity"), (NULL));
2396 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2397 g_object_set (chain->identity, "silent", TRUE, NULL);
2398 gst_bin_add (bin, chain->identity);
2399 srcpad = gst_element_get_static_pad (chain->identity, "src");
2400 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2404 /* expose the ghostpads */
2406 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2407 gst_object_unref (videosinkpad);
2408 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2411 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2412 gst_object_unref (textsinkpad);
2414 gst_pad_set_event_function (chain->textsinkpad,
2415 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2416 gst_pad_set_chain_function (chain->textsinkpad,
2417 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2419 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2422 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2423 gst_object_unref (srcpad);
2425 gst_pad_set_event_function (chain->srcpad,
2426 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2428 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2435 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2439 g_object_get (object, "volume", &vol, NULL);
2440 playsink->volume = vol;
2442 g_object_notify (G_OBJECT (playsink), "volume");
2446 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2450 g_object_get (object, "mute", &mute, NULL);
2451 playsink->mute = mute;
2453 g_object_notify (G_OBJECT (playsink), "mute");
2456 /* make the chain that contains the elements needed to perform
2459 * We add a tee as the first element so that we can link the visualisation chain
2460 * to it when requested.
2462 * +-------------------------------------------------------------+
2464 * | +---------+ +----------+ +---------+ +---------+ |
2465 * | |audioconv| |audioscale| | volume | |audiosink| |
2466 * | +-srck src-sink src-sink src-sink | |
2467 * | | +---------+ +----------+ +---------+ +---------+ |
2469 * +-------------------------------------------------------------+
2471 static GstPlayAudioChain *
2472 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2474 GstPlayAudioChain *chain;
2476 gboolean have_volume;
2478 GstElement *head, *prev, *elem = NULL;
2480 chain = g_new0 (GstPlayAudioChain, 1);
2481 chain->chain.playsink = playsink;
2482 chain->chain.raw = raw;
2484 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2486 if (playsink->audio_sink) {
2487 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2488 playsink->audio_sink);
2489 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2491 /* only try fallback if no specific sink was chosen */
2492 if (chain->sink == NULL) {
2493 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2494 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2495 chain->sink = try_element (playsink, elem, TRUE);
2497 if (chain->sink == NULL) {
2498 /* if default sink from config.h is different then try it too */
2499 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2500 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2501 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2502 chain->sink = try_element (playsink, elem, TRUE);
2506 playsink->audio_sink = gst_object_ref (chain->sink);
2508 if (chain->sink == NULL)
2511 chain->chain.bin = gst_bin_new ("abin");
2512 bin = GST_BIN_CAST (chain->chain.bin);
2513 gst_object_ref_sink (bin);
2514 gst_bin_add (bin, chain->sink);
2516 /* we have to add a queue when we need to decouple for the video sink in
2517 * visualisations and for streamsynchronizer */
2518 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2519 chain->queue = gst_element_factory_make ("queue", "aqueue");
2520 if (chain->queue == NULL) {
2521 post_missing_element_message (playsink, "queue");
2522 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2523 (_("Missing element '%s' - check your GStreamer installation."),
2524 "queue"), ("audio playback and visualizations might not work"));
2528 g_object_set (chain->queue, "silent", TRUE, NULL);
2529 gst_bin_add (bin, chain->queue);
2530 prev = head = chain->queue;
2533 /* find ts-offset element */
2534 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2535 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2538 /* check if the sink, or something within the sink, has the volume property.
2539 * If it does we don't need to add a volume element. */
2541 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2544 chain->volume = elem;
2546 g_signal_connect (chain->volume, "notify::volume",
2547 G_CALLBACK (notify_volume_cb), playsink);
2549 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2551 chain->sink_volume = TRUE;
2552 /* if the sink also has a mute property we can use this as well. We'll only
2553 * use the mute property if there is a volume property. We can simulate the
2554 * mute with the volume otherwise. */
2556 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2559 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2560 g_signal_connect (chain->mute, "notify::mute",
2561 G_CALLBACK (notify_mute_cb), playsink);
2563 /* use the sink to control the volume and mute */
2564 if (playsink->volume_changed) {
2565 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2566 playsink->volume_changed = FALSE;
2568 if (playsink->mute_changed) {
2570 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2573 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2575 playsink->mute_changed = FALSE;
2578 /* no volume, we need to add a volume element when we can */
2579 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2580 have_volume = FALSE;
2581 chain->sink_volume = FALSE;
2584 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2585 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2586 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2587 gboolean use_volume =
2588 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2589 GST_DEBUG_OBJECT (playsink,
2590 "creating audioconvert with use-converters %d, use-volume %d",
2591 use_converters, use_volume);
2593 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2594 "use-converters", use_converters, "use-volume", use_volume, NULL);
2595 gst_bin_add (bin, chain->conv);
2597 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2598 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2605 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2606 GstPlaySinkAudioConvert *conv =
2607 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2610 chain->volume = conv->volume;
2613 g_signal_connect (chain->volume, "notify::volume",
2614 G_CALLBACK (notify_volume_cb), playsink);
2616 /* volume also has the mute property */
2617 chain->mute = chain->volume;
2618 g_signal_connect (chain->mute, "notify::mute",
2619 G_CALLBACK (notify_mute_cb), playsink);
2621 /* configure with the latest volume and mute */
2622 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2624 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2630 /* we only have to link to the previous element if we have something in
2631 * front of the sink */
2632 GST_DEBUG_OBJECT (playsink, "linking to sink");
2633 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2634 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2638 /* post a warning if we have no way to configure the volume */
2640 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2641 (_("No volume control found")), ("Volume/mute is not available"));
2644 /* and ghost the sinkpad of the headmost element */
2645 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2646 pad = gst_element_get_static_pad (head, "sink");
2647 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2649 /* sending audio/video flushes break stream changes when the pipeline
2650 * is paused and played again in 0.10 */
2652 gst_pad_set_event_function (chain->sinkpad,
2653 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2654 gst_pad_set_chain_function (chain->sinkpad,
2655 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2658 gst_object_unref (pad);
2659 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2666 if (!elem && !playsink->audio_sink) {
2667 post_missing_element_message (playsink, "autoaudiosink");
2668 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2669 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2670 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2671 (_("Both autoaudiosink and %s elements are missing."),
2672 DEFAULT_AUDIOSINK), (NULL));
2674 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2675 (_("The autoaudiosink element is missing.")), (NULL));
2678 if (playsink->audio_sink) {
2679 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2680 (_("Configured audiosink %s is not working."),
2681 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2682 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2683 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2684 (_("Both autoaudiosink and %s elements are not working."),
2685 DEFAULT_AUDIOSINK), (NULL));
2687 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2688 (_("The autoaudiosink element is not working.")), (NULL));
2691 free_chain ((GstPlayChain *) chain);
2696 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2697 (NULL), ("Failed to configure the audio sink."));
2698 /* checking sink made it READY */
2699 gst_element_set_state (chain->sink, GST_STATE_NULL);
2700 /* Remove chain from the bin to allow reuse later */
2701 gst_bin_remove (bin, chain->sink);
2702 free_chain ((GstPlayChain *) chain);
2708 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2711 GstPlayAudioChain *chain;
2712 GstStateChangeReturn ret;
2713 GstPlaySinkAudioConvert *conv;
2715 chain = playsink->audiochain;
2716 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2718 chain->chain.raw = raw;
2720 /* if the chain was active we don't do anything */
2721 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2724 /* try to set the sink element to READY again */
2725 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2726 if (ret == GST_STATE_CHANGE_FAILURE)
2729 /* find ts-offset element */
2730 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2731 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2734 /* check if the sink, or something within the sink, has the volume property.
2735 * If it does we don't need to add a volume element. */
2737 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2740 chain->volume = elem;
2742 if (playsink->volume_changed) {
2743 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2745 /* use the sink to control the volume */
2746 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2747 playsink->volume_changed = FALSE;
2750 g_signal_connect (chain->volume, "notify::volume",
2751 G_CALLBACK (notify_volume_cb), playsink);
2752 /* if the sink also has a mute property we can use this as well. We'll only
2753 * use the mute property if there is a volume property. We can simulate the
2754 * mute with the volume otherwise. */
2756 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2759 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2760 g_signal_connect (chain->mute, "notify::mute",
2761 G_CALLBACK (notify_mute_cb), playsink);
2764 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2766 /* no volume, we need to add a volume element when we can */
2767 g_object_set (chain->conv, "use-volume",
2768 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2769 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2771 /* Disconnect signals */
2772 disconnect_chain (chain, playsink);
2774 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2775 chain->volume = conv->volume;
2776 chain->mute = chain->volume;
2778 g_signal_connect (chain->volume, "notify::volume",
2779 G_CALLBACK (notify_volume_cb), playsink);
2781 g_signal_connect (chain->mute, "notify::mute",
2782 G_CALLBACK (notify_mute_cb), playsink);
2784 /* configure with the latest volume and mute */
2785 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2786 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2789 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2795 * +-------------------------------------------------------------------+
2797 * | +----------+ +------------+ +----------+ +-------+ |
2798 * | | visqueue | | audioconv | | audiores | | vis | |
2799 * | +-sink src-sink + samp src-sink src-sink src-+ |
2800 * | | +----------+ +------------+ +----------+ +-------+ | |
2802 * +-------------------------------------------------------------------+
2805 static GstPlayVisChain *
2806 gen_vis_chain (GstPlaySink * playsink)
2808 GstPlayVisChain *chain;
2814 chain = g_new0 (GstPlayVisChain, 1);
2815 chain->chain.playsink = playsink;
2817 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2819 chain->chain.bin = gst_bin_new ("visbin");
2820 bin = GST_BIN_CAST (chain->chain.bin);
2821 gst_object_ref_sink (bin);
2823 /* we're queuing raw audio here, we can remove this queue when we can disable
2824 * async behaviour in the video sink. */
2825 chain->queue = gst_element_factory_make ("queue", "visqueue");
2826 if (chain->queue == NULL)
2828 g_object_set (chain->queue, "silent", TRUE, NULL);
2829 gst_bin_add (bin, chain->queue);
2831 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2832 if (chain->conv == NULL)
2833 goto no_audioconvert;
2834 gst_bin_add (bin, chain->conv);
2836 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2837 if (chain->resample == NULL)
2838 goto no_audioresample;
2839 gst_bin_add (bin, chain->resample);
2841 /* this pad will be used for blocking the dataflow and switching the vis
2843 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2845 if (playsink->visualisation) {
2846 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2847 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2849 if (chain->vis == NULL) {
2850 GST_DEBUG_OBJECT (playsink, "trying goom");
2851 elem = gst_element_factory_make ("goom", "vis");
2852 chain->vis = try_element (playsink, elem, TRUE);
2854 if (chain->vis == NULL)
2857 gst_bin_add (bin, chain->vis);
2859 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2860 GST_PAD_LINK_CHECK_NOTHING);
2862 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2863 GST_PAD_LINK_CHECK_NOTHING);
2865 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2866 GST_PAD_LINK_CHECK_NOTHING);
2870 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2871 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2873 pad = gst_element_get_static_pad (chain->queue, "sink");
2874 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2875 gst_object_unref (pad);
2876 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2878 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2879 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2886 post_missing_element_message (playsink, "queue");
2887 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2888 (_("Missing element '%s' - check your GStreamer installation."),
2890 free_chain ((GstPlayChain *) chain);
2895 post_missing_element_message (playsink, "audioconvert");
2896 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2897 (_("Missing element '%s' - check your GStreamer installation."),
2898 "audioconvert"), ("possibly a liboil version mismatch?"));
2899 free_chain ((GstPlayChain *) chain);
2904 post_missing_element_message (playsink, "audioresample");
2905 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2906 (_("Missing element '%s' - check your GStreamer installation."),
2907 "audioresample"), (NULL));
2908 free_chain ((GstPlayChain *) chain);
2913 post_missing_element_message (playsink, "goom");
2914 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2915 (_("Missing element '%s' - check your GStreamer installation."),
2917 free_chain ((GstPlayChain *) chain);
2922 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2923 (NULL), ("Failed to configure the visualisation element."));
2924 /* element made it to READY */
2925 gst_element_set_state (chain->vis, GST_STATE_NULL);
2926 free_chain ((GstPlayChain *) chain);
2931 /* this function is called when all the request pads are requested and when we
2932 * have to construct the final pipeline. Based on the flags we construct the
2933 * final output pipelines.
2936 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
2939 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2941 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2943 /* assume we need nothing */
2944 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2946 GST_PLAY_SINK_LOCK (playsink);
2947 GST_OBJECT_LOCK (playsink);
2948 /* get flags, there are protected with the object lock */
2949 flags = playsink->flags;
2950 GST_OBJECT_UNLOCK (playsink);
2952 /* figure out which components we need */
2953 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2954 /* we have subtitles and we are requested to show it */
2958 if (((flags & GST_PLAY_FLAG_VIDEO)
2959 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2960 /* we have video and we are requested to show it */
2963 /* we only deinterlace if native video is not requested and
2964 * we have raw video */
2965 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2966 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2967 need_deinterlace = TRUE;
2970 if (playsink->audio_pad) {
2971 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2974 if (playsink->audio_pad_raw) {
2975 /* only can do vis with raw uncompressed audio */
2976 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2977 /* also add video when we add visualisation */
2984 /* we have a text_pad and we need text rendering, in this case we need a
2985 * video_pad to combine the video with the text or visualizations */
2986 if (need_text && !need_video && !playsink->text_sink) {
2987 if (playsink->video_pad) {
2989 } else if (need_audio) {
2990 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2991 (_("Can't play a text file without video or visualizations.")),
2992 ("Have text pad but no video pad or visualizations"));
2995 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2996 (_("Can't play a text file without video or visualizations.")),
2997 ("Have text pad but no video pad or visualizations"));
2998 GST_PLAY_SINK_UNLOCK (playsink);
3003 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3004 need_video, need_vis, need_text);
3006 /* set up video pipeline */
3008 gboolean raw, async;
3010 /* we need a raw sink when we do vis or when we have a raw pad */
3011 raw = need_vis ? TRUE : playsink->video_pad_raw;
3012 /* we try to set the sink async=FALSE when we need vis, this way we can
3013 * avoid a queue in the audio chain. */
3016 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3017 playsink->video_pad_raw);
3019 if (playsink->videochain) {
3020 /* try to reactivate the chain */
3021 if (!setup_video_chain (playsink, raw, async)) {
3022 if (playsink->video_sinkpad_stream_synchronizer) {
3023 gst_element_release_request_pad (GST_ELEMENT_CAST
3024 (playsink->stream_synchronizer),
3025 playsink->video_sinkpad_stream_synchronizer);
3026 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3027 playsink->video_sinkpad_stream_synchronizer = NULL;
3028 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3029 playsink->video_srcpad_stream_synchronizer = NULL;
3032 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3034 /* Remove the sink from the bin to keep its state
3035 * and unparent it to allow reuse */
3036 if (playsink->videochain->sink)
3037 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3038 playsink->videochain->sink);
3040 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3041 free_chain ((GstPlayChain *) playsink->videochain);
3042 playsink->videochain = NULL;
3044 GST_OBJECT_LOCK (playsink);
3045 if (playsink->overlay_element)
3046 gst_object_unref (playsink->overlay_element);
3047 playsink->overlay_element = NULL;
3049 if (playsink->colorbalance_element) {
3050 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3051 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3052 gst_object_unref (playsink->colorbalance_element);
3054 playsink->colorbalance_element = NULL;
3055 GST_OBJECT_UNLOCK (playsink);
3059 if (!playsink->videochain)
3060 playsink->videochain = gen_video_chain (playsink, raw, async);
3061 if (!playsink->videochain)
3064 if (!playsink->video_sinkpad_stream_synchronizer) {
3065 GValue item = { 0, };
3068 playsink->video_sinkpad_stream_synchronizer =
3069 gst_element_get_request_pad (GST_ELEMENT_CAST
3070 (playsink->stream_synchronizer), "sink_%u");
3071 it = gst_pad_iterate_internal_links
3072 (playsink->video_sinkpad_stream_synchronizer);
3074 gst_iterator_next (it, &item);
3075 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3076 g_value_unset (&item);
3077 g_assert (playsink->video_srcpad_stream_synchronizer);
3078 gst_iterator_free (it);
3081 if (playsink->video_pad)
3082 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3083 playsink->video_sinkpad_stream_synchronizer);
3085 if (need_deinterlace) {
3086 if (!playsink->videodeinterlacechain)
3087 playsink->videodeinterlacechain =
3088 gen_video_deinterlace_chain (playsink);
3089 if (!playsink->videodeinterlacechain)
3092 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3094 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3096 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3097 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3099 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3100 playsink->videochain->sinkpad);
3101 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3102 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3104 if (playsink->videodeinterlacechain) {
3105 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3106 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3111 GST_DEBUG_OBJECT (playsink, "adding video chain");
3112 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3113 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3114 /* if we are not part of vis or subtitles, set the ghostpad target */
3115 if (!need_vis && !need_text && (!playsink->textchain
3116 || !playsink->text_pad)) {
3117 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3118 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3119 playsink->videochain->sinkpad);
3120 if (playsink->videodeinterlacechain
3121 && playsink->videodeinterlacechain->srcpad)
3122 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3123 playsink->videochain->sinkpad);
3124 if (need_deinterlace)
3125 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3126 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3128 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3129 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3132 GST_DEBUG_OBJECT (playsink, "no video needed");
3133 if (playsink->videochain) {
3134 GST_DEBUG_OBJECT (playsink, "removing video chain");
3135 if (playsink->vischain) {
3138 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3140 /* also had visualisation, release the tee srcpad before we then
3141 * unlink the video from it */
3142 if (playsink->audio_tee_vissrc) {
3143 gst_element_release_request_pad (playsink->audio_tee,
3144 playsink->audio_tee_vissrc);
3145 gst_object_unref (playsink->audio_tee_vissrc);
3146 playsink->audio_tee_vissrc = NULL;
3149 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3150 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3153 if (playsink->video_sinkpad_stream_synchronizer) {
3154 gst_element_release_request_pad (GST_ELEMENT_CAST
3155 (playsink->stream_synchronizer),
3156 playsink->video_sinkpad_stream_synchronizer);
3157 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3158 playsink->video_sinkpad_stream_synchronizer = NULL;
3159 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3160 playsink->video_srcpad_stream_synchronizer = NULL;
3163 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3164 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3165 if (playsink->videochain->ts_offset)
3166 gst_object_unref (playsink->videochain->ts_offset);
3167 playsink->videochain->ts_offset = NULL;
3170 if (playsink->videodeinterlacechain) {
3171 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3172 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3175 if (playsink->video_pad)
3176 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3178 GST_OBJECT_LOCK (playsink);
3179 if (playsink->overlay_element)
3180 gst_object_unref (playsink->overlay_element);
3181 playsink->overlay_element = NULL;
3183 if (playsink->colorbalance_element) {
3184 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
3185 G_CALLBACK (colorbalance_value_changed_cb), playsink);
3186 gst_object_unref (playsink->colorbalance_element);
3188 playsink->colorbalance_element = NULL;
3189 GST_OBJECT_UNLOCK (playsink);
3196 GST_DEBUG_OBJECT (playsink, "adding audio");
3198 /* get a raw sink if we are asked for a raw pad */
3199 raw = playsink->audio_pad_raw;
3201 if (playsink->audiochain) {
3202 /* try to reactivate the chain */
3203 if (!setup_audio_chain (playsink, raw)) {
3204 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3205 if (playsink->audio_tee_asrc) {
3206 gst_element_release_request_pad (playsink->audio_tee,
3207 playsink->audio_tee_asrc);
3208 gst_object_unref (playsink->audio_tee_asrc);
3209 playsink->audio_tee_asrc = NULL;
3212 if (playsink->audio_sinkpad_stream_synchronizer) {
3213 gst_element_release_request_pad (GST_ELEMENT_CAST
3214 (playsink->stream_synchronizer),
3215 playsink->audio_sinkpad_stream_synchronizer);
3216 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3217 playsink->audio_sinkpad_stream_synchronizer = NULL;
3218 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3219 playsink->audio_srcpad_stream_synchronizer = NULL;
3222 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3224 /* Remove the sink from the bin to keep its state
3225 * and unparent it to allow reuse */
3226 if (playsink->audiochain->sink)
3227 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3228 playsink->audiochain->sink);
3230 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3231 disconnect_chain (playsink->audiochain, playsink);
3232 playsink->audiochain->volume = NULL;
3233 playsink->audiochain->mute = NULL;
3234 if (playsink->audiochain->ts_offset)
3235 gst_object_unref (playsink->audiochain->ts_offset);
3236 playsink->audiochain->ts_offset = NULL;
3237 free_chain ((GstPlayChain *) playsink->audiochain);
3238 playsink->audiochain = NULL;
3239 playsink->volume_changed = playsink->mute_changed = FALSE;
3243 if (!playsink->audiochain) {
3244 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3245 playsink->audiochain = gen_audio_chain (playsink, raw);
3248 if (!playsink->audio_sinkpad_stream_synchronizer) {
3249 GValue item = { 0, };
3252 playsink->audio_sinkpad_stream_synchronizer =
3253 gst_element_get_request_pad (GST_ELEMENT_CAST
3254 (playsink->stream_synchronizer), "sink_%u");
3255 it = gst_pad_iterate_internal_links
3256 (playsink->audio_sinkpad_stream_synchronizer);
3258 gst_iterator_next (it, &item);
3259 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3260 g_value_unset (&item);
3261 g_assert (playsink->audio_srcpad_stream_synchronizer);
3262 gst_iterator_free (it);
3265 if (playsink->audiochain) {
3266 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3267 if (playsink->audio_tee_asrc == NULL) {
3268 playsink->audio_tee_asrc =
3269 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3271 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3272 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3273 gst_pad_link_full (playsink->audio_tee_asrc,
3274 playsink->audio_sinkpad_stream_synchronizer,
3275 GST_PAD_LINK_CHECK_NOTHING);
3276 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3277 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3280 GST_DEBUG_OBJECT (playsink, "no audio needed");
3281 /* we have no audio or we are requested to not play audio */
3282 if (playsink->audiochain) {
3283 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3284 /* release the audio pad */
3285 if (playsink->audio_tee_asrc) {
3286 gst_element_release_request_pad (playsink->audio_tee,
3287 playsink->audio_tee_asrc);
3288 gst_object_unref (playsink->audio_tee_asrc);
3289 playsink->audio_tee_asrc = NULL;
3292 if (playsink->audio_sinkpad_stream_synchronizer) {
3293 gst_element_release_request_pad (GST_ELEMENT_CAST
3294 (playsink->stream_synchronizer),
3295 playsink->audio_sinkpad_stream_synchronizer);
3296 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3297 playsink->audio_sinkpad_stream_synchronizer = NULL;
3298 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3299 playsink->audio_srcpad_stream_synchronizer = NULL;
3302 if (playsink->audiochain->sink_volume) {
3303 disconnect_chain (playsink->audiochain, playsink);
3304 playsink->audiochain->volume = NULL;
3305 playsink->audiochain->mute = NULL;
3306 if (playsink->audiochain->ts_offset)
3307 gst_object_unref (playsink->audiochain->ts_offset);
3308 playsink->audiochain->ts_offset = NULL;
3310 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3311 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3318 if (!playsink->vischain)
3319 playsink->vischain = gen_vis_chain (playsink);
3321 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3323 if (playsink->vischain) {
3324 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3326 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3327 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3328 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3329 if (playsink->audio_tee_vissrc == NULL) {
3330 playsink->audio_tee_vissrc =
3331 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3333 gst_pad_link_full (playsink->audio_tee_vissrc,
3334 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3335 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3336 GST_PAD_LINK_CHECK_NOTHING);
3337 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3338 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3339 gst_object_unref (srcpad);
3342 GST_DEBUG_OBJECT (playsink, "no vis needed");
3343 if (playsink->vischain) {
3344 if (playsink->audio_tee_vissrc) {
3345 gst_element_release_request_pad (playsink->audio_tee,
3346 playsink->audio_tee_vissrc);
3347 gst_object_unref (playsink->audio_tee_vissrc);
3348 playsink->audio_tee_vissrc = NULL;
3350 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3351 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3352 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3357 GST_DEBUG_OBJECT (playsink, "adding text");
3358 if (!playsink->textchain) {
3359 GST_DEBUG_OBJECT (playsink, "creating text chain");
3360 playsink->textchain = gen_text_chain (playsink);
3362 if (playsink->textchain) {
3365 GST_DEBUG_OBJECT (playsink, "adding text chain");
3366 if (playsink->textchain->overlay)
3367 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3369 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3371 if (!playsink->text_sinkpad_stream_synchronizer) {
3372 GValue item = { 0, };
3374 playsink->text_sinkpad_stream_synchronizer =
3375 gst_element_get_request_pad (GST_ELEMENT_CAST
3376 (playsink->stream_synchronizer), "sink_%u");
3377 it = gst_pad_iterate_internal_links
3378 (playsink->text_sinkpad_stream_synchronizer);
3380 gst_iterator_next (it, &item);
3381 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3382 g_value_unset (&item);
3383 g_assert (playsink->text_srcpad_stream_synchronizer);
3384 gst_iterator_free (it);
3386 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3387 playsink->text_sinkpad_stream_synchronizer);
3388 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3389 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3392 if (need_vis || need_video) {
3397 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3398 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3399 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3400 GST_PAD_LINK_CHECK_NOTHING);
3401 gst_object_unref (srcpad);
3403 if (need_deinterlace)
3404 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3405 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3407 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3408 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3410 gst_pad_link_full (playsink->textchain->srcpad,
3411 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3414 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3417 GST_DEBUG_OBJECT (playsink, "no text needed");
3418 /* we have no subtitles/text or we are requested to not show them */
3420 if (playsink->text_sinkpad_stream_synchronizer) {
3421 gst_element_release_request_pad (GST_ELEMENT_CAST
3422 (playsink->stream_synchronizer),
3423 playsink->text_sinkpad_stream_synchronizer);
3424 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3425 playsink->text_sinkpad_stream_synchronizer = NULL;
3426 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3427 playsink->text_srcpad_stream_synchronizer = NULL;
3430 if (playsink->textchain) {
3431 if (playsink->text_pad == NULL) {
3432 /* no text pad, remove the chain entirely */
3433 GST_DEBUG_OBJECT (playsink, "removing text chain");
3434 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3435 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3437 /* we have a chain and a textpad, turn the subtitles off */
3438 GST_DEBUG_OBJECT (playsink, "turning off the text");
3439 if (playsink->textchain->overlay)
3440 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3444 if (!need_video && playsink->video_pad) {
3445 if (playsink->video_sinkpad_stream_synchronizer) {
3446 gst_element_release_request_pad (GST_ELEMENT_CAST
3447 (playsink->stream_synchronizer),
3448 playsink->video_sinkpad_stream_synchronizer);
3449 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3450 playsink->video_sinkpad_stream_synchronizer = NULL;
3451 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3452 playsink->video_srcpad_stream_synchronizer = NULL;
3455 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3458 if (playsink->text_pad && !playsink->textchain)
3459 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3461 update_av_offset (playsink);
3462 do_async_done (playsink);
3463 GST_PLAY_SINK_UNLOCK (playsink);
3470 /* gen_ chain already posted error */
3471 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3472 GST_PLAY_SINK_UNLOCK (playsink);
3478 * gst_play_sink_set_flags:
3479 * @playsink: a #GstPlaySink
3480 * @flags: #GstPlayFlags
3482 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3483 * when constructing the sink pipelins.
3485 * Returns: TRUE if the flags could be configured.
3488 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3490 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3492 GST_OBJECT_LOCK (playsink);
3493 playsink->flags = flags;
3494 GST_OBJECT_UNLOCK (playsink);
3500 * gst_play_sink_get_flags:
3501 * @playsink: a #GstPlaySink
3503 * Get the flags of @playsink. That flags control the behaviour of the sink when
3504 * it constructs the sink pipelines.
3506 * Returns: the currently configured #GstPlayFlags.
3509 gst_play_sink_get_flags (GstPlaySink * playsink)
3513 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3515 GST_OBJECT_LOCK (playsink);
3516 res = playsink->flags;
3517 GST_OBJECT_UNLOCK (playsink);
3523 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3525 GstPlayTextChain *chain;
3527 GST_PLAY_SINK_LOCK (playsink);
3528 chain = (GstPlayTextChain *) playsink->textchain;
3529 g_free (playsink->font_desc);
3530 playsink->font_desc = g_strdup (desc);
3531 if (chain && chain->overlay) {
3532 g_object_set (chain->overlay, "font-desc", desc, NULL);
3534 GST_PLAY_SINK_UNLOCK (playsink);
3538 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3540 gchar *result = NULL;
3541 GstPlayTextChain *chain;
3543 GST_PLAY_SINK_LOCK (playsink);
3544 chain = (GstPlayTextChain *) playsink->textchain;
3545 if (chain && chain->overlay) {
3546 g_object_get (chain->overlay, "font-desc", &result, NULL);
3547 playsink->font_desc = g_strdup (result);
3549 result = g_strdup (playsink->font_desc);
3551 GST_PLAY_SINK_UNLOCK (playsink);
3557 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3558 const gchar * encoding)
3560 GstPlayTextChain *chain;
3562 GST_PLAY_SINK_LOCK (playsink);
3563 chain = (GstPlayTextChain *) playsink->textchain;
3564 g_free (playsink->subtitle_encoding);
3565 playsink->subtitle_encoding = g_strdup (encoding);
3566 if (chain && chain->overlay) {
3567 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3569 GST_PLAY_SINK_UNLOCK (playsink);
3573 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3575 gchar *result = NULL;
3576 GstPlayTextChain *chain;
3578 GST_PLAY_SINK_LOCK (playsink);
3579 chain = (GstPlayTextChain *) playsink->textchain;
3580 if (chain && chain->overlay) {
3581 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3582 playsink->subtitle_encoding = g_strdup (result);
3584 result = g_strdup (playsink->subtitle_encoding);
3586 GST_PLAY_SINK_UNLOCK (playsink);
3592 update_av_offset (GstPlaySink * playsink)
3595 GstPlayAudioChain *achain;
3596 GstPlayVideoChain *vchain;
3598 av_offset = playsink->av_offset;
3599 achain = (GstPlayAudioChain *) playsink->audiochain;
3600 vchain = (GstPlayVideoChain *) playsink->videochain;
3602 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3603 g_object_set (achain->ts_offset,
3604 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
3605 g_object_set (vchain->ts_offset,
3606 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
3608 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3613 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3615 GST_PLAY_SINK_LOCK (playsink);
3616 playsink->av_offset = av_offset;
3617 update_av_offset (playsink);
3618 GST_PLAY_SINK_UNLOCK (playsink);
3622 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3626 GST_PLAY_SINK_LOCK (playsink);
3627 result = playsink->av_offset;
3628 GST_PLAY_SINK_UNLOCK (playsink);
3634 * gst_play_sink_get_last_sample:
3635 * @playsink: a #GstPlaySink
3637 * Get the last displayed sample from @playsink. This sample is in the native
3638 * format of the sink element, the caps in the result sample contain the format
3639 * of the frame data.
3641 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3645 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3647 GstSample *result = NULL;
3648 GstPlayVideoChain *chain;
3650 GST_PLAY_SINK_LOCK (playsink);
3651 GST_DEBUG_OBJECT (playsink, "taking last sample");
3652 /* get the video chain if we can */
3653 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3654 GST_DEBUG_OBJECT (playsink, "found video chain");
3655 /* see if the chain is active */
3656 if (chain->chain.activated && chain->sink) {
3659 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3661 /* find and get the last-buffer property now */
3663 gst_play_sink_find_property (playsink, chain->sink,
3664 "last-sample", GST_TYPE_SAMPLE))) {
3665 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3666 g_object_get (elem, "last-sample", &result, NULL);
3667 gst_object_unref (elem);
3671 GST_PLAY_SINK_UNLOCK (playsink);
3677 * gst_play_sink_convert_sample:
3678 * @playsink: a #GstPlaySink
3681 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3682 * be in the native format of the sink element and the caps on the buffer
3683 * describe the format of the frame. If @caps is not %NULL, the video
3684 * frame will be converted to the format of the caps.
3686 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3687 * available or when the conversion failed.
3690 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3695 result = gst_play_sink_get_last_sample (playsink);
3696 if (result != NULL && caps != NULL) {
3699 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3700 if (temp == NULL && err)
3703 gst_sample_unref (result);
3711 /* I'm really uncertain whether we should make playsink post an error
3712 * on the bus or not. It's not like it's a critical issue regarding
3713 * playsink behaviour. */
3714 GST_ERROR ("Error converting frame: %s", err->message);
3715 gst_sample_unref (result);
3722 is_raw_structure (GstStructure * s)
3726 name = gst_structure_get_name (s);
3728 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3734 is_raw_pad (GstPad * pad)
3736 GstPad *peer = gst_pad_get_peer (pad);
3738 gboolean raw = TRUE;
3743 caps = gst_pad_get_current_caps (peer);
3747 caps = gst_pad_query_caps (peer, NULL);
3749 n = gst_caps_get_size (caps);
3750 for (i = 0; i < n; i++) {
3751 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3755 } else if (raw != r) {
3756 GST_ERROR_OBJECT (pad,
3757 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3763 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3765 gst_caps_unref (caps);
3766 gst_object_unref (peer);
3771 static GstPadProbeReturn
3772 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3773 gpointer user_data);
3776 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3778 if (playsink->video_pad) {
3780 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3781 (playsink->video_pad)));
3782 if (blocked && playsink->video_block_id == 0) {
3783 playsink->video_block_id =
3784 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3785 sinkpad_blocked_cb, playsink, NULL);
3786 } else if (!blocked && playsink->video_block_id) {
3787 gst_pad_remove_probe (opad, playsink->video_block_id);
3788 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3789 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3790 playsink->video_block_id = 0;
3791 playsink->video_pad_blocked = FALSE;
3793 gst_object_unref (opad);
3798 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3800 if (playsink->audio_pad) {
3802 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3803 (playsink->audio_pad)));
3804 if (blocked && playsink->audio_block_id == 0) {
3805 playsink->audio_block_id =
3806 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3807 sinkpad_blocked_cb, playsink, NULL);
3808 } else if (!blocked && playsink->audio_block_id) {
3809 gst_pad_remove_probe (opad, playsink->audio_block_id);
3810 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3811 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3812 playsink->audio_block_id = 0;
3813 playsink->audio_pad_blocked = FALSE;
3815 gst_object_unref (opad);
3820 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3822 if (playsink->text_pad) {
3824 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3825 (playsink->text_pad)));
3826 if (blocked && playsink->text_block_id == 0) {
3827 playsink->text_block_id =
3828 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3829 sinkpad_blocked_cb, playsink, NULL);
3830 } else if (!blocked && playsink->text_block_id) {
3831 gst_pad_remove_probe (opad, playsink->text_block_id);
3832 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3833 playsink->text_block_id = 0;
3834 playsink->text_pad_blocked = FALSE;
3836 gst_object_unref (opad);
3841 gst_play_sink_reconfigure (GstPlaySink * playsink)
3843 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
3845 GST_PLAY_SINK_LOCK (playsink);
3846 video_set_blocked (playsink, TRUE);
3847 audio_set_blocked (playsink, TRUE);
3848 text_set_blocked (playsink, TRUE);
3849 GST_PLAY_SINK_UNLOCK (playsink);
3854 static GstPadProbeReturn
3855 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3858 GstPlaySink *playsink = (GstPlaySink *) user_data;
3861 GST_PLAY_SINK_LOCK (playsink);
3863 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3864 if (pad == playsink->video_pad) {
3865 playsink->video_pad_blocked = TRUE;
3866 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3867 } else if (pad == playsink->audio_pad) {
3868 playsink->audio_pad_blocked = TRUE;
3869 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3870 } else if (pad == playsink->text_pad) {
3871 playsink->text_pad_blocked = TRUE;
3872 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3875 /* We reconfigure when for ALL streams:
3876 * * there isn't a pad
3877 * * OR the pad is blocked
3878 * * OR there are no pending blocks on that pad
3881 if ((!playsink->video_pad || playsink->video_pad_blocked
3882 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3883 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3884 && (!playsink->text_pad || playsink->text_pad_blocked
3885 || !PENDING_TEXT_BLOCK (playsink))) {
3886 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3888 if (playsink->video_pad) {
3889 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3890 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3891 playsink->video_pad_raw);
3894 if (playsink->audio_pad) {
3895 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3896 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3897 playsink->audio_pad_raw);
3900 gst_play_sink_do_reconfigure (playsink);
3902 video_set_blocked (playsink, FALSE);
3903 audio_set_blocked (playsink, FALSE);
3904 text_set_blocked (playsink, FALSE);
3907 gst_object_unref (pad);
3909 GST_PLAY_SINK_UNLOCK (playsink);
3911 return GST_PAD_PROBE_OK;
3915 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3917 gboolean reconfigure = FALSE;
3921 g_object_get (pad, "caps", &caps, NULL);
3925 if (pad == playsink->audio_pad) {
3926 raw = is_raw_pad (pad);
3927 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3928 && playsink->audiochain;
3929 GST_DEBUG_OBJECT (pad,
3930 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3932 } else if (pad == playsink->video_pad) {
3933 raw = is_raw_pad (pad);
3934 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3935 && playsink->videochain;
3936 GST_DEBUG_OBJECT (pad,
3937 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3941 gst_caps_unref (caps);
3944 gst_play_sink_reconfigure (playsink);
3948 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3949 GstPlaySinkType type)
3951 gulong *block_id = NULL;
3953 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3955 GST_PLAY_SINK_LOCK (playsink);
3956 if (pad == playsink->video_pad) {
3957 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3958 type != GST_PLAY_SINK_TYPE_VIDEO)
3960 block_id = &playsink->video_block_id;
3961 } else if (pad == playsink->audio_pad) {
3962 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3963 type != GST_PLAY_SINK_TYPE_AUDIO)
3965 block_id = &playsink->audio_block_id;
3966 } else if (pad == playsink->text_pad) {
3967 if (type != GST_PLAY_SINK_TYPE_TEXT)
3969 block_id = &playsink->text_block_id;
3972 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3974 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3977 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3978 sinkpad_blocked_cb, playsink, NULL);
3979 PENDING_FLAG_SET (playsink, type);
3980 gst_object_unref (blockpad);
3982 GST_PLAY_SINK_UNLOCK (playsink);
3989 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3991 GST_PLAY_SINK_UNLOCK (playsink);
3997 * gst_play_sink_request_pad
3998 * @playsink: a #GstPlaySink
3999 * @type: a #GstPlaySinkType
4001 * Create or return a pad of @type.
4003 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4006 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4009 gboolean created = FALSE;
4010 gboolean activate = TRUE;
4011 const gchar *pad_name = NULL;
4012 gulong *block_id = NULL;
4014 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4016 GST_PLAY_SINK_LOCK (playsink);
4018 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
4019 case GST_PLAY_SINK_TYPE_AUDIO:
4020 pad_name = "audio_sink";
4021 if (!playsink->audio_tee) {
4022 GST_LOG_OBJECT (playsink, "creating tee");
4023 /* create tee when needed. This element will feed the audio sink chain
4024 * and the vis chain. */
4025 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4026 if (playsink->audio_tee == NULL) {
4027 post_missing_element_message (playsink, "tee");
4028 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4029 (_("Missing element '%s' - check your GStreamer installation."),
4034 playsink->audio_tee_sink =
4035 gst_element_get_static_pad (playsink->audio_tee, "sink");
4036 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4037 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4040 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4042 if (!playsink->audio_pad) {
4043 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4044 playsink->audio_pad =
4045 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4046 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4047 G_CALLBACK (caps_notify_cb), playsink);
4050 playsink->audio_pad_raw = FALSE;
4051 res = playsink->audio_pad;
4052 block_id = &playsink->audio_block_id;
4054 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
4055 case GST_PLAY_SINK_TYPE_VIDEO:
4056 pad_name = "video_sink";
4057 if (!playsink->video_pad) {
4058 GST_LOG_OBJECT (playsink, "ghosting videosink");
4059 playsink->video_pad =
4060 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4061 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4062 G_CALLBACK (caps_notify_cb), playsink);
4065 playsink->video_pad_raw = FALSE;
4066 res = playsink->video_pad;
4067 block_id = &playsink->video_block_id;
4069 case GST_PLAY_SINK_TYPE_TEXT:
4070 GST_LOG_OBJECT (playsink, "ghosting text");
4071 if (!playsink->text_pad) {
4072 playsink->text_pad =
4073 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4076 res = playsink->text_pad;
4077 block_id = &playsink->text_block_id;
4079 case GST_PLAY_SINK_TYPE_FLUSHING:
4083 /* we need a unique padname for the flushing pad. */
4084 padname = g_strdup_printf ("flushing_%u", playsink->count);
4085 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4096 GST_PLAY_SINK_UNLOCK (playsink);
4098 if (created && res) {
4099 /* we have to add the pad when it's active or we get an error when the
4100 * element is 'running' */
4101 gst_pad_set_active (res, TRUE);
4102 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4103 if (block_id && *block_id == 0) {
4105 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4108 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4109 sinkpad_blocked_cb, playsink, NULL);
4110 PENDING_FLAG_SET (playsink, type);
4111 gst_object_unref (blockpad);
4114 gst_pad_set_active (res, activate);
4122 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4123 const gchar * name, const GstCaps * caps)
4127 GstPlaySinkType type;
4128 const gchar *tplname;
4130 g_return_val_if_fail (templ != NULL, NULL);
4132 GST_DEBUG_OBJECT (element, "name:%s", name);
4134 psink = GST_PLAY_SINK (element);
4135 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4137 /* Figure out the GstPlaySinkType based on the template */
4138 if (!strcmp (tplname, "audio_sink"))
4139 type = GST_PLAY_SINK_TYPE_AUDIO;
4140 else if (!strcmp (tplname, "audio_raw_sink"))
4141 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4142 else if (!strcmp (tplname, "video_sink"))
4143 type = GST_PLAY_SINK_TYPE_VIDEO;
4144 else if (!strcmp (tplname, "video_raw_sink"))
4145 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4146 else if (!strcmp (tplname, "text_sink"))
4147 type = GST_PLAY_SINK_TYPE_TEXT;
4149 goto unknown_template;
4151 pad = gst_play_sink_request_pad (psink, type);
4155 GST_WARNING_OBJECT (element, "Unknown pad template");
4160 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4162 GstPad **res = NULL;
4163 gboolean untarget = TRUE;
4165 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4167 GST_PLAY_SINK_LOCK (playsink);
4168 if (pad == playsink->video_pad) {
4169 res = &playsink->video_pad;
4170 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4172 } else if (pad == playsink->audio_pad) {
4173 res = &playsink->audio_pad;
4174 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4176 } else if (pad == playsink->text_pad) {
4177 res = &playsink->text_pad;
4179 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4183 GST_PLAY_SINK_UNLOCK (playsink);
4186 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4187 gst_pad_set_active (*res, FALSE);
4189 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4190 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4192 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4193 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4199 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4201 GstPlaySink *psink = GST_PLAY_SINK (element);
4203 gst_play_sink_release_pad (psink, pad);
4207 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4209 GstPlaySink *playsink;
4211 playsink = GST_PLAY_SINK_CAST (bin);
4213 switch (GST_MESSAGE_TYPE (message)) {
4214 case GST_MESSAGE_STEP_DONE:
4219 gboolean flush, intermediate, eos;
4222 GST_INFO_OBJECT (playsink, "Handling step-done message");
4223 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4224 &intermediate, &duration, &eos);
4226 if (format == GST_FORMAT_BUFFERS) {
4227 /* for the buffer format, we align the other streams */
4228 if (playsink->audiochain) {
4232 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4235 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4236 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4240 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4243 case GST_MESSAGE_ELEMENT:{
4244 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4245 GstVideoOverlay *overlay;
4247 GST_OBJECT_LOCK (playsink);
4248 if (playsink->overlay_element
4249 && GST_OBJECT_CAST (playsink->overlay_element) !=
4250 GST_MESSAGE_SRC (message)) {
4251 gst_object_unref (playsink->overlay_element);
4252 playsink->overlay_element = NULL;
4255 if (!playsink->overlay_element)
4256 playsink->overlay_element =
4257 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4259 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4260 GST_OBJECT_UNLOCK (playsink);
4262 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4264 if (playsink->overlay_handle_set)
4265 gst_video_overlay_set_window_handle (playsink->overlay_element,
4266 playsink->overlay_handle);
4267 if (playsink->overlay_handle_events_set)
4268 gst_video_overlay_handle_events (playsink->overlay_element,
4269 playsink->overlay_handle_events);
4270 if (playsink->overlay_render_rectangle_set)
4271 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4272 playsink->overlay_x, playsink->overlay_y,
4273 playsink->overlay_width, playsink->overlay_height);
4275 gst_object_unref (overlay);
4276 gst_message_unref (message);
4277 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4279 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4285 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4290 /* Send an event to our sinks until one of them works; don't then send to the
4291 * remaining sinks (unlike GstBin)
4292 * Special case: If a text sink is set we need to send the event
4293 * to them in case it's source is different from the a/v stream's source.
4296 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4298 gboolean res = TRUE;
4299 if (playsink->send_event_mode == MODE_FIRST) {
4300 if (playsink->textchain && playsink->textchain->sink) {
4301 gst_event_ref (event);
4303 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4304 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4306 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4310 if (playsink->videochain) {
4311 gst_event_ref (event);
4313 gst_element_send_event (playsink->videochain->chain.bin,
4315 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4318 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4320 if (playsink->audiochain) {
4321 gst_event_ref (event);
4323 gst_element_send_event (playsink->audiochain->chain.bin,
4325 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4328 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4332 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4333 (GST_ELEMENT_CAST (playsink), event);
4337 gst_event_unref (event);
4341 /* We only want to send the event to a single sink (overriding GstBin's
4342 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4343 * events appropriately. So, this is a messy duplication of code. */
4345 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4347 gboolean res = FALSE;
4348 GstEventType event_type = GST_EVENT_TYPE (event);
4349 GstPlaySink *playsink;
4350 playsink = GST_PLAY_SINK_CAST (element);
4351 switch (event_type) {
4352 case GST_EVENT_SEEK:
4353 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4354 res = gst_play_sink_send_event_to_sink (playsink, event);
4356 case GST_EVENT_STEP:
4361 gboolean flush, intermediate;
4362 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4364 if (format == GST_FORMAT_BUFFERS) {
4365 /* for buffers, we will try to step video frames, for other formats we
4366 * send the step to all sinks */
4367 res = gst_play_sink_send_event_to_sink (playsink, event);
4370 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4377 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4384 static GstStateChangeReturn
4385 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4387 GstStateChangeReturn ret;
4388 GstStateChangeReturn bret;
4389 GstPlaySink *playsink;
4390 playsink = GST_PLAY_SINK (element);
4391 switch (transition) {
4392 case GST_STATE_CHANGE_READY_TO_PAUSED:
4393 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4395 playsink->need_async_start = TRUE;
4396 /* we want to go async to PAUSED until we managed to configure and add the
4398 do_async_start (playsink);
4399 ret = GST_STATE_CHANGE_ASYNC;
4401 /* block all pads here */
4402 if (!gst_play_sink_reconfigure (playsink))
4403 ret = GST_STATE_CHANGE_FAILURE;
4405 case GST_STATE_CHANGE_PAUSED_TO_READY:
4406 /* unblock all pads here */
4407 GST_PLAY_SINK_LOCK (playsink);
4408 video_set_blocked (playsink, FALSE);
4409 audio_set_blocked (playsink, FALSE);
4410 text_set_blocked (playsink, FALSE);
4411 GST_PLAY_SINK_UNLOCK (playsink);
4413 case GST_STATE_CHANGE_READY_TO_NULL:
4414 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4415 /* remove our links to the mute and volume elements when they were
4416 * provided by a sink */
4417 disconnect_chain (playsink->audiochain, playsink);
4418 playsink->audiochain->volume = NULL;
4419 playsink->audiochain->mute = NULL;
4422 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4423 gst_object_unref (playsink->audiochain->ts_offset);
4424 playsink->audiochain->ts_offset = NULL;
4427 if (playsink->videochain && playsink->videochain->ts_offset) {
4428 gst_object_unref (playsink->videochain->ts_offset);
4429 playsink->videochain->ts_offset = NULL;
4432 GST_OBJECT_LOCK (playsink);
4433 if (playsink->overlay_element)
4434 gst_object_unref (playsink->overlay_element);
4435 playsink->overlay_element = NULL;
4437 if (playsink->colorbalance_element) {
4438 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4439 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4440 gst_object_unref (playsink->colorbalance_element);
4442 playsink->colorbalance_element = NULL;
4443 GST_OBJECT_UNLOCK (playsink);
4445 ret = GST_STATE_CHANGE_SUCCESS;
4448 /* all other state changes return SUCCESS by default, this value can be
4449 * overridden by the result of the children */
4450 ret = GST_STATE_CHANGE_SUCCESS;
4454 /* do the state change of the children */
4456 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4458 /* now look at the result of our children and adjust the return value */
4460 case GST_STATE_CHANGE_FAILURE:
4461 /* failure, we stop */
4462 goto activate_failed;
4463 case GST_STATE_CHANGE_NO_PREROLL:
4464 /* some child returned NO_PREROLL. This is strange but we never know. We
4465 * commit our async state change (if any) and return the NO_PREROLL */
4466 do_async_done (playsink);
4469 case GST_STATE_CHANGE_ASYNC:
4470 /* some child was async, return this */
4474 /* return our previously configured return value */
4478 switch (transition) {
4479 case GST_STATE_CHANGE_READY_TO_PAUSED:
4481 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4482 /* FIXME Release audio device when we implement that */
4483 playsink->need_async_start = TRUE;
4485 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4486 if (playsink->video_sinkpad_stream_synchronizer) {
4487 gst_element_release_request_pad (GST_ELEMENT_CAST
4488 (playsink->stream_synchronizer),
4489 playsink->video_sinkpad_stream_synchronizer);
4490 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4491 playsink->video_sinkpad_stream_synchronizer = NULL;
4492 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4493 playsink->video_srcpad_stream_synchronizer = NULL;
4495 if (playsink->audio_sinkpad_stream_synchronizer) {
4496 gst_element_release_request_pad (GST_ELEMENT_CAST
4497 (playsink->stream_synchronizer),
4498 playsink->audio_sinkpad_stream_synchronizer);
4499 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4500 playsink->audio_sinkpad_stream_synchronizer = NULL;
4501 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4502 playsink->audio_srcpad_stream_synchronizer = NULL;
4504 if (playsink->text_sinkpad_stream_synchronizer) {
4505 gst_element_release_request_pad (GST_ELEMENT_CAST
4506 (playsink->stream_synchronizer),
4507 playsink->text_sinkpad_stream_synchronizer);
4508 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4509 playsink->text_sinkpad_stream_synchronizer = NULL;
4510 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4511 playsink->text_srcpad_stream_synchronizer = NULL;
4515 case GST_STATE_CHANGE_READY_TO_NULL:
4516 /* remove sinks we added */
4517 if (playsink->videodeinterlacechain) {
4518 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4520 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4522 if (playsink->videochain) {
4523 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4524 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4526 if (playsink->audiochain) {
4527 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4528 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4530 if (playsink->vischain) {
4531 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4532 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4534 if (playsink->textchain) {
4535 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4536 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4538 do_async_done (playsink);
4539 /* when going to READY, keep elements around as long as possible,
4540 * so they may be re-used faster next time/url around.
4541 * when really going to NULL, clean up everything completely. */
4542 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4544 /* Unparent the sinks to allow reuse */
4545 if (playsink->videochain && playsink->videochain->sink)
4546 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4547 playsink->videochain->sink);
4548 if (playsink->audiochain && playsink->audiochain->sink)
4549 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4550 playsink->audiochain->sink);
4551 if (playsink->textchain && playsink->textchain->sink)
4552 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4553 playsink->textchain->sink);
4554 if (playsink->audio_sink != NULL)
4555 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4556 if (playsink->video_sink != NULL)
4557 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4558 if (playsink->visualisation != NULL)
4559 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4560 if (playsink->text_sink != NULL)
4561 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4562 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4563 playsink->videodeinterlacechain = NULL;
4564 free_chain ((GstPlayChain *) playsink->videochain);
4565 playsink->videochain = NULL;
4566 free_chain ((GstPlayChain *) playsink->audiochain);
4567 playsink->audiochain = NULL;
4568 free_chain ((GstPlayChain *) playsink->vischain);
4569 playsink->vischain = NULL;
4570 free_chain ((GstPlayChain *) playsink->textchain);
4571 playsink->textchain = NULL;
4581 GST_DEBUG_OBJECT (element,
4582 "element failed to change states -- activation problem?");
4583 return GST_STATE_CHANGE_FAILURE;
4588 gst_play_sink_set_property (GObject * object, guint prop_id,
4589 const GValue * value, GParamSpec * spec)
4591 GstPlaySink *playsink = GST_PLAY_SINK (object);
4594 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4597 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4600 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4602 case PROP_FONT_DESC:
4603 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4605 case PROP_SUBTITLE_ENCODING:
4606 gst_play_sink_set_subtitle_encoding (playsink,
4607 g_value_get_string (value));
4609 case PROP_VIS_PLUGIN:
4610 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4612 case PROP_AV_OFFSET:
4613 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4615 case PROP_VIDEO_SINK:
4616 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4617 g_value_get_object (value));
4619 case PROP_AUDIO_SINK:
4620 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4621 g_value_get_object (value));
4623 case PROP_TEXT_SINK:
4624 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4625 g_value_get_object (value));
4627 case PROP_SEND_EVENT_MODE:
4628 playsink->send_event_mode = g_value_get_enum (value);
4630 case PROP_FORCE_ASPECT_RATIO:{
4631 GstPlayVideoChain *chain;
4634 playsink->force_aspect_ratio = g_value_get_boolean (value);
4636 GST_PLAY_SINK_LOCK (playsink);
4637 if (playsink->videochain) {
4638 chain = (GstPlayVideoChain *) playsink->videochain;
4642 gst_play_sink_find_property_sinks (playsink, chain->sink,
4643 "force-aspect-ratio", G_TYPE_BOOLEAN);
4646 g_object_set (elem, "force-aspect-ratio",
4647 playsink->force_aspect_ratio, NULL);
4650 GST_PLAY_SINK_UNLOCK (playsink);
4654 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4660 gst_play_sink_get_property (GObject * object, guint prop_id,
4661 GValue * value, GParamSpec * spec)
4663 GstPlaySink *playsink = GST_PLAY_SINK (object);
4666 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4669 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4672 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4674 case PROP_FONT_DESC:
4675 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4677 case PROP_SUBTITLE_ENCODING:
4678 g_value_take_string (value,
4679 gst_play_sink_get_subtitle_encoding (playsink));
4681 case PROP_VIS_PLUGIN:
4682 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4685 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4687 case PROP_AV_OFFSET:
4688 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4690 case PROP_VIDEO_SINK:
4691 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4692 GST_PLAY_SINK_TYPE_VIDEO));
4694 case PROP_AUDIO_SINK:
4695 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4696 GST_PLAY_SINK_TYPE_AUDIO));
4698 case PROP_TEXT_SINK:
4699 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4700 GST_PLAY_SINK_TYPE_TEXT));
4702 case PROP_SEND_EVENT_MODE:
4703 g_value_set_enum (value, playsink->send_event_mode);
4705 case PROP_FORCE_ASPECT_RATIO:
4706 g_value_set_boolean (value, playsink->force_aspect_ratio);
4709 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4715 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4717 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4718 GstVideoOverlay *overlay_element;
4720 GST_OBJECT_LOCK (playsink);
4721 if (playsink->overlay_element)
4723 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4725 overlay_element = NULL;
4726 GST_OBJECT_UNLOCK (playsink);
4728 if (overlay_element) {
4729 gst_video_overlay_expose (overlay_element);
4730 gst_object_unref (overlay_element);
4735 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4736 gboolean handle_events)
4738 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4739 GstVideoOverlay *overlay_element;
4741 GST_OBJECT_LOCK (playsink);
4742 if (playsink->overlay_element)
4744 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4746 overlay_element = NULL;
4747 GST_OBJECT_UNLOCK (playsink);
4749 playsink->overlay_handle_events_set = TRUE;
4750 playsink->overlay_handle_events = handle_events;
4752 if (overlay_element) {
4753 gst_video_overlay_handle_events (overlay_element, handle_events);
4754 gst_object_unref (overlay_element);
4759 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4760 gint y, gint width, gint height)
4762 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4763 GstVideoOverlay *overlay_element;
4765 GST_OBJECT_LOCK (playsink);
4766 if (playsink->overlay_element)
4768 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4770 overlay_element = NULL;
4771 GST_OBJECT_UNLOCK (playsink);
4773 playsink->overlay_render_rectangle_set = TRUE;
4774 playsink->overlay_x = x;
4775 playsink->overlay_y = y;
4776 playsink->overlay_width = width;
4777 playsink->overlay_height = height;
4779 if (overlay_element) {
4780 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4782 gst_object_unref (overlay_element);
4787 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4790 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4791 GstVideoOverlay *overlay_element;
4793 GST_OBJECT_LOCK (playsink);
4794 if (playsink->overlay_element)
4796 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4798 overlay_element = NULL;
4799 GST_OBJECT_UNLOCK (playsink);
4801 playsink->overlay_handle_set = TRUE;
4802 playsink->overlay_handle = handle;
4804 if (overlay_element) {
4805 gst_video_overlay_set_window_handle (overlay_element, handle);
4806 gst_object_unref (overlay_element);
4811 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4813 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4814 iface->expose = gst_play_sink_overlay_expose;
4815 iface->handle_events = gst_play_sink_overlay_handle_events;
4816 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4817 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4821 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4822 GstStructure * structure)
4824 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4827 GST_PLAY_SINK_LOCK (playsink);
4828 if (playsink->videochain && playsink->videochain->chain.bin)
4829 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4830 GST_PLAY_SINK_UNLOCK (playsink);
4833 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4836 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4838 gst_object_unref (nav);
4840 GstEvent *event = gst_event_new_navigation (structure);
4842 gst_element_send_event (GST_ELEMENT (bin), event);
4845 gst_object_unref (bin);
4849 gst_structure_free (structure);
4853 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4855 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4857 iface->send_event = gst_play_sink_navigation_send_event;
4860 static const GList *
4861 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4863 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4865 return playsink->colorbalance_channels;
4869 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4870 GstColorBalanceChannel * proxy, gint value)
4872 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4875 GstColorBalance *balance_element = NULL;
4877 GST_OBJECT_LOCK (playsink);
4878 if (playsink->colorbalance_element)
4880 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4881 GST_OBJECT_UNLOCK (playsink);
4883 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4884 GstColorBalanceChannel *proxy_tmp = l->data;
4887 if (proxy_tmp != proxy)
4890 playsink->colorbalance_values[i] = value;
4892 if (balance_element) {
4893 GstColorBalanceChannel *channel = NULL;
4894 const GList *channels, *k;
4896 channels = gst_color_balance_list_channels (balance_element);
4897 for (k = channels; k; k = k->next) {
4898 GstColorBalanceChannel *tmp = l->data;
4900 if (g_strrstr (tmp->label, proxy->label)) {
4908 /* Convert to [0, 1] range */
4911 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4912 (gdouble) proxy->min_value);
4913 /* Convert to channel range */
4915 channel->min_value + new_val * ((gdouble) channel->max_value -
4916 (gdouble) channel->min_value);
4918 gst_color_balance_set_value (balance_element, channel,
4919 (gint) (new_val + 0.5));
4921 gst_object_unref (balance_element);
4924 gst_color_balance_value_changed (balance, proxy, value);
4930 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4931 GstColorBalanceChannel * proxy)
4933 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4937 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4938 GstColorBalanceChannel *proxy_tmp = l->data;
4940 if (proxy_tmp != proxy)
4943 return playsink->colorbalance_values[i];
4946 g_return_val_if_reached (0);
4949 static GstColorBalanceType
4950 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4952 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4953 GstColorBalance *balance_element = NULL;
4954 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4956 GST_OBJECT_LOCK (playsink);
4957 if (playsink->colorbalance_element)
4959 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4960 GST_OBJECT_UNLOCK (playsink);
4962 if (balance_element) {
4963 t = gst_color_balance_get_balance_type (balance_element);
4964 gst_object_unref (balance_element);
4971 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4973 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4975 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4976 iface->set_value = gst_play_sink_colorbalance_set_value;
4977 iface->get_value = gst_play_sink_colorbalance_get_value;
4978 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4982 gst_play_sink_plugin_init (GstPlugin * plugin)
4984 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4985 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4986 GST_TYPE_PLAY_SINK);