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;
246 /* videooverlay proxy interface */
247 GstVideoOverlay *overlay_element; /* protected with LOCK */
248 gboolean overlay_handle_set;
249 guintptr overlay_handle;
250 gboolean overlay_render_rectangle_set;
251 gint overlay_x, overlay_y, overlay_width, overlay_height;
252 gboolean overlay_handle_events_set;
253 gboolean overlay_handle_events;
255 /* colorbalance proxy interface */
256 GstColorBalance *colorbalance_element;
257 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
258 gint colorbalance_values[4];
261 struct _GstPlaySinkClass
263 GstBinClass parent_class;
265 gboolean (*reconfigure) (GstPlaySink * playsink);
267 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
271 static GstStaticPadTemplate audiotemplate =
272 GST_STATIC_PAD_TEMPLATE ("audio_sink",
275 GST_STATIC_CAPS_ANY);
276 static GstStaticPadTemplate videotemplate =
277 GST_STATIC_PAD_TEMPLATE ("video_sink",
280 GST_STATIC_CAPS_ANY);
281 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
284 GST_STATIC_CAPS_ANY);
286 /* FIXME 0.11: Remove */
287 static GstStaticPadTemplate audiorawtemplate =
288 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
291 GST_STATIC_CAPS_ANY);
292 static GstStaticPadTemplate videorawtemplate =
293 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
296 GST_STATIC_CAPS_ANY);
307 PROP_SUBTITLE_ENCODING,
314 PROP_SEND_EVENT_MODE,
324 static void gst_play_sink_dispose (GObject * object);
325 static void gst_play_sink_finalize (GObject * object);
326 static void gst_play_sink_set_property (GObject * object, guint prop_id,
327 const GValue * value, GParamSpec * spec);
328 static void gst_play_sink_get_property (GObject * object, guint prop_id,
329 GValue * value, GParamSpec * spec);
331 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
332 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
333 static void gst_play_sink_release_request_pad (GstElement * element,
335 static gboolean gst_play_sink_send_event (GstElement * element,
337 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
338 GstStateChange transition);
340 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
342 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
343 GstPlaySink * playsink);
344 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
345 GstPlaySink * playsink);
347 static void update_av_offset (GstPlaySink * playsink);
349 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
351 static void gst_play_sink_overlay_init (gpointer g_iface,
352 gpointer g_iface_data);
353 static void gst_play_sink_navigation_init (gpointer g_iface,
354 gpointer g_iface_data);
355 static void gst_play_sink_colorbalance_init (gpointer g_iface,
356 gpointer g_iface_data);
359 _do_init (GType type)
361 static const GInterfaceInfo svol_info = {
364 static const GInterfaceInfo ov_info = {
365 gst_play_sink_overlay_init,
368 static const GInterfaceInfo nav_info = {
369 gst_play_sink_navigation_init,
372 static const GInterfaceInfo col_info = {
373 gst_play_sink_colorbalance_init,
377 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
378 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
379 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
380 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
383 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
384 _do_init (g_define_type_id));
387 gst_play_sink_class_init (GstPlaySinkClass * klass)
389 GObjectClass *gobject_klass;
390 GstElementClass *gstelement_klass;
391 GstBinClass *gstbin_klass;
393 gobject_klass = (GObjectClass *) klass;
394 gstelement_klass = (GstElementClass *) klass;
395 gstbin_klass = (GstBinClass *) klass;
397 gobject_klass->dispose = gst_play_sink_dispose;
398 gobject_klass->finalize = gst_play_sink_finalize;
399 gobject_klass->set_property = gst_play_sink_set_property;
400 gobject_klass->get_property = gst_play_sink_get_property;
406 * Control the behaviour of playsink.
408 g_object_class_install_property (gobject_klass, PROP_FLAGS,
409 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
410 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
411 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
414 * GstPlaySink:volume:
416 * Get or set the current audio stream volume. 1.0 means 100%,
417 * 0.0 means mute. This uses a linear volume scale.
420 g_object_class_install_property (gobject_klass, PROP_VOLUME,
421 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
422 0.0, VOLUME_MAX_DOUBLE, 1.0,
423 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
424 g_object_class_install_property (gobject_klass, PROP_MUTE,
425 g_param_spec_boolean ("mute", "Mute",
426 "Mute the audio channel without changing the volume", FALSE,
427 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
428 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
429 g_param_spec_string ("subtitle-font-desc",
430 "Subtitle font description",
431 "Pango font description of font "
432 "to be used for subtitle rendering", NULL,
433 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
434 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
435 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
436 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
437 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
438 "be checked for an encoding to use. If that is not set either, "
439 "ISO-8859-15 will be assumed.", NULL,
440 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
442 g_param_spec_object ("vis-plugin", "Vis plugin",
443 "the visualization element to use (NULL = default)",
444 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
446 * GstPlaySink:sample:
448 * Get the currently rendered or prerolled sample in the video sink.
449 * The #GstCaps in the sample will describe the format of the buffer.
451 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
452 g_param_spec_boxed ("sample", "Sample",
453 "The last sample (NULL = no video available)",
454 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
456 * GstPlaySink:av-offset:
458 * Control the synchronisation offset between the audio and video streams.
459 * Positive values make the audio ahead of the video and negative values make
460 * the audio go behind the video.
464 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
465 g_param_spec_int64 ("av-offset", "AV Offset",
466 "The synchronisation offset between audio and video in nanoseconds",
467 G_MININT64, G_MAXINT64, 0,
468 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
471 * GstPlaySink:video-sink:
473 * Set the used video sink element. NULL will use the default sink. playsink
474 * must be in %GST_STATE_NULL
478 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
479 g_param_spec_object ("video-sink", "Video Sink",
480 "the video output element to use (NULL = default sink)",
481 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
483 * GstPlaySink:audio-sink:
485 * Set the used audio sink element. NULL will use the default sink. playsink
486 * must be in %GST_STATE_NULL
490 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
491 g_param_spec_object ("audio-sink", "Audio Sink",
492 "the audio output element to use (NULL = default sink)",
493 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496 * GstPlaySink:text-sink:
498 * Set the used text sink element. NULL will use the default sink. playsink
499 * must be in %GST_STATE_NULL
503 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
504 g_param_spec_object ("text-sink", "Text sink",
505 "the text output element to use (NULL = default subtitleoverlay)",
506 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
509 * GstPlaySink::send-event-mode:
511 * Sets the handling method used for events received from send_event
512 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
513 * handling (push the event to all internal sinks).
517 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
518 g_param_spec_enum ("send-event-mode", "Send event mode",
519 "How to send events received in send_event function",
520 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
521 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
523 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
524 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
525 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
528 * GstPlaySink::convert-sample
529 * @playsink: a #GstPlaySink
530 * @caps: the target format of the sample
532 * Action signal to retrieve the currently playing video sample in the format
533 * specified by @caps.
534 * If @caps is %NULL, no conversion will be performed and this function is
535 * equivalent to the #GstPlaySink::sample property.
537 * Returns: a #GstSample of the current video sample converted to #caps.
538 * The caps in the sample will describe the final layout of the buffer data.
539 * %NULL is returned when no current sample can be retrieved or when the
542 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
543 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
544 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
545 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
547 gst_element_class_add_pad_template (gstelement_klass,
548 gst_static_pad_template_get (&audiorawtemplate));
549 gst_element_class_add_pad_template (gstelement_klass,
550 gst_static_pad_template_get (&audiotemplate));
551 gst_element_class_add_pad_template (gstelement_klass,
552 gst_static_pad_template_get (&videorawtemplate));
553 gst_element_class_add_pad_template (gstelement_klass,
554 gst_static_pad_template_get (&videotemplate));
555 gst_element_class_add_pad_template (gstelement_klass,
556 gst_static_pad_template_get (&texttemplate));
557 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
559 "Convenience sink for multiple streams",
560 "Wim Taymans <wim.taymans@gmail.com>");
562 gstelement_klass->change_state =
563 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
564 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
565 gstelement_klass->request_new_pad =
566 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
567 gstelement_klass->release_pad =
568 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
570 gstbin_klass->handle_message =
571 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
573 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
574 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
578 gst_play_sink_init (GstPlaySink * playsink)
580 GstColorBalanceChannel *channel;
583 playsink->video_sink = NULL;
584 playsink->audio_sink = NULL;
585 playsink->visualisation = NULL;
586 playsink->text_sink = NULL;
587 playsink->volume = 1.0;
588 playsink->font_desc = NULL;
589 playsink->subtitle_encoding = NULL;
590 playsink->flags = DEFAULT_FLAGS;
591 playsink->send_event_mode = MODE_DEFAULT;
593 playsink->stream_synchronizer =
594 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
595 gst_bin_add (GST_BIN_CAST (playsink),
596 GST_ELEMENT_CAST (playsink->stream_synchronizer));
598 g_rec_mutex_init (&playsink->lock);
599 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
602 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
604 channel->label = g_strdup ("CONTRAST");
605 channel->min_value = -1000;
606 channel->max_value = 1000;
607 playsink->colorbalance_channels =
608 g_list_append (playsink->colorbalance_channels, channel);
609 playsink->colorbalance_values[0] = 0;
612 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
614 channel->label = g_strdup ("BRIGHTNESS");
615 channel->min_value = -1000;
616 channel->max_value = 1000;
617 playsink->colorbalance_channels =
618 g_list_append (playsink->colorbalance_channels, channel);
619 playsink->colorbalance_values[1] = 0;
622 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
624 channel->label = g_strdup ("HUE");
625 channel->min_value = -1000;
626 channel->max_value = 1000;
627 playsink->colorbalance_channels =
628 g_list_append (playsink->colorbalance_channels, channel);
629 playsink->colorbalance_values[2] = 0;
632 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
634 channel->label = g_strdup ("SATURATION");
635 channel->min_value = -1000;
636 channel->max_value = 1000;
637 playsink->colorbalance_channels =
638 g_list_append (playsink->colorbalance_channels, channel);
639 playsink->colorbalance_values[3] = 0;
643 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
647 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
650 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
656 free_chain (GstPlayChain * chain)
660 gst_object_unref (chain->bin);
666 gst_play_sink_dispose (GObject * object)
668 GstPlaySink *playsink;
670 playsink = GST_PLAY_SINK (object);
672 if (playsink->audio_sink != NULL) {
673 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
674 gst_object_unref (playsink->audio_sink);
675 playsink->audio_sink = NULL;
677 if (playsink->video_sink != NULL) {
678 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
679 gst_object_unref (playsink->video_sink);
680 playsink->video_sink = NULL;
682 if (playsink->visualisation != NULL) {
683 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
684 gst_object_unref (playsink->visualisation);
685 playsink->visualisation = NULL;
687 if (playsink->text_sink != NULL) {
688 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
689 gst_object_unref (playsink->text_sink);
690 playsink->text_sink = NULL;
693 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
694 playsink->videodeinterlacechain = NULL;
695 free_chain ((GstPlayChain *) playsink->videochain);
696 playsink->videochain = NULL;
697 free_chain ((GstPlayChain *) playsink->audiochain);
698 playsink->audiochain = NULL;
699 free_chain ((GstPlayChain *) playsink->vischain);
700 playsink->vischain = NULL;
701 free_chain ((GstPlayChain *) playsink->textchain);
702 playsink->textchain = NULL;
704 if (playsink->audio_tee_sink) {
705 gst_object_unref (playsink->audio_tee_sink);
706 playsink->audio_tee_sink = NULL;
709 if (playsink->audio_tee_vissrc) {
710 gst_element_release_request_pad (playsink->audio_tee,
711 playsink->audio_tee_vissrc);
712 gst_object_unref (playsink->audio_tee_vissrc);
713 playsink->audio_tee_vissrc = NULL;
716 if (playsink->audio_tee_asrc) {
717 gst_element_release_request_pad (playsink->audio_tee,
718 playsink->audio_tee_asrc);
719 gst_object_unref (playsink->audio_tee_asrc);
720 playsink->audio_tee_asrc = NULL;
723 g_free (playsink->font_desc);
724 playsink->font_desc = NULL;
726 g_free (playsink->subtitle_encoding);
727 playsink->subtitle_encoding = NULL;
729 playsink->stream_synchronizer = NULL;
731 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
733 g_list_free (playsink->colorbalance_channels);
734 playsink->colorbalance_channels = NULL;
736 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
740 gst_play_sink_finalize (GObject * object)
742 GstPlaySink *playsink;
744 playsink = GST_PLAY_SINK (object);
746 g_rec_mutex_clear (&playsink->lock);
748 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
752 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
755 GstElement **elem = NULL, *old = NULL;
757 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
759 GST_PLAY_SINK_LOCK (playsink);
761 case GST_PLAY_SINK_TYPE_AUDIO:
762 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
763 elem = &playsink->audio_sink;
765 case GST_PLAY_SINK_TYPE_VIDEO:
766 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
767 elem = &playsink->video_sink;
769 case GST_PLAY_SINK_TYPE_TEXT:
770 elem = &playsink->text_sink;
778 gst_object_ref (sink);
781 GST_PLAY_SINK_UNLOCK (playsink);
785 gst_element_set_state (old, GST_STATE_NULL);
786 gst_object_unref (old);
791 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
793 GstElement *result = NULL;
794 GstElement *elem = NULL, *chainp = NULL;
796 GST_PLAY_SINK_LOCK (playsink);
798 case GST_PLAY_SINK_TYPE_AUDIO:
799 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
801 GstPlayAudioChain *chain;
802 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
803 chainp = chain->sink;
804 elem = playsink->audio_sink;
807 case GST_PLAY_SINK_TYPE_VIDEO:
808 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
810 GstPlayVideoChain *chain;
811 if ((chain = (GstPlayVideoChain *) playsink->videochain))
812 chainp = chain->sink;
813 elem = playsink->video_sink;
816 case GST_PLAY_SINK_TYPE_TEXT:
818 GstPlayTextChain *chain;
819 if ((chain = (GstPlayTextChain *) playsink->textchain))
820 chainp = chain->sink;
821 elem = playsink->text_sink;
828 /* we have an active chain with a sink, get the sink */
829 result = gst_object_ref (chainp);
831 /* nothing found, return last configured sink */
832 if (result == NULL && elem)
833 result = gst_object_ref (elem);
834 GST_PLAY_SINK_UNLOCK (playsink);
839 static GstPadProbeReturn
840 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
843 GstPlaySink *playsink;
844 GstPlayVisChain *chain;
846 playsink = GST_PLAY_SINK (user_data);
848 GST_PLAY_SINK_LOCK (playsink);
849 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
850 /* now try to change the plugin in the running vis chain */
851 if (!(chain = (GstPlayVisChain *) playsink->vischain))
854 /* unlink the old plugin and unghost the pad */
855 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
856 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
858 /* set the old plugin to NULL and remove */
859 gst_element_set_state (chain->vis, GST_STATE_NULL);
860 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
862 /* add new plugin and set state to playing */
863 chain->vis = playsink->visualisation;
864 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
865 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
868 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
869 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
872 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
873 GST_PAD_LINK_CHECK_NOTHING);
874 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
878 GST_PLAY_SINK_UNLOCK (playsink);
880 /* remove the probe and unblock the pad */
881 return GST_PAD_PROBE_REMOVE;
885 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
887 GstPlayVisChain *chain;
889 /* setting NULL means creating the default vis plugin */
891 vis = gst_element_factory_make ("goom", "vis");
893 /* simply return if we don't have a vis plugin here */
897 GST_PLAY_SINK_LOCK (playsink);
898 /* first store the new vis */
899 if (playsink->visualisation)
900 gst_object_unref (playsink->visualisation);
902 gst_object_ref_sink (vis);
903 playsink->visualisation = vis;
905 /* now try to change the plugin in the running vis chain, if we have no chain,
906 * we don't bother, any future vis chain will be created with the new vis
908 if (!(chain = (GstPlayVisChain *) playsink->vischain))
911 /* block the pad, the next time the callback is called we can change the
912 * visualisation. It's possible that this never happens or that the pad was
913 * already blocked. If the callback never happens, we don't have new data so
914 * we don't need the new vis plugin. If the pad was already blocked, the
915 * function returns FALSE but the previous pad block will do the right thing
917 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
918 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
919 gst_play_sink_vis_blocked, playsink, NULL);
921 GST_PLAY_SINK_UNLOCK (playsink);
927 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
929 GstElement *result = NULL;
930 GstPlayVisChain *chain;
932 GST_PLAY_SINK_LOCK (playsink);
933 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
934 /* we have an active chain, get the sink */
936 result = gst_object_ref (chain->vis);
938 /* nothing found, return last configured sink */
939 if (result == NULL && playsink->visualisation)
940 result = gst_object_ref (playsink->visualisation);
941 GST_PLAY_SINK_UNLOCK (playsink);
947 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
949 GstPlayAudioChain *chain;
951 GST_PLAY_SINK_LOCK (playsink);
952 playsink->volume = volume;
953 chain = (GstPlayAudioChain *) playsink->audiochain;
954 if (chain && chain->volume) {
955 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
956 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
957 chain->mute, volume, playsink->mute);
958 /* if there is a mute element or we are not muted, set the volume */
959 if (chain->mute || !playsink->mute)
960 g_object_set (chain->volume, "volume", volume, NULL);
962 GST_LOG_OBJECT (playsink, "no volume element");
963 playsink->volume_changed = TRUE;
965 GST_PLAY_SINK_UNLOCK (playsink);
969 gst_play_sink_get_volume (GstPlaySink * playsink)
972 GstPlayAudioChain *chain;
974 GST_PLAY_SINK_LOCK (playsink);
975 chain = (GstPlayAudioChain *) playsink->audiochain;
976 result = playsink->volume;
977 if (chain && chain->volume) {
978 if (chain->mute || !playsink->mute) {
979 g_object_get (chain->volume, "volume", &result, NULL);
980 playsink->volume = result;
983 GST_PLAY_SINK_UNLOCK (playsink);
989 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
991 GstPlayAudioChain *chain;
993 GST_PLAY_SINK_LOCK (playsink);
994 playsink->mute = mute;
995 chain = (GstPlayAudioChain *) playsink->audiochain;
998 g_object_set (chain->mute, "mute", mute, NULL);
999 } else if (chain->volume) {
1001 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1003 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1007 playsink->mute_changed = TRUE;
1009 GST_PLAY_SINK_UNLOCK (playsink);
1013 gst_play_sink_get_mute (GstPlaySink * playsink)
1016 GstPlayAudioChain *chain;
1018 GST_PLAY_SINK_LOCK (playsink);
1019 chain = (GstPlayAudioChain *) playsink->audiochain;
1020 if (chain && chain->mute) {
1021 g_object_get (chain->mute, "mute", &result, NULL);
1022 playsink->mute = result;
1024 result = playsink->mute;
1026 GST_PLAY_SINK_UNLOCK (playsink);
1032 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1036 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1037 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1041 add_chain (GstPlayChain * chain, gboolean add)
1043 if (chain->added == add)
1047 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1049 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1050 /* we don't want to lose our sink status */
1051 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1060 activate_chain (GstPlayChain * chain, gboolean activate)
1064 if (chain->activated == activate)
1067 GST_OBJECT_LOCK (chain->playsink);
1068 state = GST_STATE_TARGET (chain->playsink);
1069 GST_OBJECT_UNLOCK (chain->playsink);
1072 gst_element_set_state (chain->bin, state);
1074 gst_element_set_state (chain->bin, GST_STATE_NULL);
1076 chain->activated = activate;
1082 element_is_sink (GstElement * element)
1086 GST_OBJECT_LOCK (element);
1087 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1088 GST_OBJECT_UNLOCK (element);
1090 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1095 element_has_property (GstElement * element, const gchar * pname, GType type)
1099 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1101 if (pspec == NULL) {
1102 GST_DEBUG_OBJECT (element, "no %s property", pname);
1106 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1107 g_type_is_a (pspec->value_type, type)) {
1108 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1109 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1113 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1114 "and we expected it to be of type %s", pname,
1115 g_type_name (pspec->value_type), g_type_name (type));
1122 const gchar *prop_name;
1125 } FindPropertyHelper;
1128 find_property (const GValue * item, FindPropertyHelper * helper)
1130 GstElement *element = g_value_get_object (item);
1131 if (helper->need_sink && !element_is_sink (element)) {
1135 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1139 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1140 (helper->need_sink) ? "sink" : "element");
1141 return 0; /* keep it */
1144 /* FIXME: why not move these functions into core? */
1145 /* find a sink in the hierarchy with a property named @name. This function does
1146 * not increase the refcount of the returned object and thus remains valid as
1147 * long as the bin is valid. */
1149 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1150 const gchar * name, GType expected_type)
1152 GstElement *result = NULL;
1155 if (element_has_property (obj, name, expected_type)) {
1157 } else if (GST_IS_BIN (obj)) {
1159 GValue item = { 0, };
1160 FindPropertyHelper helper = { name, expected_type, TRUE };
1162 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1163 found = gst_iterator_find_custom (it,
1164 (GCompareFunc) find_property, &item, &helper);
1165 gst_iterator_free (it);
1167 result = g_value_get_object (&item);
1168 /* we don't need the extra ref */
1169 g_value_unset (&item);
1175 /* find an object in the hierarchy with a property named @name */
1177 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1178 const gchar * name, GType expected_type)
1180 GstElement *result = NULL;
1183 if (GST_IS_BIN (obj)) {
1185 GValue item = { 0, };
1186 FindPropertyHelper helper = { name, expected_type, FALSE };
1188 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1189 found = gst_iterator_find_custom (it,
1190 (GCompareFunc) find_property, &item, &helper);
1191 gst_iterator_free (it);
1193 result = g_value_dup_object (&item);
1194 g_value_unset (&item);
1197 if (element_has_property (obj, name, expected_type)) {
1199 gst_object_ref (obj);
1206 do_async_start (GstPlaySink * playsink)
1208 GstMessage *message;
1210 if (!playsink->need_async_start) {
1211 GST_INFO_OBJECT (playsink, "no async_start needed");
1215 playsink->async_pending = TRUE;
1217 GST_INFO_OBJECT (playsink, "Sending async_start message");
1218 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1219 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1220 (playsink), message);
1224 do_async_done (GstPlaySink * playsink)
1226 GstMessage *message;
1228 if (playsink->async_pending) {
1229 GST_INFO_OBJECT (playsink, "Sending async_done message");
1230 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1231 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1232 (playsink), message);
1234 playsink->async_pending = FALSE;
1237 playsink->need_async_start = FALSE;
1240 /* try to change the state of an element. This function returns the element when
1241 * the state change could be performed. When this function returns NULL an error
1242 * occured and the element is unreffed if @unref is TRUE. */
1244 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1246 GstStateChangeReturn ret;
1249 ret = gst_element_set_state (element, GST_STATE_READY);
1250 if (ret == GST_STATE_CHANGE_FAILURE) {
1251 GST_DEBUG_OBJECT (playsink, "failed state change..");
1252 gst_element_set_state (element, GST_STATE_NULL);
1254 gst_object_unref (element);
1261 /* make the element (bin) that contains the elements needed to perform
1262 * video display. Only used for *raw* video streams.
1264 * +------------------------------------------------------------+
1266 * | +-------+ +----------+ +----------+ +---------+ |
1267 * | | queue | |colorspace| |videoscale| |videosink| |
1268 * | +-sink src-sink src-sink src-sink | |
1269 * | | +-------+ +----------+ +----------+ +---------+ |
1271 * +------------------------------------------------------------+
1274 static GstPlayVideoDeinterlaceChain *
1275 gen_video_deinterlace_chain (GstPlaySink * playsink)
1277 GstPlayVideoDeinterlaceChain *chain;
1280 GstElement *head = NULL, *prev = NULL;
1282 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1283 chain->chain.playsink = playsink;
1285 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1287 /* create a bin to hold objects, as we create them we add them to this bin so
1288 * that when something goes wrong we only need to unref the bin */
1289 chain->chain.bin = gst_bin_new ("vdbin");
1290 bin = GST_BIN_CAST (chain->chain.bin);
1291 gst_object_ref_sink (bin);
1293 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1294 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1295 if (chain->conv == NULL) {
1296 post_missing_element_message (playsink, COLORSPACE);
1297 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1298 (_("Missing element '%s' - check your GStreamer installation."),
1299 COLORSPACE), ("video rendering might fail"));
1301 gst_bin_add (bin, chain->conv);
1306 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1307 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1308 if (chain->deinterlace == NULL) {
1309 post_missing_element_message (playsink, "deinterlace");
1310 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1311 (_("Missing element '%s' - check your GStreamer installation."),
1312 "deinterlace"), ("deinterlacing won't work"));
1314 gst_bin_add (bin, chain->deinterlace);
1316 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1317 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1320 head = chain->deinterlace;
1322 prev = chain->deinterlace;
1326 pad = gst_element_get_static_pad (head, "sink");
1327 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1328 gst_object_unref (pad);
1330 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1334 pad = gst_element_get_static_pad (prev, "src");
1335 chain->srcpad = gst_ghost_pad_new ("src", pad);
1336 gst_object_unref (pad);
1338 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1341 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1342 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1348 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1349 (NULL), ("Failed to configure the video deinterlace chain."));
1350 free_chain ((GstPlayChain *) chain);
1356 is_valid_color_balance_element (GstColorBalance * bal)
1358 gboolean have_brightness = FALSE;
1359 gboolean have_contrast = FALSE;
1360 gboolean have_hue = FALSE;
1361 gboolean have_saturation = FALSE;
1362 const GList *channels, *l;
1364 channels = gst_color_balance_list_channels (bal);
1365 for (l = channels; l; l = l->next) {
1366 GstColorBalanceChannel *ch = l->data;
1368 if (g_strrstr (ch->label, "BRIGHTNESS"))
1369 have_brightness = TRUE;
1370 else if (g_strrstr (ch->label, "CONTRAST"))
1371 have_contrast = TRUE;
1372 else if (g_strrstr (ch->label, "HUE"))
1374 else if (g_strrstr (ch->label, "SATURATION"))
1375 have_saturation = TRUE;
1378 return have_brightness && have_contrast && have_hue && have_saturation;
1382 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1385 GstColorBalance *cb, **cb_out = user_data;
1387 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1388 valid = is_valid_color_balance_element (cb);
1391 && gst_color_balance_get_balance_type (*cb_out) ==
1392 GST_COLOR_BALANCE_SOFTWARE) {
1393 gst_object_unref (*cb_out);
1394 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1395 } else if (!*cb_out) {
1396 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1401 static GstColorBalance *
1402 find_color_balance_element (GstElement * element)
1405 GstColorBalance *cb = NULL;
1407 if (GST_IS_COLOR_BALANCE (element)
1408 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1409 return GST_COLOR_BALANCE (gst_object_ref (element));
1410 else if (!GST_IS_BIN (element))
1413 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1414 GST_TYPE_COLOR_BALANCE);
1415 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1416 &cb) == GST_ITERATOR_RESYNC)
1417 gst_iterator_resync (it);
1418 gst_iterator_free (it);
1424 colorbalance_value_changed_cb (GstColorBalance * balance,
1425 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1430 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1431 GstColorBalanceChannel *proxy = l->data;
1433 if (g_strrstr (channel->label, proxy->label)) {
1436 /* Convert to [0, 1] range */
1439 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1440 (gdouble) channel->min_value);
1441 /* Convert to proxy range */
1443 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1444 (gdouble) proxy->min_value);
1445 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1447 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1448 playsink->colorbalance_values[i]);
1455 update_colorbalance (GstPlaySink * playsink)
1457 GstColorBalance *balance = NULL;
1461 GST_OBJECT_LOCK (playsink);
1462 if (playsink->colorbalance_element) {
1464 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1466 GST_OBJECT_UNLOCK (playsink);
1470 g_signal_handlers_block_by_func (balance,
1471 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1473 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1474 GstColorBalanceChannel *proxy = l->data;
1475 GstColorBalanceChannel *channel = NULL;
1476 const GList *channels, *k;
1478 channels = gst_color_balance_list_channels (balance);
1479 for (k = channels; k; k = k->next) {
1480 GstColorBalanceChannel *tmp = k->data;
1482 if (g_strrstr (tmp->label, proxy->label)) {
1490 gst_color_balance_set_value (balance, channel,
1491 playsink->colorbalance_values[i]);
1494 g_signal_handlers_unblock_by_func (balance,
1495 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1497 gst_object_unref (balance);
1500 /* make the element (bin) that contains the elements needed to perform
1503 * +------------------------------------------------------------+
1505 * | +-------+ +----------+ +----------+ +---------+ |
1506 * | | queue | |colorspace| |videoscale| |videosink| |
1507 * | +-sink src-sink src-sink src-sink | |
1508 * | | +-------+ +----------+ +----------+ +---------+ |
1510 * +------------------------------------------------------------+
1513 static GstPlayVideoChain *
1514 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1516 GstPlayVideoChain *chain;
1519 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1521 chain = g_new0 (GstPlayVideoChain, 1);
1522 chain->chain.playsink = playsink;
1523 chain->chain.raw = raw;
1525 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1527 if (playsink->video_sink) {
1528 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1529 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1531 /* only try fallback if no specific sink was chosen */
1532 if (chain->sink == NULL) {
1533 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1534 elem = gst_element_factory_make ("autovideosink", "videosink");
1535 chain->sink = try_element (playsink, elem, TRUE);
1537 if (chain->sink == NULL) {
1538 /* if default sink from config.h is different then try it too */
1539 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1540 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1541 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1542 chain->sink = try_element (playsink, elem, TRUE);
1546 playsink->video_sink = gst_object_ref (chain->sink);
1548 if (chain->sink == NULL)
1552 /* if we can disable async behaviour of the sink, we can avoid adding a
1553 * queue for the audio chain. */
1555 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1558 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1559 async, GST_ELEMENT_NAME (elem));
1560 g_object_set (elem, "async", async, NULL);
1561 chain->async = async;
1563 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1564 chain->async = TRUE;
1567 /* Make sure the aspect ratio is kept */
1569 gst_play_sink_find_property_sinks (playsink, chain->sink,
1570 "force-aspect-ratio", G_TYPE_BOOLEAN);
1572 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1574 /* find ts-offset element */
1575 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1576 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1579 /* create a bin to hold objects, as we create them we add them to this bin so
1580 * that when something goes wrong we only need to unref the bin */
1581 chain->chain.bin = gst_bin_new ("vbin");
1582 bin = GST_BIN_CAST (chain->chain.bin);
1583 gst_object_ref_sink (bin);
1584 gst_bin_add (bin, chain->sink);
1586 /* Get the VideoOverlay element */
1588 GstVideoOverlay *overlay = NULL;
1590 GST_OBJECT_LOCK (playsink);
1591 if (playsink->overlay_element)
1592 gst_object_unref (playsink->overlay_element);
1593 playsink->overlay_element =
1594 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1595 GST_TYPE_VIDEO_OVERLAY));
1596 if (playsink->overlay_element)
1597 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1598 GST_OBJECT_UNLOCK (playsink);
1601 if (playsink->overlay_handle_set)
1602 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1603 if (playsink->overlay_handle_events_set)
1604 gst_video_overlay_handle_events (overlay,
1605 playsink->overlay_handle_events);
1606 if (playsink->overlay_render_rectangle_set)
1607 gst_video_overlay_set_render_rectangle (overlay,
1608 playsink->overlay_x, playsink->overlay_y,
1609 playsink->overlay_width, playsink->overlay_height);
1610 gst_object_unref (overlay);
1614 /* decouple decoder from sink, this improves playback quite a lot since the
1615 * decoder can continue while the sink blocks for synchronisation. We don't
1616 * need a lot of buffers as this consumes a lot of memory and we don't want
1617 * too little because else we would be context switching too quickly. */
1618 chain->queue = gst_element_factory_make ("queue", "vqueue");
1619 if (chain->queue == NULL) {
1620 post_missing_element_message (playsink, "queue");
1621 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1622 (_("Missing element '%s' - check your GStreamer installation."),
1623 "queue"), ("video rendering might be suboptimal"));
1627 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1628 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1629 gst_bin_add (bin, chain->queue);
1630 head = prev = chain->queue;
1633 GST_OBJECT_LOCK (playsink);
1634 if (playsink->colorbalance_element) {
1635 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1636 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1637 gst_object_unref (playsink->colorbalance_element);
1639 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1640 GST_OBJECT_UNLOCK (playsink);
1642 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1643 || (!playsink->colorbalance_element
1644 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1645 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1646 gboolean use_balance = !playsink->colorbalance_element
1647 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1649 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1651 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1652 "use-converters", use_converters, "use-balance", use_balance, NULL);
1654 GST_OBJECT_LOCK (playsink);
1655 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1656 playsink->colorbalance_element =
1657 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1658 (chain->conv)->balance));
1659 GST_OBJECT_UNLOCK (playsink);
1661 gst_bin_add (bin, chain->conv);
1663 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1664 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1672 update_colorbalance (playsink);
1675 GST_DEBUG_OBJECT (playsink, "linking to sink");
1676 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1677 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1681 pad = gst_element_get_static_pad (head, "sink");
1682 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1683 gst_object_unref (pad);
1685 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1692 if (!elem && !playsink->video_sink) {
1693 post_missing_element_message (playsink, "autovideosink");
1694 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1695 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1696 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1697 (_("Both autovideosink and %s elements are missing."),
1698 DEFAULT_VIDEOSINK), (NULL));
1700 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1701 (_("The autovideosink element is missing.")), (NULL));
1704 if (playsink->video_sink) {
1705 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1706 (_("Configured videosink %s is not working."),
1707 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1708 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1709 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1710 (_("Both autovideosink and %s elements are not working."),
1711 DEFAULT_VIDEOSINK), (NULL));
1713 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1714 (_("The autovideosink element is not working.")), (NULL));
1717 free_chain ((GstPlayChain *) chain);
1723 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1724 (NULL), ("Failed to configure the video sink."));
1725 /* checking sink made it READY */
1726 gst_element_set_state (chain->sink, GST_STATE_NULL);
1727 /* Remove chain from the bin to allow reuse later */
1728 gst_bin_remove (bin, chain->sink);
1729 free_chain ((GstPlayChain *) chain);
1735 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1738 GstPlayVideoChain *chain;
1739 GstStateChangeReturn ret;
1741 chain = playsink->videochain;
1743 chain->chain.raw = raw;
1745 /* if the chain was active we don't do anything */
1746 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1749 /* try to set the sink element to READY again */
1750 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1751 if (ret == GST_STATE_CHANGE_FAILURE)
1754 /* Get the VideoOverlay element */
1756 GstVideoOverlay *overlay = NULL;
1758 GST_OBJECT_LOCK (playsink);
1759 if (playsink->overlay_element)
1760 gst_object_unref (playsink->overlay_element);
1761 playsink->overlay_element =
1762 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1763 GST_TYPE_VIDEO_OVERLAY));
1764 if (playsink->overlay_element)
1765 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1766 GST_OBJECT_UNLOCK (playsink);
1769 if (playsink->overlay_handle_set)
1770 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1771 if (playsink->overlay_handle_events_set)
1772 gst_video_overlay_handle_events (overlay,
1773 playsink->overlay_handle_events);
1774 if (playsink->overlay_render_rectangle_set)
1775 gst_video_overlay_set_render_rectangle (overlay,
1776 playsink->overlay_x, playsink->overlay_y,
1777 playsink->overlay_width, playsink->overlay_height);
1778 gst_object_unref (overlay);
1782 /* find ts-offset element */
1783 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1784 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1787 /* if we can disable async behaviour of the sink, we can avoid adding a
1788 * queue for the audio chain. */
1790 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1793 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1794 async, GST_ELEMENT_NAME (elem));
1795 g_object_set (elem, "async", async, NULL);
1796 chain->async = async;
1798 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1799 chain->async = TRUE;
1802 /* Make sure the aspect ratio is kept */
1804 gst_play_sink_find_property_sinks (playsink, chain->sink,
1805 "force-aspect-ratio", G_TYPE_BOOLEAN);
1807 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1809 GST_OBJECT_LOCK (playsink);
1810 if (playsink->colorbalance_element) {
1811 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1812 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1813 gst_object_unref (playsink->colorbalance_element);
1815 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1816 GST_OBJECT_UNLOCK (playsink);
1819 gboolean use_balance = !playsink->colorbalance_element
1820 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1822 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1824 GST_OBJECT_LOCK (playsink);
1825 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1826 playsink->colorbalance_element =
1827 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1828 (chain->conv)->balance));
1829 GST_OBJECT_UNLOCK (playsink);
1832 update_colorbalance (playsink);
1837 /* make an element for playback of video with subtitles embedded.
1838 * Only used for *raw* video streams.
1840 * +--------------------------------------------+
1842 * | +--------+ +-----------------+ |
1843 * | | queue | | subtitleoverlay | |
1844 * video--src sink---video_sink | |
1845 * | +--------+ | src--src
1846 * text------------------text_sink | |
1847 * | +-----------------+ |
1848 * +--------------------------------------------+
1851 static GstPlayTextChain *
1852 gen_text_chain (GstPlaySink * playsink)
1854 GstPlayTextChain *chain;
1857 GstPad *videosinkpad, *textsinkpad, *srcpad;
1859 chain = g_new0 (GstPlayTextChain, 1);
1860 chain->chain.playsink = playsink;
1862 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
1864 chain->chain.bin = gst_bin_new ("tbin");
1865 bin = GST_BIN_CAST (chain->chain.bin);
1866 gst_object_ref_sink (bin);
1868 videosinkpad = textsinkpad = srcpad = NULL;
1870 /* first try to hook the text pad to the custom sink */
1871 if (playsink->text_sink) {
1872 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
1873 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
1876 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1879 /* make sure the sparse subtitles don't participate in the preroll */
1880 g_object_set (elem, "async", FALSE, NULL);
1881 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
1882 gst_bin_add (bin, chain->sink);
1883 /* NOTE streamsynchronizer needs streams decoupled */
1884 /* make a little queue */
1885 chain->queue = gst_element_factory_make ("queue", "subqueue");
1886 if (chain->queue == NULL) {
1887 post_missing_element_message (playsink, "queue");
1888 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1889 (_("Missing element '%s' - check your GStreamer installation."),
1890 "queue"), ("rendering might be suboptimal"));
1892 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1893 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1894 "silent", TRUE, NULL);
1895 gst_bin_add (bin, chain->queue);
1897 /* we have a custom sink, this will be our textsinkpad */
1898 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
1899 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1900 /* we're all fine now and we can add the sink to the chain */
1901 GST_DEBUG_OBJECT (playsink, "using custom text sink");
1902 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
1904 GST_WARNING_OBJECT (playsink,
1905 "can't find a sink pad on custom text sink");
1906 gst_bin_remove (bin, chain->sink);
1907 gst_bin_remove (bin, chain->queue);
1909 chain->queue = NULL;
1911 /* try to set sync to true but it's no biggie when we can't */
1912 if (chain->sink && (elem =
1913 gst_play_sink_find_property_sinks (playsink, chain->sink,
1914 "sync", G_TYPE_BOOLEAN)))
1915 g_object_set (elem, "sync", TRUE, NULL);
1918 gst_bin_remove (bin, chain->sink);
1920 GST_WARNING_OBJECT (playsink,
1921 "can't find async property in custom text sink");
1924 if (textsinkpad == NULL) {
1925 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1926 (_("Custom text sink element is not usable.")),
1927 ("fallback to default subtitleoverlay"));
1931 if (textsinkpad == NULL) {
1932 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
1933 /* make a little queue */
1934 chain->queue = gst_element_factory_make ("queue", "vqueue");
1935 if (chain->queue == NULL) {
1936 post_missing_element_message (playsink, "queue");
1937 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1938 (_("Missing element '%s' - check your GStreamer installation."),
1939 "queue"), ("video rendering might be suboptimal"));
1941 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1942 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1943 "silent", TRUE, NULL);
1944 gst_bin_add (bin, chain->queue);
1945 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
1949 gst_element_factory_make ("subtitleoverlay", "suboverlay");
1950 if (chain->overlay == NULL) {
1951 post_missing_element_message (playsink, "subtitleoverlay");
1952 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1953 (_("Missing element '%s' - check your GStreamer installation."),
1954 "subtitleoverlay"), ("subtitle rendering disabled"));
1956 GstElement *element;
1958 gst_bin_add (bin, chain->overlay);
1960 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
1961 if (playsink->font_desc) {
1962 g_object_set (G_OBJECT (chain->overlay), "font-desc",
1963 playsink->font_desc, NULL);
1965 if (playsink->subtitle_encoding) {
1966 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
1967 playsink->subtitle_encoding, NULL);
1970 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
1971 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
1973 /* make another little queue to decouple streams */
1974 element = gst_element_factory_make ("queue", "subqueue");
1975 if (element == NULL) {
1976 post_missing_element_message (playsink, "queue");
1977 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1978 (_("Missing element '%s' - check your GStreamer installation."),
1979 "queue"), ("rendering might be suboptimal"));
1981 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
1982 "max-size-bytes", 0, "max-size-time", (gint64) 0,
1983 "silent", TRUE, NULL);
1984 gst_bin_add (bin, element);
1985 if (gst_element_link_pads_full (element, "src", chain->overlay,
1986 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1987 textsinkpad = gst_element_get_static_pad (element, "sink");
1988 srcpad = gst_element_get_static_pad (chain->overlay, "src");
1990 gst_bin_remove (bin, chain->sink);
1991 gst_bin_remove (bin, chain->overlay);
1993 chain->overlay = NULL;
1994 gst_object_unref (videosinkpad);
1995 videosinkpad = NULL;
2002 if (videosinkpad == NULL) {
2003 /* if we still don't have a videosink, we don't have an overlay. the only
2004 * thing we can do is insert an identity and ghost the src
2006 chain->identity = gst_element_factory_make ("identity", "tidentity");
2007 if (chain->identity == NULL) {
2008 post_missing_element_message (playsink, "identity");
2009 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2010 (_("Missing element '%s' - check your GStreamer installation."),
2011 "identity"), (NULL));
2013 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2014 g_object_set (chain->identity, "silent", TRUE, NULL);
2015 gst_bin_add (bin, chain->identity);
2016 srcpad = gst_element_get_static_pad (chain->identity, "src");
2017 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2021 /* expose the ghostpads */
2023 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2024 gst_object_unref (videosinkpad);
2025 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2028 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2029 gst_object_unref (textsinkpad);
2030 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2033 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2034 gst_object_unref (srcpad);
2035 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2042 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2046 g_object_get (object, "volume", &vol, NULL);
2047 playsink->volume = vol;
2049 g_object_notify (G_OBJECT (playsink), "volume");
2053 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2057 g_object_get (object, "mute", &mute, NULL);
2058 playsink->mute = mute;
2060 g_object_notify (G_OBJECT (playsink), "mute");
2063 /* make the chain that contains the elements needed to perform
2066 * We add a tee as the first element so that we can link the visualisation chain
2067 * to it when requested.
2069 * +-------------------------------------------------------------+
2071 * | +---------+ +----------+ +---------+ +---------+ |
2072 * | |audioconv| |audioscale| | volume | |audiosink| |
2073 * | +-srck src-sink src-sink src-sink | |
2074 * | | +---------+ +----------+ +---------+ +---------+ |
2076 * +-------------------------------------------------------------+
2078 static GstPlayAudioChain *
2079 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2081 GstPlayAudioChain *chain;
2083 gboolean have_volume;
2085 GstElement *head, *prev, *elem = NULL;
2087 chain = g_new0 (GstPlayAudioChain, 1);
2088 chain->chain.playsink = playsink;
2089 chain->chain.raw = raw;
2091 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2093 if (playsink->audio_sink) {
2094 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2095 playsink->audio_sink);
2096 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2098 /* only try fallback if no specific sink was chosen */
2099 if (chain->sink == NULL) {
2100 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2101 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2102 chain->sink = try_element (playsink, elem, TRUE);
2104 if (chain->sink == NULL) {
2105 /* if default sink from config.h is different then try it too */
2106 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2107 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2108 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2109 chain->sink = try_element (playsink, elem, TRUE);
2113 playsink->audio_sink = gst_object_ref (chain->sink);
2115 if (chain->sink == NULL)
2118 chain->chain.bin = gst_bin_new ("abin");
2119 bin = GST_BIN_CAST (chain->chain.bin);
2120 gst_object_ref_sink (bin);
2121 gst_bin_add (bin, chain->sink);
2123 /* we have to add a queue when we need to decouple for the video sink in
2124 * visualisations and for streamsynchronizer */
2125 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2126 chain->queue = gst_element_factory_make ("queue", "aqueue");
2127 if (chain->queue == NULL) {
2128 post_missing_element_message (playsink, "queue");
2129 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2130 (_("Missing element '%s' - check your GStreamer installation."),
2131 "queue"), ("audio playback and visualizations might not work"));
2135 g_object_set (chain->queue, "silent", TRUE, NULL);
2136 gst_bin_add (bin, chain->queue);
2137 prev = head = chain->queue;
2140 /* find ts-offset element */
2141 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2142 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2145 /* check if the sink, or something within the sink, has the volume property.
2146 * If it does we don't need to add a volume element. */
2148 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2151 chain->volume = elem;
2153 g_signal_connect (chain->volume, "notify::volume",
2154 G_CALLBACK (notify_volume_cb), playsink);
2156 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2158 chain->sink_volume = TRUE;
2159 /* if the sink also has a mute property we can use this as well. We'll only
2160 * use the mute property if there is a volume property. We can simulate the
2161 * mute with the volume otherwise. */
2163 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2166 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2167 g_signal_connect (chain->mute, "notify::mute",
2168 G_CALLBACK (notify_mute_cb), playsink);
2170 /* use the sink to control the volume and mute */
2171 if (playsink->volume_changed) {
2172 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2173 playsink->volume_changed = FALSE;
2175 if (playsink->mute_changed) {
2177 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2180 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2182 playsink->mute_changed = FALSE;
2185 /* no volume, we need to add a volume element when we can */
2186 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2187 have_volume = FALSE;
2188 chain->sink_volume = FALSE;
2191 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2192 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2193 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2194 gboolean use_volume =
2195 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2196 GST_DEBUG_OBJECT (playsink,
2197 "creating audioconvert with use-converters %d, use-volume %d",
2198 use_converters, use_volume);
2200 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2201 "use-converters", use_converters, "use-volume", use_volume, NULL);
2202 gst_bin_add (bin, chain->conv);
2204 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2205 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2212 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2213 GstPlaySinkAudioConvert *conv =
2214 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2217 chain->volume = conv->volume;
2220 g_signal_connect (chain->volume, "notify::volume",
2221 G_CALLBACK (notify_volume_cb), playsink);
2223 /* volume also has the mute property */
2224 chain->mute = chain->volume;
2225 g_signal_connect (chain->mute, "notify::mute",
2226 G_CALLBACK (notify_mute_cb), playsink);
2228 /* configure with the latest volume and mute */
2229 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2231 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2237 /* we only have to link to the previous element if we have something in
2238 * front of the sink */
2239 GST_DEBUG_OBJECT (playsink, "linking to sink");
2240 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2241 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2245 /* post a warning if we have no way to configure the volume */
2247 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2248 (_("No volume control found")), ("Volume/mute is not available"));
2251 /* and ghost the sinkpad of the headmost element */
2252 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2253 pad = gst_element_get_static_pad (head, "sink");
2254 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2255 gst_object_unref (pad);
2256 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2263 if (!elem && !playsink->audio_sink) {
2264 post_missing_element_message (playsink, "autoaudiosink");
2265 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2266 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2267 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2268 (_("Both autoaudiosink and %s elements are missing."),
2269 DEFAULT_AUDIOSINK), (NULL));
2271 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2272 (_("The autoaudiosink element is missing.")), (NULL));
2275 if (playsink->audio_sink) {
2276 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2277 (_("Configured audiosink %s is not working."),
2278 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2279 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2280 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2281 (_("Both autoaudiosink and %s elements are not working."),
2282 DEFAULT_AUDIOSINK), (NULL));
2284 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2285 (_("The autoaudiosink element is not working.")), (NULL));
2288 free_chain ((GstPlayChain *) chain);
2293 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2294 (NULL), ("Failed to configure the audio sink."));
2295 /* checking sink made it READY */
2296 gst_element_set_state (chain->sink, GST_STATE_NULL);
2297 /* Remove chain from the bin to allow reuse later */
2298 gst_bin_remove (bin, chain->sink);
2299 free_chain ((GstPlayChain *) chain);
2305 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2308 GstPlayAudioChain *chain;
2309 GstStateChangeReturn ret;
2310 GstPlaySinkAudioConvert *conv;
2312 chain = playsink->audiochain;
2313 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2315 chain->chain.raw = raw;
2317 /* if the chain was active we don't do anything */
2318 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2321 /* try to set the sink element to READY again */
2322 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2323 if (ret == GST_STATE_CHANGE_FAILURE)
2326 /* find ts-offset element */
2327 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2328 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2331 /* check if the sink, or something within the sink, has the volume property.
2332 * If it does we don't need to add a volume element. */
2334 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2337 chain->volume = elem;
2339 if (playsink->volume_changed) {
2340 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2342 /* use the sink to control the volume */
2343 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2344 playsink->volume_changed = FALSE;
2347 g_signal_connect (chain->volume, "notify::volume",
2348 G_CALLBACK (notify_volume_cb), playsink);
2349 /* if the sink also has a mute property we can use this as well. We'll only
2350 * use the mute property if there is a volume property. We can simulate the
2351 * mute with the volume otherwise. */
2353 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2356 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2357 g_signal_connect (chain->mute, "notify::mute",
2358 G_CALLBACK (notify_mute_cb), playsink);
2361 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2363 /* no volume, we need to add a volume element when we can */
2364 g_object_set (chain->conv, "use-volume",
2365 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2366 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2368 /* Disconnect signals */
2369 disconnect_chain (chain, playsink);
2371 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2372 chain->volume = conv->volume;
2373 chain->mute = chain->volume;
2375 g_signal_connect (chain->volume, "notify::volume",
2376 G_CALLBACK (notify_volume_cb), playsink);
2378 g_signal_connect (chain->mute, "notify::mute",
2379 G_CALLBACK (notify_mute_cb), playsink);
2381 /* configure with the latest volume and mute */
2382 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2383 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2386 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2392 * +-------------------------------------------------------------------+
2394 * | +----------+ +------------+ +----------+ +-------+ |
2395 * | | visqueue | | audioconv | | audiores | | vis | |
2396 * | +-sink src-sink + samp src-sink src-sink src-+ |
2397 * | | +----------+ +------------+ +----------+ +-------+ | |
2399 * +-------------------------------------------------------------------+
2402 static GstPlayVisChain *
2403 gen_vis_chain (GstPlaySink * playsink)
2405 GstPlayVisChain *chain;
2411 chain = g_new0 (GstPlayVisChain, 1);
2412 chain->chain.playsink = playsink;
2414 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2416 chain->chain.bin = gst_bin_new ("visbin");
2417 bin = GST_BIN_CAST (chain->chain.bin);
2418 gst_object_ref_sink (bin);
2420 /* we're queuing raw audio here, we can remove this queue when we can disable
2421 * async behaviour in the video sink. */
2422 chain->queue = gst_element_factory_make ("queue", "visqueue");
2423 if (chain->queue == NULL)
2425 g_object_set (chain->queue, "silent", TRUE, NULL);
2426 gst_bin_add (bin, chain->queue);
2428 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2429 if (chain->conv == NULL)
2430 goto no_audioconvert;
2431 gst_bin_add (bin, chain->conv);
2433 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2434 if (chain->resample == NULL)
2435 goto no_audioresample;
2436 gst_bin_add (bin, chain->resample);
2438 /* this pad will be used for blocking the dataflow and switching the vis
2440 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2442 if (playsink->visualisation) {
2443 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2444 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2446 if (chain->vis == NULL) {
2447 GST_DEBUG_OBJECT (playsink, "trying goom");
2448 elem = gst_element_factory_make ("goom", "vis");
2449 chain->vis = try_element (playsink, elem, TRUE);
2451 if (chain->vis == NULL)
2454 gst_bin_add (bin, chain->vis);
2456 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2457 GST_PAD_LINK_CHECK_NOTHING);
2459 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2460 GST_PAD_LINK_CHECK_NOTHING);
2462 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2463 GST_PAD_LINK_CHECK_NOTHING);
2467 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2468 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2470 pad = gst_element_get_static_pad (chain->queue, "sink");
2471 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2472 gst_object_unref (pad);
2473 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2475 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2476 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2483 post_missing_element_message (playsink, "queue");
2484 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2485 (_("Missing element '%s' - check your GStreamer installation."),
2487 free_chain ((GstPlayChain *) chain);
2492 post_missing_element_message (playsink, "audioconvert");
2493 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2494 (_("Missing element '%s' - check your GStreamer installation."),
2495 "audioconvert"), ("possibly a liboil version mismatch?"));
2496 free_chain ((GstPlayChain *) chain);
2501 post_missing_element_message (playsink, "audioresample");
2502 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2503 (_("Missing element '%s' - check your GStreamer installation."),
2504 "audioresample"), (NULL));
2505 free_chain ((GstPlayChain *) chain);
2510 post_missing_element_message (playsink, "goom");
2511 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2512 (_("Missing element '%s' - check your GStreamer installation."),
2514 free_chain ((GstPlayChain *) chain);
2519 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2520 (NULL), ("Failed to configure the visualisation element."));
2521 /* element made it to READY */
2522 gst_element_set_state (chain->vis, GST_STATE_NULL);
2523 free_chain ((GstPlayChain *) chain);
2528 /* this function is called when all the request pads are requested and when we
2529 * have to construct the final pipeline. Based on the flags we construct the
2530 * final output pipelines.
2533 gst_play_sink_reconfigure (GstPlaySink * playsink)
2536 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2538 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2540 /* assume we need nothing */
2541 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2543 GST_PLAY_SINK_LOCK (playsink);
2544 GST_OBJECT_LOCK (playsink);
2545 /* get flags, there are protected with the object lock */
2546 flags = playsink->flags;
2547 GST_OBJECT_UNLOCK (playsink);
2549 /* figure out which components we need */
2550 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2551 /* we have subtitles and we are requested to show it */
2555 GST_OBJECT_LOCK (playsink);
2556 if (playsink->overlay_element)
2557 gst_object_unref (playsink->overlay_element);
2558 playsink->overlay_element = NULL;
2560 if (playsink->colorbalance_element) {
2561 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2562 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2563 gst_object_unref (playsink->colorbalance_element);
2565 playsink->colorbalance_element = NULL;
2566 GST_OBJECT_UNLOCK (playsink);
2568 if (((flags & GST_PLAY_FLAG_VIDEO)
2569 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2570 /* we have video and we are requested to show it */
2573 /* we only deinterlace if native video is not requested and
2574 * we have raw video */
2575 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2576 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2577 need_deinterlace = TRUE;
2580 if (playsink->audio_pad) {
2581 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2584 if (playsink->audio_pad_raw) {
2585 /* only can do vis with raw uncompressed audio */
2586 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2587 /* also add video when we add visualisation */
2594 /* we have a text_pad and we need text rendering, in this case we need a
2595 * video_pad to combine the video with the text or visualizations */
2596 if (need_text && !need_video && !playsink->text_sink) {
2597 if (playsink->video_pad) {
2599 } else if (need_audio) {
2600 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2601 (_("Can't play a text file without video or visualizations.")),
2602 ("Have text pad but no video pad or visualizations"));
2605 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2606 (_("Can't play a text file without video or visualizations.")),
2607 ("Have text pad but no video pad or visualizations"));
2608 GST_PLAY_SINK_UNLOCK (playsink);
2613 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2614 need_video, need_vis, need_text);
2616 /* set up video pipeline */
2618 gboolean raw, async;
2620 /* we need a raw sink when we do vis or when we have a raw pad */
2621 raw = need_vis ? TRUE : playsink->video_pad_raw;
2622 /* we try to set the sink async=FALSE when we need vis, this way we can
2623 * avoid a queue in the audio chain. */
2626 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
2627 playsink->video_pad_raw);
2629 if (playsink->videochain) {
2630 /* try to reactivate the chain */
2631 if (!setup_video_chain (playsink, raw, async)) {
2632 if (playsink->video_sinkpad_stream_synchronizer) {
2633 gst_element_release_request_pad (GST_ELEMENT_CAST
2634 (playsink->stream_synchronizer),
2635 playsink->video_sinkpad_stream_synchronizer);
2636 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2637 playsink->video_sinkpad_stream_synchronizer = NULL;
2638 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2639 playsink->video_srcpad_stream_synchronizer = NULL;
2642 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2644 /* Remove the sink from the bin to keep its state
2645 * and unparent it to allow reuse */
2646 if (playsink->videochain->sink)
2647 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
2648 playsink->videochain->sink);
2650 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2651 free_chain ((GstPlayChain *) playsink->videochain);
2652 playsink->videochain = NULL;
2656 if (!playsink->videochain)
2657 playsink->videochain = gen_video_chain (playsink, raw, async);
2658 if (!playsink->videochain)
2661 if (!playsink->video_sinkpad_stream_synchronizer) {
2662 GValue item = { 0, };
2665 playsink->video_sinkpad_stream_synchronizer =
2666 gst_element_get_request_pad (GST_ELEMENT_CAST
2667 (playsink->stream_synchronizer), "sink_%u");
2668 it = gst_pad_iterate_internal_links
2669 (playsink->video_sinkpad_stream_synchronizer);
2671 gst_iterator_next (it, &item);
2672 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
2673 g_value_unset (&item);
2674 g_assert (playsink->video_srcpad_stream_synchronizer);
2675 gst_iterator_free (it);
2678 if (playsink->video_pad)
2679 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
2680 playsink->video_sinkpad_stream_synchronizer);
2682 if (need_deinterlace) {
2683 if (!playsink->videodeinterlacechain)
2684 playsink->videodeinterlacechain =
2685 gen_video_deinterlace_chain (playsink);
2686 if (!playsink->videodeinterlacechain)
2689 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
2691 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
2693 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2694 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
2696 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2697 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2699 if (playsink->videodeinterlacechain) {
2700 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2701 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
2706 GST_DEBUG_OBJECT (playsink, "adding video chain");
2707 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2708 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
2709 /* if we are not part of vis or subtitles, set the ghostpad target */
2710 if (!need_vis && !need_text && (!playsink->textchain
2711 || !playsink->text_pad)) {
2712 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
2713 if (need_deinterlace)
2714 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2715 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2717 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2718 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2721 GST_DEBUG_OBJECT (playsink, "no video needed");
2722 if (playsink->videochain) {
2723 GST_DEBUG_OBJECT (playsink, "removing video chain");
2724 if (playsink->vischain) {
2727 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
2729 /* also had visualisation, release the tee srcpad before we then
2730 * unlink the video from it */
2731 if (playsink->audio_tee_vissrc) {
2732 gst_element_release_request_pad (playsink->audio_tee,
2733 playsink->audio_tee_vissrc);
2734 gst_object_unref (playsink->audio_tee_vissrc);
2735 playsink->audio_tee_vissrc = NULL;
2738 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2739 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2742 if (playsink->video_sinkpad_stream_synchronizer) {
2743 gst_element_release_request_pad (GST_ELEMENT_CAST
2744 (playsink->stream_synchronizer),
2745 playsink->video_sinkpad_stream_synchronizer);
2746 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
2747 playsink->video_sinkpad_stream_synchronizer = NULL;
2748 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
2749 playsink->video_srcpad_stream_synchronizer = NULL;
2752 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2753 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
2754 if (playsink->videochain->ts_offset)
2755 gst_object_unref (playsink->videochain->ts_offset);
2756 playsink->videochain->ts_offset = NULL;
2759 if (playsink->videodeinterlacechain) {
2760 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2761 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
2764 if (playsink->video_pad)
2765 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
2771 GST_DEBUG_OBJECT (playsink, "adding audio");
2773 /* get a raw sink if we are asked for a raw pad */
2774 raw = playsink->audio_pad_raw;
2776 if (playsink->audiochain) {
2777 /* try to reactivate the chain */
2778 if (!setup_audio_chain (playsink, raw)) {
2779 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
2780 if (playsink->audio_tee_asrc) {
2781 gst_element_release_request_pad (playsink->audio_tee,
2782 playsink->audio_tee_asrc);
2783 gst_object_unref (playsink->audio_tee_asrc);
2784 playsink->audio_tee_asrc = NULL;
2787 if (playsink->audio_sinkpad_stream_synchronizer) {
2788 gst_element_release_request_pad (GST_ELEMENT_CAST
2789 (playsink->stream_synchronizer),
2790 playsink->audio_sinkpad_stream_synchronizer);
2791 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2792 playsink->audio_sinkpad_stream_synchronizer = NULL;
2793 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2794 playsink->audio_srcpad_stream_synchronizer = NULL;
2797 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2799 /* Remove the sink from the bin to keep its state
2800 * and unparent it to allow reuse */
2801 if (playsink->audiochain->sink)
2802 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
2803 playsink->audiochain->sink);
2805 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2806 disconnect_chain (playsink->audiochain, playsink);
2807 playsink->audiochain->volume = NULL;
2808 playsink->audiochain->mute = NULL;
2809 if (playsink->audiochain->ts_offset)
2810 gst_object_unref (playsink->audiochain->ts_offset);
2811 playsink->audiochain->ts_offset = NULL;
2812 free_chain ((GstPlayChain *) playsink->audiochain);
2813 playsink->audiochain = NULL;
2814 playsink->volume_changed = playsink->mute_changed = FALSE;
2818 if (!playsink->audiochain) {
2819 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
2820 playsink->audiochain = gen_audio_chain (playsink, raw);
2823 if (!playsink->audio_sinkpad_stream_synchronizer) {
2824 GValue item = { 0, };
2827 playsink->audio_sinkpad_stream_synchronizer =
2828 gst_element_get_request_pad (GST_ELEMENT_CAST
2829 (playsink->stream_synchronizer), "sink_%u");
2830 it = gst_pad_iterate_internal_links
2831 (playsink->audio_sinkpad_stream_synchronizer);
2833 gst_iterator_next (it, &item);
2834 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
2835 g_value_unset (&item);
2836 g_assert (playsink->audio_srcpad_stream_synchronizer);
2837 gst_iterator_free (it);
2840 if (playsink->audiochain) {
2841 GST_DEBUG_OBJECT (playsink, "adding audio chain");
2842 if (playsink->audio_tee_asrc == NULL) {
2843 playsink->audio_tee_asrc =
2844 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2846 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2847 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
2848 gst_pad_link_full (playsink->audio_tee_asrc,
2849 playsink->audio_sinkpad_stream_synchronizer,
2850 GST_PAD_LINK_CHECK_NOTHING);
2851 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
2852 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2855 GST_DEBUG_OBJECT (playsink, "no audio needed");
2856 /* we have no audio or we are requested to not play audio */
2857 if (playsink->audiochain) {
2858 GST_DEBUG_OBJECT (playsink, "removing audio chain");
2859 /* release the audio pad */
2860 if (playsink->audio_tee_asrc) {
2861 gst_element_release_request_pad (playsink->audio_tee,
2862 playsink->audio_tee_asrc);
2863 gst_object_unref (playsink->audio_tee_asrc);
2864 playsink->audio_tee_asrc = NULL;
2867 if (playsink->audio_sinkpad_stream_synchronizer) {
2868 gst_element_release_request_pad (GST_ELEMENT_CAST
2869 (playsink->stream_synchronizer),
2870 playsink->audio_sinkpad_stream_synchronizer);
2871 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
2872 playsink->audio_sinkpad_stream_synchronizer = NULL;
2873 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
2874 playsink->audio_srcpad_stream_synchronizer = NULL;
2877 if (playsink->audiochain->sink_volume) {
2878 disconnect_chain (playsink->audiochain, playsink);
2879 playsink->audiochain->volume = NULL;
2880 playsink->audiochain->mute = NULL;
2881 if (playsink->audiochain->ts_offset)
2882 gst_object_unref (playsink->audiochain->ts_offset);
2883 playsink->audiochain->ts_offset = NULL;
2885 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2886 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
2893 if (!playsink->vischain)
2894 playsink->vischain = gen_vis_chain (playsink);
2896 GST_DEBUG_OBJECT (playsink, "adding visualisation");
2898 if (playsink->vischain) {
2899 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
2901 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2902 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2903 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
2904 if (playsink->audio_tee_vissrc == NULL) {
2905 playsink->audio_tee_vissrc =
2906 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
2908 gst_pad_link_full (playsink->audio_tee_vissrc,
2909 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2910 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
2911 GST_PAD_LINK_CHECK_NOTHING);
2912 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2913 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2914 gst_object_unref (srcpad);
2917 GST_DEBUG_OBJECT (playsink, "no vis needed");
2918 if (playsink->vischain) {
2919 if (playsink->audio_tee_vissrc) {
2920 gst_element_release_request_pad (playsink->audio_tee,
2921 playsink->audio_tee_vissrc);
2922 gst_object_unref (playsink->audio_tee_vissrc);
2923 playsink->audio_tee_vissrc = NULL;
2925 GST_DEBUG_OBJECT (playsink, "removing vis chain");
2926 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2927 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
2932 GST_DEBUG_OBJECT (playsink, "adding text");
2933 if (!playsink->textchain) {
2934 GST_DEBUG_OBJECT (playsink, "creating text chain");
2935 playsink->textchain = gen_text_chain (playsink);
2937 if (playsink->textchain) {
2940 GST_DEBUG_OBJECT (playsink, "adding text chain");
2941 if (playsink->textchain->overlay)
2942 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
2944 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2946 if (!playsink->text_sinkpad_stream_synchronizer) {
2947 GValue item = { 0, };
2949 playsink->text_sinkpad_stream_synchronizer =
2950 gst_element_get_request_pad (GST_ELEMENT_CAST
2951 (playsink->stream_synchronizer), "sink_%u");
2952 it = gst_pad_iterate_internal_links
2953 (playsink->text_sinkpad_stream_synchronizer);
2955 gst_iterator_next (it, &item);
2956 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
2957 g_value_unset (&item);
2958 g_assert (playsink->text_srcpad_stream_synchronizer);
2959 gst_iterator_free (it);
2961 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
2962 playsink->text_sinkpad_stream_synchronizer);
2963 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
2964 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
2967 if (need_vis || need_video) {
2972 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
2973 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
2974 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
2975 GST_PAD_LINK_CHECK_NOTHING);
2976 gst_object_unref (srcpad);
2978 if (need_deinterlace)
2979 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
2980 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2982 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
2983 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
2985 gst_pad_link_full (playsink->textchain->srcpad,
2986 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
2989 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
2992 GST_DEBUG_OBJECT (playsink, "no text needed");
2993 /* we have no subtitles/text or we are requested to not show them */
2995 if (playsink->text_sinkpad_stream_synchronizer) {
2996 gst_element_release_request_pad (GST_ELEMENT_CAST
2997 (playsink->stream_synchronizer),
2998 playsink->text_sinkpad_stream_synchronizer);
2999 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3000 playsink->text_sinkpad_stream_synchronizer = NULL;
3001 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3002 playsink->text_srcpad_stream_synchronizer = NULL;
3005 if (playsink->textchain) {
3006 if (playsink->text_pad == NULL) {
3007 /* no text pad, remove the chain entirely */
3008 GST_DEBUG_OBJECT (playsink, "removing text chain");
3009 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3010 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3012 /* we have a chain and a textpad, turn the subtitles off */
3013 GST_DEBUG_OBJECT (playsink, "turning off the text");
3014 if (playsink->textchain->overlay)
3015 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3019 if (!need_video && playsink->video_pad) {
3020 if (playsink->video_sinkpad_stream_synchronizer) {
3021 gst_element_release_request_pad (GST_ELEMENT_CAST
3022 (playsink->stream_synchronizer),
3023 playsink->video_sinkpad_stream_synchronizer);
3024 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3025 playsink->video_sinkpad_stream_synchronizer = NULL;
3026 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3027 playsink->video_srcpad_stream_synchronizer = NULL;
3030 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3033 if (playsink->text_pad && !playsink->textchain)
3034 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3036 update_av_offset (playsink);
3037 do_async_done (playsink);
3038 GST_PLAY_SINK_UNLOCK (playsink);
3045 /* gen_ chain already posted error */
3046 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3047 GST_PLAY_SINK_UNLOCK (playsink);
3053 * gst_play_sink_set_flags:
3054 * @playsink: a #GstPlaySink
3055 * @flags: #GstPlayFlags
3057 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3058 * when constructing the sink pipelins.
3060 * Returns: TRUE if the flags could be configured.
3063 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3065 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3067 GST_OBJECT_LOCK (playsink);
3068 playsink->flags = flags;
3069 GST_OBJECT_UNLOCK (playsink);
3075 * gst_play_sink_get_flags:
3076 * @playsink: a #GstPlaySink
3078 * Get the flags of @playsink. That flags control the behaviour of the sink when
3079 * it constructs the sink pipelines.
3081 * Returns: the currently configured #GstPlayFlags.
3084 gst_play_sink_get_flags (GstPlaySink * playsink)
3088 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3090 GST_OBJECT_LOCK (playsink);
3091 res = playsink->flags;
3092 GST_OBJECT_UNLOCK (playsink);
3098 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3100 GstPlayTextChain *chain;
3102 GST_PLAY_SINK_LOCK (playsink);
3103 chain = (GstPlayTextChain *) playsink->textchain;
3104 g_free (playsink->font_desc);
3105 playsink->font_desc = g_strdup (desc);
3106 if (chain && chain->overlay) {
3107 g_object_set (chain->overlay, "font-desc", desc, NULL);
3109 GST_PLAY_SINK_UNLOCK (playsink);
3113 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3115 gchar *result = NULL;
3116 GstPlayTextChain *chain;
3118 GST_PLAY_SINK_LOCK (playsink);
3119 chain = (GstPlayTextChain *) playsink->textchain;
3120 if (chain && chain->overlay) {
3121 g_object_get (chain->overlay, "font-desc", &result, NULL);
3122 playsink->font_desc = g_strdup (result);
3124 result = g_strdup (playsink->font_desc);
3126 GST_PLAY_SINK_UNLOCK (playsink);
3132 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3133 const gchar * encoding)
3135 GstPlayTextChain *chain;
3137 GST_PLAY_SINK_LOCK (playsink);
3138 chain = (GstPlayTextChain *) playsink->textchain;
3139 g_free (playsink->subtitle_encoding);
3140 playsink->subtitle_encoding = g_strdup (encoding);
3141 if (chain && chain->overlay) {
3142 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3144 GST_PLAY_SINK_UNLOCK (playsink);
3148 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3150 gchar *result = NULL;
3151 GstPlayTextChain *chain;
3153 GST_PLAY_SINK_LOCK (playsink);
3154 chain = (GstPlayTextChain *) playsink->textchain;
3155 if (chain && chain->overlay) {
3156 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3157 playsink->subtitle_encoding = g_strdup (result);
3159 result = g_strdup (playsink->subtitle_encoding);
3161 GST_PLAY_SINK_UNLOCK (playsink);
3167 update_av_offset (GstPlaySink * playsink)
3170 GstPlayAudioChain *achain;
3171 GstPlayVideoChain *vchain;
3173 av_offset = playsink->av_offset;
3174 achain = (GstPlayAudioChain *) playsink->audiochain;
3175 vchain = (GstPlayVideoChain *) playsink->videochain;
3177 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3178 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3179 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3181 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3186 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3188 GST_PLAY_SINK_LOCK (playsink);
3189 playsink->av_offset = av_offset;
3190 update_av_offset (playsink);
3191 GST_PLAY_SINK_UNLOCK (playsink);
3195 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3199 GST_PLAY_SINK_LOCK (playsink);
3200 result = playsink->av_offset;
3201 GST_PLAY_SINK_UNLOCK (playsink);
3207 * gst_play_sink_get_last_sample:
3208 * @playsink: a #GstPlaySink
3210 * Get the last displayed sample from @playsink. This sample is in the native
3211 * format of the sink element, the caps in the result sample contain the format
3212 * of the frame data.
3214 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3218 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3220 GstSample *result = NULL;
3221 GstPlayVideoChain *chain;
3223 GST_PLAY_SINK_LOCK (playsink);
3224 GST_DEBUG_OBJECT (playsink, "taking last sample");
3225 /* get the video chain if we can */
3226 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3227 GST_DEBUG_OBJECT (playsink, "found video chain");
3228 /* see if the chain is active */
3229 if (chain->chain.activated && chain->sink) {
3232 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3234 /* find and get the last-buffer property now */
3236 gst_play_sink_find_property (playsink, chain->sink,
3237 "last-sample", GST_TYPE_SAMPLE))) {
3238 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3239 g_object_get (elem, "last-sample", &result, NULL);
3240 gst_object_unref (elem);
3244 GST_PLAY_SINK_UNLOCK (playsink);
3250 * gst_play_sink_convert_sample:
3251 * @playsink: a #GstPlaySink
3254 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3255 * be in the native format of the sink element and the caps on the buffer
3256 * describe the format of the frame. If @caps is not %NULL, the video
3257 * frame will be converted to the format of the caps.
3259 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3260 * available or when the conversion failed.
3263 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3268 result = gst_play_sink_get_last_sample (playsink);
3269 if (result != NULL && caps != NULL) {
3272 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3273 if (temp == NULL && err)
3276 gst_sample_unref (result);
3284 /* I'm really uncertain whether we should make playsink post an error
3285 * on the bus or not. It's not like it's a critical issue regarding
3286 * playsink behaviour. */
3287 GST_ERROR ("Error converting frame: %s", err->message);
3288 gst_sample_unref (result);
3295 is_raw_structure (GstStructure * s)
3299 name = gst_structure_get_name (s);
3301 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3307 is_raw_pad (GstPad * pad)
3309 GstPad *peer = gst_pad_get_peer (pad);
3311 gboolean raw = TRUE;
3316 caps = gst_pad_get_current_caps (peer);
3320 caps = gst_pad_query_caps (peer, NULL);
3322 n = gst_caps_get_size (caps);
3323 for (i = 0; i < n; i++) {
3324 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3328 } else if (raw != r) {
3329 GST_ERROR_OBJECT (pad,
3330 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3336 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3338 gst_caps_unref (caps);
3339 gst_object_unref (peer);
3344 static GstPadProbeReturn
3345 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3346 gpointer user_data);
3349 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3351 if (playsink->video_pad) {
3353 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3354 (playsink->video_pad)));
3355 if (blocked && playsink->video_block_id == 0) {
3356 playsink->video_block_id =
3357 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3358 sinkpad_blocked_cb, playsink, NULL);
3359 } else if (!blocked && playsink->video_block_id) {
3360 gst_pad_remove_probe (opad, playsink->video_block_id);
3361 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3362 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3363 playsink->video_block_id = 0;
3364 playsink->video_pad_blocked = FALSE;
3366 gst_object_unref (opad);
3371 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3373 if (playsink->audio_pad) {
3375 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3376 (playsink->audio_pad)));
3377 if (blocked && playsink->audio_block_id == 0) {
3378 playsink->audio_block_id =
3379 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3380 sinkpad_blocked_cb, playsink, NULL);
3381 } else if (!blocked && playsink->audio_block_id) {
3382 gst_pad_remove_probe (opad, playsink->audio_block_id);
3383 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3384 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3385 playsink->audio_block_id = 0;
3386 playsink->audio_pad_blocked = FALSE;
3388 gst_object_unref (opad);
3393 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3395 if (playsink->text_pad) {
3397 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3398 (playsink->text_pad)));
3399 if (blocked && playsink->text_block_id == 0) {
3400 playsink->text_block_id =
3401 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3402 sinkpad_blocked_cb, playsink, NULL);
3403 } else if (!blocked && playsink->text_block_id) {
3404 gst_pad_remove_probe (opad, playsink->text_block_id);
3405 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3406 playsink->text_block_id = 0;
3407 playsink->text_pad_blocked = FALSE;
3409 gst_object_unref (opad);
3413 static GstPadProbeReturn
3414 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3417 GstPlaySink *playsink = (GstPlaySink *) user_data;
3420 GST_PLAY_SINK_LOCK (playsink);
3422 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3423 if (pad == playsink->video_pad) {
3424 playsink->video_pad_blocked = TRUE;
3425 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3426 } else if (pad == playsink->audio_pad) {
3427 playsink->audio_pad_blocked = TRUE;
3428 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3429 } else if (pad == playsink->text_pad) {
3430 playsink->text_pad_blocked = TRUE;
3431 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3434 /* We reconfigure when for ALL streams:
3435 * * there isn't a pad
3436 * * OR the pad is blocked
3437 * * OR there are no pending blocks on that pad
3440 if ((!playsink->video_pad || playsink->video_pad_blocked
3441 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3442 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3443 && (!playsink->text_pad || playsink->text_pad_blocked
3444 || !PENDING_TEXT_BLOCK (playsink))) {
3445 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3447 if (playsink->video_pad) {
3448 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3449 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3450 playsink->video_pad_raw);
3453 if (playsink->audio_pad) {
3454 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3455 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3456 playsink->audio_pad_raw);
3459 gst_play_sink_reconfigure (playsink);
3461 video_set_blocked (playsink, FALSE);
3462 audio_set_blocked (playsink, FALSE);
3463 text_set_blocked (playsink, FALSE);
3466 gst_object_unref (pad);
3468 GST_PLAY_SINK_UNLOCK (playsink);
3470 return GST_PAD_PROBE_OK;
3474 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3476 gboolean reconfigure = FALSE;
3480 g_object_get (pad, "caps", &caps, NULL);
3484 if (pad == playsink->audio_pad) {
3485 raw = is_raw_pad (pad);
3486 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3487 && playsink->audiochain;
3488 GST_DEBUG_OBJECT (pad,
3489 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3491 } else if (pad == playsink->video_pad) {
3492 raw = is_raw_pad (pad);
3493 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3494 && playsink->videochain;
3495 GST_DEBUG_OBJECT (pad,
3496 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3500 gst_caps_unref (caps);
3503 GST_PLAY_SINK_LOCK (playsink);
3504 video_set_blocked (playsink, TRUE);
3505 audio_set_blocked (playsink, TRUE);
3506 text_set_blocked (playsink, TRUE);
3507 GST_PLAY_SINK_UNLOCK (playsink);
3512 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3513 GstPlaySinkType type)
3515 gulong *block_id = NULL;
3517 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3519 GST_PLAY_SINK_LOCK (playsink);
3520 if (pad == playsink->video_pad) {
3521 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3522 type != GST_PLAY_SINK_TYPE_VIDEO)
3524 block_id = &playsink->video_block_id;
3525 } else if (pad == playsink->audio_pad) {
3526 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3527 type != GST_PLAY_SINK_TYPE_AUDIO)
3529 block_id = &playsink->audio_block_id;
3530 } else if (pad == playsink->text_pad) {
3531 if (type != GST_PLAY_SINK_TYPE_TEXT)
3533 block_id = &playsink->text_block_id;
3536 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3538 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3541 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3542 sinkpad_blocked_cb, playsink, NULL);
3543 PENDING_FLAG_SET (playsink, type);
3544 gst_object_unref (blockpad);
3546 GST_PLAY_SINK_UNLOCK (playsink);
3553 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3555 GST_PLAY_SINK_UNLOCK (playsink);
3561 * gst_play_sink_request_pad
3562 * @playsink: a #GstPlaySink
3563 * @type: a #GstPlaySinkType
3565 * Create or return a pad of @type.
3567 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3570 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3573 gboolean created = FALSE;
3574 gboolean activate = TRUE;
3575 const gchar *pad_name = NULL;
3576 gulong *block_id = NULL;
3578 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3580 GST_PLAY_SINK_LOCK (playsink);
3582 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3583 case GST_PLAY_SINK_TYPE_AUDIO:
3584 pad_name = "audio_sink";
3585 if (!playsink->audio_tee) {
3586 GST_LOG_OBJECT (playsink, "creating tee");
3587 /* create tee when needed. This element will feed the audio sink chain
3588 * and the vis chain. */
3589 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3590 if (playsink->audio_tee == NULL) {
3591 post_missing_element_message (playsink, "tee");
3592 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3593 (_("Missing element '%s' - check your GStreamer installation."),
3598 playsink->audio_tee_sink =
3599 gst_element_get_static_pad (playsink->audio_tee, "sink");
3600 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3601 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3604 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3606 if (!playsink->audio_pad) {
3607 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3608 playsink->audio_pad =
3609 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3610 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3611 G_CALLBACK (caps_notify_cb), playsink);
3614 playsink->audio_pad_raw = FALSE;
3615 res = playsink->audio_pad;
3616 block_id = &playsink->audio_block_id;
3618 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3619 case GST_PLAY_SINK_TYPE_VIDEO:
3620 pad_name = "video_sink";
3621 if (!playsink->video_pad) {
3622 GST_LOG_OBJECT (playsink, "ghosting videosink");
3623 playsink->video_pad =
3624 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3625 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
3626 G_CALLBACK (caps_notify_cb), playsink);
3629 playsink->video_pad_raw = FALSE;
3630 res = playsink->video_pad;
3631 block_id = &playsink->video_block_id;
3633 case GST_PLAY_SINK_TYPE_TEXT:
3634 GST_LOG_OBJECT (playsink, "ghosting text");
3635 if (!playsink->text_pad) {
3636 playsink->text_pad =
3637 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
3640 res = playsink->text_pad;
3641 block_id = &playsink->text_block_id;
3643 case GST_PLAY_SINK_TYPE_FLUSHING:
3647 /* we need a unique padname for the flushing pad. */
3648 padname = g_strdup_printf ("flushing_%u", playsink->count);
3649 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
3660 GST_PLAY_SINK_UNLOCK (playsink);
3662 if (created && res) {
3663 /* we have to add the pad when it's active or we get an error when the
3664 * element is 'running' */
3665 gst_pad_set_active (res, TRUE);
3666 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
3667 if (block_id && *block_id == 0) {
3669 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
3672 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3673 sinkpad_blocked_cb, playsink, NULL);
3674 PENDING_FLAG_SET (playsink, type);
3675 gst_object_unref (blockpad);
3678 gst_pad_set_active (res, activate);
3686 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
3687 const gchar * name, const GstCaps * caps)
3691 GstPlaySinkType type;
3692 const gchar *tplname;
3694 g_return_val_if_fail (templ != NULL, NULL);
3696 GST_DEBUG_OBJECT (element, "name:%s", name);
3698 psink = GST_PLAY_SINK (element);
3699 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
3701 /* Figure out the GstPlaySinkType based on the template */
3702 if (!strcmp (tplname, "audio_sink"))
3703 type = GST_PLAY_SINK_TYPE_AUDIO;
3704 else if (!strcmp (tplname, "audio_raw_sink"))
3705 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
3706 else if (!strcmp (tplname, "video_sink"))
3707 type = GST_PLAY_SINK_TYPE_VIDEO;
3708 else if (!strcmp (tplname, "video_raw_sink"))
3709 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
3710 else if (!strcmp (tplname, "text_sink"))
3711 type = GST_PLAY_SINK_TYPE_TEXT;
3713 goto unknown_template;
3715 pad = gst_play_sink_request_pad (psink, type);
3719 GST_WARNING_OBJECT (element, "Unknown pad template");
3724 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
3726 GstPad **res = NULL;
3727 gboolean untarget = TRUE;
3729 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
3731 GST_PLAY_SINK_LOCK (playsink);
3732 if (pad == playsink->video_pad) {
3733 res = &playsink->video_pad;
3734 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
3736 } else if (pad == playsink->audio_pad) {
3737 res = &playsink->audio_pad;
3738 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
3740 } else if (pad == playsink->text_pad) {
3741 res = &playsink->text_pad;
3743 /* try to release the given pad anyway, these could be the FLUSHING pads. */
3747 GST_PLAY_SINK_UNLOCK (playsink);
3750 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
3751 gst_pad_set_active (*res, FALSE);
3753 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
3754 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
3756 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
3757 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
3763 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
3765 GstPlaySink *psink = GST_PLAY_SINK (element);
3767 gst_play_sink_release_pad (psink, pad);
3771 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
3773 GstPlaySink *playsink;
3775 playsink = GST_PLAY_SINK_CAST (bin);
3777 switch (GST_MESSAGE_TYPE (message)) {
3778 case GST_MESSAGE_STEP_DONE:
3783 gboolean flush, intermediate, eos;
3786 GST_INFO_OBJECT (playsink, "Handling step-done message");
3787 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
3788 &intermediate, &duration, &eos);
3790 if (format == GST_FORMAT_BUFFERS) {
3791 /* for the buffer format, we align the other streams */
3792 if (playsink->audiochain) {
3796 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
3799 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
3800 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3804 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3807 case GST_MESSAGE_ELEMENT:{
3808 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
3809 GstVideoOverlay *overlay;
3811 GST_OBJECT_LOCK (playsink);
3812 if (playsink->overlay_element
3813 && GST_OBJECT_CAST (playsink->overlay_element) !=
3814 GST_MESSAGE_SRC (message)) {
3815 gst_object_unref (playsink->overlay_element);
3816 playsink->overlay_element = NULL;
3819 if (!playsink->overlay_element)
3820 playsink->overlay_element =
3821 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
3823 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
3824 GST_OBJECT_UNLOCK (playsink);
3826 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
3828 if (playsink->overlay_handle_set)
3829 gst_video_overlay_set_window_handle (playsink->overlay_element,
3830 playsink->overlay_handle);
3831 if (playsink->overlay_handle_events_set)
3832 gst_video_overlay_handle_events (playsink->overlay_element,
3833 playsink->overlay_handle_events);
3834 if (playsink->overlay_render_rectangle_set)
3835 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
3836 playsink->overlay_x, playsink->overlay_y,
3837 playsink->overlay_width, playsink->overlay_height);
3839 gst_object_unref (overlay);
3840 gst_message_unref (message);
3841 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
3843 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
3849 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
3854 /* Send an event to our sinks until one of them works; don't then send to the
3855 * remaining sinks (unlike GstBin)
3856 * Special case: If a text sink is set we need to send the event
3857 * to them in case it's source is different from the a/v stream's source.
3860 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
3862 gboolean res = TRUE;
3863 if (playsink->send_event_mode == MODE_FIRST) {
3864 if (playsink->textchain && playsink->textchain->sink) {
3865 gst_event_ref (event);
3867 gst_element_send_event (playsink->textchain->chain.bin, event))) {
3868 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
3870 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
3874 if (playsink->videochain) {
3875 gst_event_ref (event);
3877 gst_element_send_event (playsink->videochain->chain.bin,
3879 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
3882 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
3884 if (playsink->audiochain) {
3885 gst_event_ref (event);
3887 gst_element_send_event (playsink->audiochain->chain.bin,
3889 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
3892 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
3896 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
3897 (GST_ELEMENT_CAST (playsink), event);
3901 gst_event_unref (event);
3905 /* We only want to send the event to a single sink (overriding GstBin's
3906 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
3907 * events appropriately. So, this is a messy duplication of code. */
3909 gst_play_sink_send_event (GstElement * element, GstEvent * event)
3911 gboolean res = FALSE;
3912 GstEventType event_type = GST_EVENT_TYPE (event);
3913 GstPlaySink *playsink;
3914 playsink = GST_PLAY_SINK_CAST (element);
3915 switch (event_type) {
3916 case GST_EVENT_SEEK:
3917 GST_DEBUG_OBJECT (element, "Sending event to a sink");
3918 res = gst_play_sink_send_event_to_sink (playsink, event);
3920 case GST_EVENT_STEP:
3925 gboolean flush, intermediate;
3926 gst_event_parse_step (event, &format, &amount, &rate, &flush,
3928 if (format == GST_FORMAT_BUFFERS) {
3929 /* for buffers, we will try to step video frames, for other formats we
3930 * send the step to all sinks */
3931 res = gst_play_sink_send_event_to_sink (playsink, event);
3934 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3941 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
3948 static GstStateChangeReturn
3949 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
3951 GstStateChangeReturn ret;
3952 GstStateChangeReturn bret;
3953 GstPlaySink *playsink;
3954 playsink = GST_PLAY_SINK (element);
3955 switch (transition) {
3956 case GST_STATE_CHANGE_READY_TO_PAUSED:
3957 playsink->need_async_start = TRUE;
3958 /* we want to go async to PAUSED until we managed to configure and add the
3960 do_async_start (playsink);
3961 ret = GST_STATE_CHANGE_ASYNC;
3963 /* block all pads here */
3964 GST_PLAY_SINK_LOCK (playsink);
3965 if (playsink->video_pad && playsink->video_block_id == 0) {
3967 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3968 (playsink->video_pad)));
3969 playsink->video_block_id =
3970 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3971 sinkpad_blocked_cb, playsink, NULL);
3972 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3973 gst_object_unref (opad);
3976 if (playsink->audio_pad && playsink->audio_block_id == 0) {
3978 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3979 (playsink->audio_pad)));
3980 playsink->audio_block_id =
3981 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3982 sinkpad_blocked_cb, playsink, NULL);
3983 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3984 gst_object_unref (opad);
3987 if (playsink->text_pad && playsink->text_block_id == 0) {
3989 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3990 (playsink->text_pad)));
3991 playsink->text_block_id =
3992 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3993 sinkpad_blocked_cb, playsink, NULL);
3994 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3995 gst_object_unref (opad);
3997 GST_PLAY_SINK_UNLOCK (playsink);
3999 case GST_STATE_CHANGE_PAUSED_TO_READY:
4000 /* unblock all pads here */
4001 GST_PLAY_SINK_LOCK (playsink);
4002 video_set_blocked (playsink, FALSE);
4003 audio_set_blocked (playsink, FALSE);
4004 text_set_blocked (playsink, FALSE);
4005 GST_PLAY_SINK_UNLOCK (playsink);
4007 case GST_STATE_CHANGE_READY_TO_NULL:
4008 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4009 /* remove our links to the mute and volume elements when they were
4010 * provided by a sink */
4011 disconnect_chain (playsink->audiochain, playsink);
4012 playsink->audiochain->volume = NULL;
4013 playsink->audiochain->mute = NULL;
4016 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4017 gst_object_unref (playsink->audiochain->ts_offset);
4018 playsink->audiochain->ts_offset = NULL;
4021 if (playsink->videochain && playsink->videochain->ts_offset) {
4022 gst_object_unref (playsink->videochain->ts_offset);
4023 playsink->videochain->ts_offset = NULL;
4026 GST_OBJECT_LOCK (playsink);
4027 if (playsink->overlay_element)
4028 gst_object_unref (playsink->overlay_element);
4029 playsink->overlay_element = NULL;
4031 if (playsink->colorbalance_element) {
4032 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4033 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4034 gst_object_unref (playsink->colorbalance_element);
4036 playsink->colorbalance_element = NULL;
4037 GST_OBJECT_UNLOCK (playsink);
4039 ret = GST_STATE_CHANGE_SUCCESS;
4042 /* all other state changes return SUCCESS by default, this value can be
4043 * overridden by the result of the children */
4044 ret = GST_STATE_CHANGE_SUCCESS;
4048 /* do the state change of the children */
4050 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4052 /* now look at the result of our children and adjust the return value */
4054 case GST_STATE_CHANGE_FAILURE:
4055 /* failure, we stop */
4056 goto activate_failed;
4057 case GST_STATE_CHANGE_NO_PREROLL:
4058 /* some child returned NO_PREROLL. This is strange but we never know. We
4059 * commit our async state change (if any) and return the NO_PREROLL */
4060 do_async_done (playsink);
4063 case GST_STATE_CHANGE_ASYNC:
4064 /* some child was async, return this */
4068 /* return our previously configured return value */
4072 switch (transition) {
4073 case GST_STATE_CHANGE_READY_TO_PAUSED:
4075 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4076 /* FIXME Release audio device when we implement that */
4077 playsink->need_async_start = TRUE;
4079 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4080 if (playsink->video_sinkpad_stream_synchronizer) {
4081 gst_element_release_request_pad (GST_ELEMENT_CAST
4082 (playsink->stream_synchronizer),
4083 playsink->video_sinkpad_stream_synchronizer);
4084 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4085 playsink->video_sinkpad_stream_synchronizer = NULL;
4086 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4087 playsink->video_srcpad_stream_synchronizer = NULL;
4089 if (playsink->audio_sinkpad_stream_synchronizer) {
4090 gst_element_release_request_pad (GST_ELEMENT_CAST
4091 (playsink->stream_synchronizer),
4092 playsink->audio_sinkpad_stream_synchronizer);
4093 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4094 playsink->audio_sinkpad_stream_synchronizer = NULL;
4095 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4096 playsink->audio_srcpad_stream_synchronizer = NULL;
4098 if (playsink->text_sinkpad_stream_synchronizer) {
4099 gst_element_release_request_pad (GST_ELEMENT_CAST
4100 (playsink->stream_synchronizer),
4101 playsink->text_sinkpad_stream_synchronizer);
4102 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4103 playsink->text_sinkpad_stream_synchronizer = NULL;
4104 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4105 playsink->text_srcpad_stream_synchronizer = NULL;
4109 case GST_STATE_CHANGE_READY_TO_NULL:
4110 /* remove sinks we added */
4111 if (playsink->videodeinterlacechain) {
4112 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4114 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4116 if (playsink->videochain) {
4117 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4118 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4120 if (playsink->audiochain) {
4121 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4122 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4124 if (playsink->vischain) {
4125 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4126 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4128 if (playsink->textchain) {
4129 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4130 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4132 do_async_done (playsink);
4133 /* when going to READY, keep elements around as long as possible,
4134 * so they may be re-used faster next time/url around.
4135 * when really going to NULL, clean up everything completely. */
4136 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4138 /* Unparent the sinks to allow reuse */
4139 if (playsink->videochain && playsink->videochain->sink)
4140 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4141 playsink->videochain->sink);
4142 if (playsink->audiochain && playsink->audiochain->sink)
4143 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4144 playsink->audiochain->sink);
4145 if (playsink->textchain && playsink->textchain->sink)
4146 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4147 playsink->textchain->sink);
4148 if (playsink->audio_sink != NULL)
4149 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4150 if (playsink->video_sink != NULL)
4151 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4152 if (playsink->visualisation != NULL)
4153 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4154 if (playsink->text_sink != NULL)
4155 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4156 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4157 playsink->videodeinterlacechain = NULL;
4158 free_chain ((GstPlayChain *) playsink->videochain);
4159 playsink->videochain = NULL;
4160 free_chain ((GstPlayChain *) playsink->audiochain);
4161 playsink->audiochain = NULL;
4162 free_chain ((GstPlayChain *) playsink->vischain);
4163 playsink->vischain = NULL;
4164 free_chain ((GstPlayChain *) playsink->textchain);
4165 playsink->textchain = NULL;
4175 GST_DEBUG_OBJECT (element,
4176 "element failed to change states -- activation problem?");
4177 return GST_STATE_CHANGE_FAILURE;
4182 gst_play_sink_set_property (GObject * object, guint prop_id,
4183 const GValue * value, GParamSpec * spec)
4185 GstPlaySink *playsink = GST_PLAY_SINK (object);
4188 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4191 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4194 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4196 case PROP_FONT_DESC:
4197 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4199 case PROP_SUBTITLE_ENCODING:
4200 gst_play_sink_set_subtitle_encoding (playsink,
4201 g_value_get_string (value));
4203 case PROP_VIS_PLUGIN:
4204 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4206 case PROP_AV_OFFSET:
4207 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4209 case PROP_VIDEO_SINK:
4210 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4211 g_value_get_object (value));
4213 case PROP_AUDIO_SINK:
4214 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4215 g_value_get_object (value));
4217 case PROP_TEXT_SINK:
4218 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4219 g_value_get_object (value));
4221 case PROP_SEND_EVENT_MODE:
4222 playsink->send_event_mode = g_value_get_enum (value);
4225 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4231 gst_play_sink_get_property (GObject * object, guint prop_id,
4232 GValue * value, GParamSpec * spec)
4234 GstPlaySink *playsink = GST_PLAY_SINK (object);
4237 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4240 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4243 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4245 case PROP_FONT_DESC:
4246 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4248 case PROP_SUBTITLE_ENCODING:
4249 g_value_take_string (value,
4250 gst_play_sink_get_subtitle_encoding (playsink));
4252 case PROP_VIS_PLUGIN:
4253 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4256 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4258 case PROP_AV_OFFSET:
4259 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4261 case PROP_VIDEO_SINK:
4262 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4263 GST_PLAY_SINK_TYPE_VIDEO));
4265 case PROP_AUDIO_SINK:
4266 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4267 GST_PLAY_SINK_TYPE_AUDIO));
4269 case PROP_TEXT_SINK:
4270 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4271 GST_PLAY_SINK_TYPE_TEXT));
4273 case PROP_SEND_EVENT_MODE:
4274 g_value_set_enum (value, playsink->send_event_mode);
4277 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4283 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4285 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4286 GstVideoOverlay *overlay_element;
4288 GST_OBJECT_LOCK (playsink);
4289 if (playsink->overlay_element)
4291 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4293 overlay_element = NULL;
4294 GST_OBJECT_UNLOCK (playsink);
4296 if (overlay_element) {
4297 gst_video_overlay_expose (overlay_element);
4298 gst_object_unref (overlay_element);
4303 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4304 gboolean handle_events)
4306 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4307 GstVideoOverlay *overlay_element;
4309 GST_OBJECT_LOCK (playsink);
4310 if (playsink->overlay_element)
4312 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4314 overlay_element = NULL;
4315 GST_OBJECT_UNLOCK (playsink);
4317 playsink->overlay_handle_events_set = TRUE;
4318 playsink->overlay_handle_events = handle_events;
4320 if (overlay_element) {
4321 gst_video_overlay_handle_events (overlay_element, handle_events);
4322 gst_object_unref (overlay_element);
4327 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4328 gint y, gint width, gint height)
4330 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4331 GstVideoOverlay *overlay_element;
4333 GST_OBJECT_LOCK (playsink);
4334 if (playsink->overlay_element)
4336 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4338 overlay_element = NULL;
4339 GST_OBJECT_UNLOCK (playsink);
4341 playsink->overlay_render_rectangle_set = TRUE;
4342 playsink->overlay_x = x;
4343 playsink->overlay_y = y;
4344 playsink->overlay_width = width;
4345 playsink->overlay_height = height;
4347 if (overlay_element) {
4348 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4350 gst_object_unref (overlay_element);
4355 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4358 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4359 GstVideoOverlay *overlay_element;
4361 GST_OBJECT_LOCK (playsink);
4362 if (playsink->overlay_element)
4364 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4366 overlay_element = NULL;
4367 GST_OBJECT_UNLOCK (playsink);
4369 playsink->overlay_handle_set = TRUE;
4370 playsink->overlay_handle = handle;
4372 if (overlay_element) {
4373 gst_video_overlay_set_window_handle (overlay_element, handle);
4374 gst_object_unref (overlay_element);
4379 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4381 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4382 iface->expose = gst_play_sink_overlay_expose;
4383 iface->handle_events = gst_play_sink_overlay_handle_events;
4384 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4385 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4389 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4390 GstStructure * structure)
4392 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4395 GST_PLAY_SINK_LOCK (playsink);
4396 if (playsink->videochain && playsink->videochain->chain.bin)
4397 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4398 GST_PLAY_SINK_UNLOCK (playsink);
4401 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4404 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4406 gst_object_unref (nav);
4408 GstEvent *event = gst_event_new_navigation (structure);
4410 gst_element_send_event (GST_ELEMENT (bin), event);
4413 gst_object_unref (bin);
4417 gst_structure_free (structure);
4421 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4423 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4425 iface->send_event = gst_play_sink_navigation_send_event;
4428 static const GList *
4429 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4431 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4433 return playsink->colorbalance_channels;
4437 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4438 GstColorBalanceChannel * proxy, gint value)
4440 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4443 GstColorBalance *balance_element = NULL;
4445 GST_OBJECT_LOCK (playsink);
4446 if (playsink->colorbalance_element)
4448 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4449 GST_OBJECT_UNLOCK (playsink);
4451 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4452 GstColorBalanceChannel *proxy_tmp = l->data;
4455 if (proxy_tmp != proxy)
4458 playsink->colorbalance_values[i] = value;
4460 if (balance_element) {
4461 GstColorBalanceChannel *channel = NULL;
4462 const GList *channels, *k;
4464 channels = gst_color_balance_list_channels (balance_element);
4465 for (k = channels; k; k = k->next) {
4466 GstColorBalanceChannel *tmp = l->data;
4468 if (g_strrstr (tmp->label, proxy->label)) {
4476 /* Convert to [0, 1] range */
4479 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4480 (gdouble) proxy->min_value);
4481 /* Convert to channel range */
4483 channel->min_value + new_val * ((gdouble) channel->max_value -
4484 (gdouble) channel->min_value);
4486 gst_color_balance_set_value (balance_element, channel,
4487 (gint) (new_val + 0.5));
4489 gst_object_unref (balance_element);
4492 gst_color_balance_value_changed (balance, proxy, value);
4498 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4499 GstColorBalanceChannel * proxy)
4501 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4505 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4506 GstColorBalanceChannel *proxy_tmp = l->data;
4508 if (proxy_tmp != proxy)
4511 return playsink->colorbalance_values[i];
4514 g_return_val_if_reached (0);
4517 static GstColorBalanceType
4518 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4520 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4521 GstColorBalance *balance_element = NULL;
4522 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4524 GST_OBJECT_LOCK (playsink);
4525 if (playsink->colorbalance_element)
4527 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4528 GST_OBJECT_UNLOCK (playsink);
4530 if (balance_element) {
4531 t = gst_color_balance_get_balance_type (balance_element);
4532 gst_object_unref (balance_element);
4539 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4541 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4543 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4544 iface->set_value = gst_play_sink_colorbalance_set_value;
4545 iface->get_value = gst_play_sink_colorbalance_get_value;
4546 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4550 gst_play_sink_plugin_init (GstPlugin * plugin)
4552 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4553 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4554 GST_TYPE_PLAY_SINK);