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];
260 /* sending audio/video flushes break stream changes when the pipeline
261 * is paused and played again in 0.10 */
263 GstSegment video_segment;
264 gboolean video_custom_flush_finished;
265 gboolean video_ignore_wrong_state;
266 gboolean video_pending_flush;
268 GstSegment audio_segment;
269 gboolean audio_custom_flush_finished;
270 gboolean audio_ignore_wrong_state;
271 gboolean audio_pending_flush;
274 GstSegment text_segment;
275 gboolean text_custom_flush_finished;
276 gboolean text_ignore_wrong_state;
277 gboolean text_pending_flush;
280 struct _GstPlaySinkClass
282 GstBinClass parent_class;
284 gboolean (*reconfigure) (GstPlaySink * playsink);
286 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
290 static GstStaticPadTemplate audiotemplate =
291 GST_STATIC_PAD_TEMPLATE ("audio_sink",
294 GST_STATIC_CAPS_ANY);
295 static GstStaticPadTemplate videotemplate =
296 GST_STATIC_PAD_TEMPLATE ("video_sink",
299 GST_STATIC_CAPS_ANY);
300 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
303 GST_STATIC_CAPS_ANY);
305 /* FIXME 0.11: Remove */
306 static GstStaticPadTemplate audiorawtemplate =
307 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
310 GST_STATIC_CAPS_ANY);
311 static GstStaticPadTemplate videorawtemplate =
312 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
315 GST_STATIC_CAPS_ANY);
326 PROP_SUBTITLE_ENCODING,
333 PROP_SEND_EVENT_MODE,
343 static void gst_play_sink_dispose (GObject * object);
344 static void gst_play_sink_finalize (GObject * object);
345 static void gst_play_sink_set_property (GObject * object, guint prop_id,
346 const GValue * value, GParamSpec * spec);
347 static void gst_play_sink_get_property (GObject * object, guint prop_id,
348 GValue * value, GParamSpec * spec);
350 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
351 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
352 static void gst_play_sink_release_request_pad (GstElement * element,
354 static gboolean gst_play_sink_send_event (GstElement * element,
356 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
357 GstStateChange transition);
359 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
361 /* sending audio/video flushes break stream changes when the pipeline
362 * is paused and played again in 0.10 */
364 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
365 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
367 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
368 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
371 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
373 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
374 GstObject * parent, GstBuffer * buffer);
376 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
377 GstPlaySink * playsink);
378 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
379 GstPlaySink * playsink);
381 static void update_av_offset (GstPlaySink * playsink);
383 static GQuark _playsink_reset_segment_event_marker_id = 0;
385 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
387 static void gst_play_sink_overlay_init (gpointer g_iface,
388 gpointer g_iface_data);
389 static void gst_play_sink_navigation_init (gpointer g_iface,
390 gpointer g_iface_data);
391 static void gst_play_sink_colorbalance_init (gpointer g_iface,
392 gpointer g_iface_data);
395 _do_init (GType type)
397 static const GInterfaceInfo svol_info = {
400 static const GInterfaceInfo ov_info = {
401 gst_play_sink_overlay_init,
404 static const GInterfaceInfo nav_info = {
405 gst_play_sink_navigation_init,
408 static const GInterfaceInfo col_info = {
409 gst_play_sink_colorbalance_init,
413 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
414 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
415 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
416 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
419 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
420 _do_init (g_define_type_id));
423 gst_play_sink_class_init (GstPlaySinkClass * klass)
425 GObjectClass *gobject_klass;
426 GstElementClass *gstelement_klass;
427 GstBinClass *gstbin_klass;
429 gobject_klass = (GObjectClass *) klass;
430 gstelement_klass = (GstElementClass *) klass;
431 gstbin_klass = (GstBinClass *) klass;
433 gobject_klass->dispose = gst_play_sink_dispose;
434 gobject_klass->finalize = gst_play_sink_finalize;
435 gobject_klass->set_property = gst_play_sink_set_property;
436 gobject_klass->get_property = gst_play_sink_get_property;
442 * Control the behaviour of playsink.
444 g_object_class_install_property (gobject_klass, PROP_FLAGS,
445 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
446 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
447 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
450 * GstPlaySink:volume:
452 * Get or set the current audio stream volume. 1.0 means 100%,
453 * 0.0 means mute. This uses a linear volume scale.
456 g_object_class_install_property (gobject_klass, PROP_VOLUME,
457 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
458 0.0, VOLUME_MAX_DOUBLE, 1.0,
459 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
460 g_object_class_install_property (gobject_klass, PROP_MUTE,
461 g_param_spec_boolean ("mute", "Mute",
462 "Mute the audio channel without changing the volume", FALSE,
463 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
464 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
465 g_param_spec_string ("subtitle-font-desc",
466 "Subtitle font description",
467 "Pango font description of font "
468 "to be used for subtitle rendering", NULL,
469 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
470 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
471 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
472 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
473 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
474 "be checked for an encoding to use. If that is not set either, "
475 "ISO-8859-15 will be assumed.", NULL,
476 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
477 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
478 g_param_spec_object ("vis-plugin", "Vis plugin",
479 "the visualization element to use (NULL = default)",
480 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
482 * GstPlaySink:sample:
484 * Get the currently rendered or prerolled sample in the video sink.
485 * The #GstCaps in the sample will describe the format of the buffer.
487 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
488 g_param_spec_boxed ("sample", "Sample",
489 "The last sample (NULL = no video available)",
490 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
492 * GstPlaySink:av-offset:
494 * Control the synchronisation offset between the audio and video streams.
495 * Positive values make the audio ahead of the video and negative values make
496 * the audio go behind the video.
500 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
501 g_param_spec_int64 ("av-offset", "AV Offset",
502 "The synchronisation offset between audio and video in nanoseconds",
503 G_MININT64, G_MAXINT64, 0,
504 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
507 * GstPlaySink:video-sink:
509 * Set the used video sink element. NULL will use the default sink. playsink
510 * must be in %GST_STATE_NULL
514 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
515 g_param_spec_object ("video-sink", "Video Sink",
516 "the video output element to use (NULL = default sink)",
517 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
519 * GstPlaySink:audio-sink:
521 * Set the used audio sink element. NULL will use the default sink. playsink
522 * must be in %GST_STATE_NULL
526 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
527 g_param_spec_object ("audio-sink", "Audio Sink",
528 "the audio output element to use (NULL = default sink)",
529 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
532 * GstPlaySink:text-sink:
534 * Set the used text sink element. NULL will use the default sink. playsink
535 * must be in %GST_STATE_NULL
539 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
540 g_param_spec_object ("text-sink", "Text sink",
541 "the text output element to use (NULL = default subtitleoverlay)",
542 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
545 * GstPlaySink::send-event-mode:
547 * Sets the handling method used for events received from send_event
548 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
549 * handling (push the event to all internal sinks).
553 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
554 g_param_spec_enum ("send-event-mode", "Send event mode",
555 "How to send events received in send_event function",
556 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
557 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
559 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
560 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
561 reconfigure), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN,
564 * GstPlaySink::convert-sample
565 * @playsink: a #GstPlaySink
566 * @caps: the target format of the sample
568 * Action signal to retrieve the currently playing video sample in the format
569 * specified by @caps.
570 * If @caps is %NULL, no conversion will be performed and this function is
571 * equivalent to the #GstPlaySink::sample property.
573 * Returns: a #GstSample of the current video sample converted to #caps.
574 * The caps in the sample will describe the final layout of the buffer data.
575 * %NULL is returned when no current sample can be retrieved or when the
578 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
579 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
580 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
581 g_cclosure_marshal_generic, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
583 gst_element_class_add_pad_template (gstelement_klass,
584 gst_static_pad_template_get (&audiorawtemplate));
585 gst_element_class_add_pad_template (gstelement_klass,
586 gst_static_pad_template_get (&audiotemplate));
587 gst_element_class_add_pad_template (gstelement_klass,
588 gst_static_pad_template_get (&videorawtemplate));
589 gst_element_class_add_pad_template (gstelement_klass,
590 gst_static_pad_template_get (&videotemplate));
591 gst_element_class_add_pad_template (gstelement_klass,
592 gst_static_pad_template_get (&texttemplate));
593 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
595 "Convenience sink for multiple streams",
596 "Wim Taymans <wim.taymans@gmail.com>");
598 gstelement_klass->change_state =
599 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
600 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
601 gstelement_klass->request_new_pad =
602 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
603 gstelement_klass->release_pad =
604 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
606 gstbin_klass->handle_message =
607 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
609 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
610 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
612 _playsink_reset_segment_event_marker_id =
613 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
617 gst_play_sink_init (GstPlaySink * playsink)
619 GstColorBalanceChannel *channel;
622 playsink->video_sink = NULL;
623 playsink->audio_sink = NULL;
624 playsink->visualisation = NULL;
625 playsink->text_sink = NULL;
626 playsink->volume = 1.0;
627 playsink->font_desc = NULL;
628 playsink->subtitle_encoding = NULL;
629 playsink->flags = DEFAULT_FLAGS;
630 playsink->send_event_mode = MODE_DEFAULT;
632 playsink->stream_synchronizer =
633 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
634 gst_bin_add (GST_BIN_CAST (playsink),
635 GST_ELEMENT_CAST (playsink->stream_synchronizer));
637 g_rec_mutex_init (&playsink->lock);
638 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
641 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
643 channel->label = g_strdup ("CONTRAST");
644 channel->min_value = -1000;
645 channel->max_value = 1000;
646 playsink->colorbalance_channels =
647 g_list_append (playsink->colorbalance_channels, channel);
648 playsink->colorbalance_values[0] = 0;
651 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
653 channel->label = g_strdup ("BRIGHTNESS");
654 channel->min_value = -1000;
655 channel->max_value = 1000;
656 playsink->colorbalance_channels =
657 g_list_append (playsink->colorbalance_channels, channel);
658 playsink->colorbalance_values[1] = 0;
661 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
663 channel->label = g_strdup ("HUE");
664 channel->min_value = -1000;
665 channel->max_value = 1000;
666 playsink->colorbalance_channels =
667 g_list_append (playsink->colorbalance_channels, channel);
668 playsink->colorbalance_values[2] = 0;
671 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
673 channel->label = g_strdup ("SATURATION");
674 channel->min_value = -1000;
675 channel->max_value = 1000;
676 playsink->colorbalance_channels =
677 g_list_append (playsink->colorbalance_channels, channel);
678 playsink->colorbalance_values[3] = 0;
682 disconnect_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
686 g_signal_handlers_disconnect_by_func (chain->volume, notify_volume_cb,
689 g_signal_handlers_disconnect_by_func (chain->mute, notify_mute_cb,
695 free_chain (GstPlayChain * chain)
699 gst_object_unref (chain->bin);
705 gst_play_sink_dispose (GObject * object)
707 GstPlaySink *playsink;
709 playsink = GST_PLAY_SINK (object);
711 if (playsink->audio_sink != NULL) {
712 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
713 gst_object_unref (playsink->audio_sink);
714 playsink->audio_sink = NULL;
716 if (playsink->video_sink != NULL) {
717 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
718 gst_object_unref (playsink->video_sink);
719 playsink->video_sink = NULL;
721 if (playsink->visualisation != NULL) {
722 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
723 gst_object_unref (playsink->visualisation);
724 playsink->visualisation = NULL;
726 if (playsink->text_sink != NULL) {
727 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
728 gst_object_unref (playsink->text_sink);
729 playsink->text_sink = NULL;
732 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
733 playsink->videodeinterlacechain = NULL;
734 free_chain ((GstPlayChain *) playsink->videochain);
735 playsink->videochain = NULL;
736 free_chain ((GstPlayChain *) playsink->audiochain);
737 playsink->audiochain = NULL;
738 free_chain ((GstPlayChain *) playsink->vischain);
739 playsink->vischain = NULL;
740 free_chain ((GstPlayChain *) playsink->textchain);
741 playsink->textchain = NULL;
743 if (playsink->audio_tee_sink) {
744 gst_object_unref (playsink->audio_tee_sink);
745 playsink->audio_tee_sink = NULL;
748 if (playsink->audio_tee_vissrc) {
749 gst_element_release_request_pad (playsink->audio_tee,
750 playsink->audio_tee_vissrc);
751 gst_object_unref (playsink->audio_tee_vissrc);
752 playsink->audio_tee_vissrc = NULL;
755 if (playsink->audio_tee_asrc) {
756 gst_element_release_request_pad (playsink->audio_tee,
757 playsink->audio_tee_asrc);
758 gst_object_unref (playsink->audio_tee_asrc);
759 playsink->audio_tee_asrc = NULL;
762 g_free (playsink->font_desc);
763 playsink->font_desc = NULL;
765 g_free (playsink->subtitle_encoding);
766 playsink->subtitle_encoding = NULL;
768 playsink->stream_synchronizer = NULL;
770 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
772 g_list_free (playsink->colorbalance_channels);
773 playsink->colorbalance_channels = NULL;
775 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
779 gst_play_sink_finalize (GObject * object)
781 GstPlaySink *playsink;
783 playsink = GST_PLAY_SINK (object);
785 g_rec_mutex_clear (&playsink->lock);
787 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
791 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
794 GstElement **elem = NULL, *old = NULL;
796 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
798 GST_PLAY_SINK_LOCK (playsink);
800 case GST_PLAY_SINK_TYPE_AUDIO:
801 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
802 elem = &playsink->audio_sink;
804 case GST_PLAY_SINK_TYPE_VIDEO:
805 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
806 elem = &playsink->video_sink;
808 case GST_PLAY_SINK_TYPE_TEXT:
809 elem = &playsink->text_sink;
817 gst_object_ref (sink);
820 GST_PLAY_SINK_UNLOCK (playsink);
824 gst_element_set_state (old, GST_STATE_NULL);
825 gst_object_unref (old);
830 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
832 GstElement *result = NULL;
833 GstElement *elem = NULL, *chainp = NULL;
835 GST_PLAY_SINK_LOCK (playsink);
837 case GST_PLAY_SINK_TYPE_AUDIO:
838 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
840 GstPlayAudioChain *chain;
841 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
842 chainp = chain->sink;
843 elem = playsink->audio_sink;
846 case GST_PLAY_SINK_TYPE_VIDEO:
847 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
849 GstPlayVideoChain *chain;
850 if ((chain = (GstPlayVideoChain *) playsink->videochain))
851 chainp = chain->sink;
852 elem = playsink->video_sink;
855 case GST_PLAY_SINK_TYPE_TEXT:
857 GstPlayTextChain *chain;
858 if ((chain = (GstPlayTextChain *) playsink->textchain))
859 chainp = chain->sink;
860 elem = playsink->text_sink;
867 /* we have an active chain with a sink, get the sink */
868 result = gst_object_ref (chainp);
870 /* nothing found, return last configured sink */
871 if (result == NULL && elem)
872 result = gst_object_ref (elem);
873 GST_PLAY_SINK_UNLOCK (playsink);
878 static GstPadProbeReturn
879 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
882 GstPlaySink *playsink;
883 GstPlayVisChain *chain;
885 playsink = GST_PLAY_SINK (user_data);
887 GST_PLAY_SINK_LOCK (playsink);
888 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
889 /* now try to change the plugin in the running vis chain */
890 if (!(chain = (GstPlayVisChain *) playsink->vischain))
893 /* unlink the old plugin and unghost the pad */
894 gst_pad_unlink (chain->blockpad, chain->vissinkpad);
895 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
897 /* set the old plugin to NULL and remove */
898 gst_element_set_state (chain->vis, GST_STATE_NULL);
899 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
901 /* add new plugin and set state to playing */
902 chain->vis = playsink->visualisation;
903 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
904 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
907 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
908 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
911 gst_pad_link_full (chain->blockpad, chain->vissinkpad,
912 GST_PAD_LINK_CHECK_NOTHING);
913 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
917 GST_PLAY_SINK_UNLOCK (playsink);
919 /* remove the probe and unblock the pad */
920 return GST_PAD_PROBE_REMOVE;
924 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
926 GstPlayVisChain *chain;
928 /* setting NULL means creating the default vis plugin */
930 vis = gst_element_factory_make ("goom", "vis");
932 /* simply return if we don't have a vis plugin here */
936 GST_PLAY_SINK_LOCK (playsink);
937 /* first store the new vis */
938 if (playsink->visualisation)
939 gst_object_unref (playsink->visualisation);
941 gst_object_ref_sink (vis);
942 playsink->visualisation = vis;
944 /* now try to change the plugin in the running vis chain, if we have no chain,
945 * we don't bother, any future vis chain will be created with the new vis
947 if (!(chain = (GstPlayVisChain *) playsink->vischain))
950 /* block the pad, the next time the callback is called we can change the
951 * visualisation. It's possible that this never happens or that the pad was
952 * already blocked. If the callback never happens, we don't have new data so
953 * we don't need the new vis plugin. If the pad was already blocked, the
954 * function returns FALSE but the previous pad block will do the right thing
956 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
957 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
958 gst_play_sink_vis_blocked, playsink, NULL);
960 GST_PLAY_SINK_UNLOCK (playsink);
966 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
968 GstElement *result = NULL;
969 GstPlayVisChain *chain;
971 GST_PLAY_SINK_LOCK (playsink);
972 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
973 /* we have an active chain, get the sink */
975 result = gst_object_ref (chain->vis);
977 /* nothing found, return last configured sink */
978 if (result == NULL && playsink->visualisation)
979 result = gst_object_ref (playsink->visualisation);
980 GST_PLAY_SINK_UNLOCK (playsink);
986 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
988 GstPlayAudioChain *chain;
990 GST_PLAY_SINK_LOCK (playsink);
991 playsink->volume = volume;
992 chain = (GstPlayAudioChain *) playsink->audiochain;
993 if (chain && chain->volume) {
994 GST_LOG_OBJECT (playsink, "elements: volume=%" GST_PTR_FORMAT ", mute=%"
995 GST_PTR_FORMAT "; new volume=%.03f, mute=%d", chain->volume,
996 chain->mute, volume, playsink->mute);
997 /* if there is a mute element or we are not muted, set the volume */
998 if (chain->mute || !playsink->mute)
999 g_object_set (chain->volume, "volume", volume, NULL);
1001 GST_LOG_OBJECT (playsink, "no volume element");
1002 playsink->volume_changed = TRUE;
1004 GST_PLAY_SINK_UNLOCK (playsink);
1008 gst_play_sink_get_volume (GstPlaySink * playsink)
1011 GstPlayAudioChain *chain;
1013 GST_PLAY_SINK_LOCK (playsink);
1014 chain = (GstPlayAudioChain *) playsink->audiochain;
1015 result = playsink->volume;
1016 if (chain && chain->volume) {
1017 if (chain->mute || !playsink->mute) {
1018 g_object_get (chain->volume, "volume", &result, NULL);
1019 playsink->volume = result;
1022 GST_PLAY_SINK_UNLOCK (playsink);
1028 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1030 GstPlayAudioChain *chain;
1032 GST_PLAY_SINK_LOCK (playsink);
1033 playsink->mute = mute;
1034 chain = (GstPlayAudioChain *) playsink->audiochain;
1037 g_object_set (chain->mute, "mute", mute, NULL);
1038 } else if (chain->volume) {
1040 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
1042 g_object_set (chain->volume, "volume", (gdouble) playsink->volume,
1046 playsink->mute_changed = TRUE;
1048 GST_PLAY_SINK_UNLOCK (playsink);
1052 gst_play_sink_get_mute (GstPlaySink * playsink)
1055 GstPlayAudioChain *chain;
1057 GST_PLAY_SINK_LOCK (playsink);
1058 chain = (GstPlayAudioChain *) playsink->audiochain;
1059 if (chain && chain->mute) {
1060 g_object_get (chain->mute, "mute", &result, NULL);
1061 playsink->mute = result;
1063 result = playsink->mute;
1065 GST_PLAY_SINK_UNLOCK (playsink);
1071 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1075 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1076 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1080 add_chain (GstPlayChain * chain, gboolean add)
1082 if (chain->added == add)
1086 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1088 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1089 /* we don't want to lose our sink status */
1090 GST_OBJECT_FLAG_SET (chain->playsink, GST_ELEMENT_FLAG_SINK);
1099 activate_chain (GstPlayChain * chain, gboolean activate)
1103 if (chain->activated == activate)
1106 GST_OBJECT_LOCK (chain->playsink);
1107 state = GST_STATE_TARGET (chain->playsink);
1108 GST_OBJECT_UNLOCK (chain->playsink);
1111 gst_element_set_state (chain->bin, state);
1113 gst_element_set_state (chain->bin, GST_STATE_NULL);
1115 chain->activated = activate;
1121 element_is_sink (GstElement * element)
1125 GST_OBJECT_LOCK (element);
1126 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1127 GST_OBJECT_UNLOCK (element);
1129 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1134 element_has_property (GstElement * element, const gchar * pname, GType type)
1138 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1140 if (pspec == NULL) {
1141 GST_DEBUG_OBJECT (element, "no %s property", pname);
1145 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1146 g_type_is_a (pspec->value_type, type)) {
1147 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1148 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1152 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1153 "and we expected it to be of type %s", pname,
1154 g_type_name (pspec->value_type), g_type_name (type));
1161 const gchar *prop_name;
1164 } FindPropertyHelper;
1167 find_property (const GValue * item, FindPropertyHelper * helper)
1169 GstElement *element = g_value_get_object (item);
1170 if (helper->need_sink && !element_is_sink (element)) {
1174 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1178 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1179 (helper->need_sink) ? "sink" : "element");
1180 return 0; /* keep it */
1183 /* FIXME: why not move these functions into core? */
1184 /* find a sink in the hierarchy with a property named @name. This function does
1185 * not increase the refcount of the returned object and thus remains valid as
1186 * long as the bin is valid. */
1188 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1189 const gchar * name, GType expected_type)
1191 GstElement *result = NULL;
1194 if (element_has_property (obj, name, expected_type)) {
1196 } else if (GST_IS_BIN (obj)) {
1198 GValue item = { 0, };
1199 FindPropertyHelper helper = { name, expected_type, TRUE };
1201 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1202 found = gst_iterator_find_custom (it,
1203 (GCompareFunc) find_property, &item, &helper);
1204 gst_iterator_free (it);
1206 result = g_value_get_object (&item);
1207 /* we don't need the extra ref */
1208 g_value_unset (&item);
1214 /* find an object in the hierarchy with a property named @name */
1216 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1217 const gchar * name, GType expected_type)
1219 GstElement *result = NULL;
1222 if (GST_IS_BIN (obj)) {
1224 GValue item = { 0, };
1225 FindPropertyHelper helper = { name, expected_type, FALSE };
1227 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1228 found = gst_iterator_find_custom (it,
1229 (GCompareFunc) find_property, &item, &helper);
1230 gst_iterator_free (it);
1232 result = g_value_dup_object (&item);
1233 g_value_unset (&item);
1236 if (element_has_property (obj, name, expected_type)) {
1238 gst_object_ref (obj);
1245 do_async_start (GstPlaySink * playsink)
1247 GstMessage *message;
1249 if (!playsink->need_async_start) {
1250 GST_INFO_OBJECT (playsink, "no async_start needed");
1254 playsink->async_pending = TRUE;
1256 GST_INFO_OBJECT (playsink, "Sending async_start message");
1257 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1258 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1259 (playsink), message);
1263 do_async_done (GstPlaySink * playsink)
1265 GstMessage *message;
1267 if (playsink->async_pending) {
1268 GST_INFO_OBJECT (playsink, "Sending async_done message");
1269 message = gst_message_new_async_done (GST_OBJECT_CAST (playsink), FALSE);
1270 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1271 (playsink), message);
1273 playsink->async_pending = FALSE;
1276 playsink->need_async_start = FALSE;
1279 /* try to change the state of an element. This function returns the element when
1280 * the state change could be performed. When this function returns NULL an error
1281 * occured and the element is unreffed if @unref is TRUE. */
1283 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1285 GstStateChangeReturn ret;
1288 ret = gst_element_set_state (element, GST_STATE_READY);
1289 if (ret == GST_STATE_CHANGE_FAILURE) {
1290 GST_DEBUG_OBJECT (playsink, "failed state change..");
1291 gst_element_set_state (element, GST_STATE_NULL);
1293 gst_object_unref (element);
1300 /* make the element (bin) that contains the elements needed to perform
1301 * video display. Only used for *raw* video streams.
1303 * +------------------------------------------------------------+
1305 * | +-------+ +----------+ +----------+ +---------+ |
1306 * | | queue | |colorspace| |videoscale| |videosink| |
1307 * | +-sink src-sink src-sink src-sink | |
1308 * | | +-------+ +----------+ +----------+ +---------+ |
1310 * +------------------------------------------------------------+
1313 static GstPlayVideoDeinterlaceChain *
1314 gen_video_deinterlace_chain (GstPlaySink * playsink)
1316 GstPlayVideoDeinterlaceChain *chain;
1319 GstElement *head = NULL, *prev = NULL;
1321 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1322 chain->chain.playsink = playsink;
1324 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1326 /* create a bin to hold objects, as we create them we add them to this bin so
1327 * that when something goes wrong we only need to unref the bin */
1328 chain->chain.bin = gst_bin_new ("vdbin");
1329 bin = GST_BIN_CAST (chain->chain.bin);
1330 gst_object_ref_sink (bin);
1332 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1333 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1334 if (chain->conv == NULL) {
1335 post_missing_element_message (playsink, COLORSPACE);
1336 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1337 (_("Missing element '%s' - check your GStreamer installation."),
1338 COLORSPACE), ("video rendering might fail"));
1340 gst_bin_add (bin, chain->conv);
1345 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1346 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1347 if (chain->deinterlace == NULL) {
1348 post_missing_element_message (playsink, "deinterlace");
1349 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1350 (_("Missing element '%s' - check your GStreamer installation."),
1351 "deinterlace"), ("deinterlacing won't work"));
1353 gst_bin_add (bin, chain->deinterlace);
1355 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1356 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1359 head = chain->deinterlace;
1361 prev = chain->deinterlace;
1365 pad = gst_element_get_static_pad (head, "sink");
1366 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1367 gst_object_unref (pad);
1369 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1373 pad = gst_element_get_static_pad (prev, "src");
1374 chain->srcpad = gst_ghost_pad_new ("src", pad);
1375 gst_object_unref (pad);
1377 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1380 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1381 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1387 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1388 (NULL), ("Failed to configure the video deinterlace chain."));
1389 free_chain ((GstPlayChain *) chain);
1395 is_valid_color_balance_element (GstColorBalance * bal)
1397 gboolean have_brightness = FALSE;
1398 gboolean have_contrast = FALSE;
1399 gboolean have_hue = FALSE;
1400 gboolean have_saturation = FALSE;
1401 const GList *channels, *l;
1403 channels = gst_color_balance_list_channels (bal);
1404 for (l = channels; l; l = l->next) {
1405 GstColorBalanceChannel *ch = l->data;
1407 if (g_strrstr (ch->label, "BRIGHTNESS"))
1408 have_brightness = TRUE;
1409 else if (g_strrstr (ch->label, "CONTRAST"))
1410 have_contrast = TRUE;
1411 else if (g_strrstr (ch->label, "HUE"))
1413 else if (g_strrstr (ch->label, "SATURATION"))
1414 have_saturation = TRUE;
1417 return have_brightness && have_contrast && have_hue && have_saturation;
1421 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1424 GstColorBalance *cb, **cb_out = user_data;
1426 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1427 valid = is_valid_color_balance_element (cb);
1430 && gst_color_balance_get_balance_type (*cb_out) ==
1431 GST_COLOR_BALANCE_SOFTWARE) {
1432 gst_object_unref (*cb_out);
1433 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1434 } else if (!*cb_out) {
1435 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1440 static GstColorBalance *
1441 find_color_balance_element (GstElement * element)
1444 GstColorBalance *cb = NULL;
1446 if (GST_IS_COLOR_BALANCE (element)
1447 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1448 return GST_COLOR_BALANCE (gst_object_ref (element));
1449 else if (!GST_IS_BIN (element))
1452 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1453 GST_TYPE_COLOR_BALANCE);
1454 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1455 &cb) == GST_ITERATOR_RESYNC)
1456 gst_iterator_resync (it);
1457 gst_iterator_free (it);
1463 colorbalance_value_changed_cb (GstColorBalance * balance,
1464 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1469 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1470 GstColorBalanceChannel *proxy = l->data;
1472 if (g_strrstr (channel->label, proxy->label)) {
1475 /* Convert to [0, 1] range */
1478 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1479 (gdouble) channel->min_value);
1480 /* Convert to proxy range */
1482 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1483 (gdouble) proxy->min_value);
1484 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1486 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1487 playsink->colorbalance_values[i]);
1494 update_colorbalance (GstPlaySink * playsink)
1496 GstColorBalance *balance = NULL;
1500 GST_OBJECT_LOCK (playsink);
1501 if (playsink->colorbalance_element) {
1503 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1505 GST_OBJECT_UNLOCK (playsink);
1509 g_signal_handlers_block_by_func (balance,
1510 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1512 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1513 GstColorBalanceChannel *proxy = l->data;
1514 GstColorBalanceChannel *channel = NULL;
1515 const GList *channels, *k;
1517 channels = gst_color_balance_list_channels (balance);
1518 for (k = channels; k; k = k->next) {
1519 GstColorBalanceChannel *tmp = k->data;
1521 if (g_strrstr (tmp->label, proxy->label)) {
1529 gst_color_balance_set_value (balance, channel,
1530 playsink->colorbalance_values[i]);
1533 g_signal_handlers_unblock_by_func (balance,
1534 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1536 gst_object_unref (balance);
1539 /* make the element (bin) that contains the elements needed to perform
1542 * +------------------------------------------------------------+
1544 * | +-------+ +----------+ +----------+ +---------+ |
1545 * | | queue | |colorspace| |videoscale| |videosink| |
1546 * | +-sink src-sink src-sink src-sink | |
1547 * | | +-------+ +----------+ +----------+ +---------+ |
1549 * +------------------------------------------------------------+
1552 static GstPlayVideoChain *
1553 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1555 GstPlayVideoChain *chain;
1558 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1560 chain = g_new0 (GstPlayVideoChain, 1);
1561 chain->chain.playsink = playsink;
1562 chain->chain.raw = raw;
1564 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1566 if (playsink->video_sink) {
1567 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1568 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1570 /* only try fallback if no specific sink was chosen */
1571 if (chain->sink == NULL) {
1572 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1573 elem = gst_element_factory_make ("autovideosink", "videosink");
1574 chain->sink = try_element (playsink, elem, TRUE);
1576 if (chain->sink == NULL) {
1577 /* if default sink from config.h is different then try it too */
1578 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1579 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1580 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1581 chain->sink = try_element (playsink, elem, TRUE);
1585 playsink->video_sink = gst_object_ref (chain->sink);
1587 if (chain->sink == NULL)
1591 /* if we can disable async behaviour of the sink, we can avoid adding a
1592 * queue for the audio chain. */
1594 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1597 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1598 async, GST_ELEMENT_NAME (elem));
1599 g_object_set (elem, "async", async, NULL);
1600 chain->async = async;
1602 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1603 chain->async = TRUE;
1606 /* Make sure the aspect ratio is kept */
1608 gst_play_sink_find_property_sinks (playsink, chain->sink,
1609 "force-aspect-ratio", G_TYPE_BOOLEAN);
1611 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1613 /* find ts-offset element */
1614 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1615 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1618 /* create a bin to hold objects, as we create them we add them to this bin so
1619 * that when something goes wrong we only need to unref the bin */
1620 chain->chain.bin = gst_bin_new ("vbin");
1621 bin = GST_BIN_CAST (chain->chain.bin);
1622 gst_object_ref_sink (bin);
1623 gst_bin_add (bin, chain->sink);
1625 /* Get the VideoOverlay element */
1627 GstVideoOverlay *overlay = NULL;
1629 GST_OBJECT_LOCK (playsink);
1630 if (playsink->overlay_element)
1631 gst_object_unref (playsink->overlay_element);
1632 playsink->overlay_element =
1633 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1634 GST_TYPE_VIDEO_OVERLAY));
1635 if (playsink->overlay_element)
1636 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1637 GST_OBJECT_UNLOCK (playsink);
1640 if (playsink->overlay_handle_set)
1641 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1642 if (playsink->overlay_handle_events_set)
1643 gst_video_overlay_handle_events (overlay,
1644 playsink->overlay_handle_events);
1645 if (playsink->overlay_render_rectangle_set)
1646 gst_video_overlay_set_render_rectangle (overlay,
1647 playsink->overlay_x, playsink->overlay_y,
1648 playsink->overlay_width, playsink->overlay_height);
1649 gst_object_unref (overlay);
1653 /* decouple decoder from sink, this improves playback quite a lot since the
1654 * decoder can continue while the sink blocks for synchronisation. We don't
1655 * need a lot of buffers as this consumes a lot of memory and we don't want
1656 * too little because else we would be context switching too quickly. */
1657 chain->queue = gst_element_factory_make ("queue", "vqueue");
1658 if (chain->queue == NULL) {
1659 post_missing_element_message (playsink, "queue");
1660 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1661 (_("Missing element '%s' - check your GStreamer installation."),
1662 "queue"), ("video rendering might be suboptimal"));
1666 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1667 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1668 gst_bin_add (bin, chain->queue);
1669 head = prev = chain->queue;
1672 GST_OBJECT_LOCK (playsink);
1673 if (playsink->colorbalance_element) {
1674 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1675 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1676 gst_object_unref (playsink->colorbalance_element);
1678 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1679 GST_OBJECT_UNLOCK (playsink);
1681 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1682 || (!playsink->colorbalance_element
1683 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1684 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1685 gboolean use_balance = !playsink->colorbalance_element
1686 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1688 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1690 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1691 "use-converters", use_converters, "use-balance", use_balance, NULL);
1693 GST_OBJECT_LOCK (playsink);
1694 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1695 playsink->colorbalance_element =
1696 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1697 (chain->conv)->balance));
1698 GST_OBJECT_UNLOCK (playsink);
1700 gst_bin_add (bin, chain->conv);
1702 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1703 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1711 update_colorbalance (playsink);
1714 GST_DEBUG_OBJECT (playsink, "linking to sink");
1715 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1716 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1720 pad = gst_element_get_static_pad (head, "sink");
1721 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1723 /* sending audio/video flushes break stream changes when the pipeline
1724 * is paused and played again in 0.10 */
1726 gst_pad_set_event_function (chain->sinkpad,
1727 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1728 gst_pad_set_chain_function (chain->sinkpad,
1729 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1732 gst_object_unref (pad);
1733 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1740 if (!elem && !playsink->video_sink) {
1741 post_missing_element_message (playsink, "autovideosink");
1742 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1743 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
1744 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1745 (_("Both autovideosink and %s elements are missing."),
1746 DEFAULT_VIDEOSINK), (NULL));
1748 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
1749 (_("The autovideosink element is missing.")), (NULL));
1752 if (playsink->video_sink) {
1753 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1754 (_("Configured videosink %s is not working."),
1755 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
1756 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1757 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1758 (_("Both autovideosink and %s elements are not working."),
1759 DEFAULT_VIDEOSINK), (NULL));
1761 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
1762 (_("The autovideosink element is not working.")), (NULL));
1765 free_chain ((GstPlayChain *) chain);
1771 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1772 (NULL), ("Failed to configure the video sink."));
1773 /* checking sink made it READY */
1774 gst_element_set_state (chain->sink, GST_STATE_NULL);
1775 /* Remove chain from the bin to allow reuse later */
1776 gst_bin_remove (bin, chain->sink);
1777 free_chain ((GstPlayChain *) chain);
1783 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1786 GstPlayVideoChain *chain;
1787 GstStateChangeReturn ret;
1789 chain = playsink->videochain;
1791 chain->chain.raw = raw;
1793 /* if the chain was active we don't do anything */
1794 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
1797 /* try to set the sink element to READY again */
1798 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
1799 if (ret == GST_STATE_CHANGE_FAILURE)
1802 /* Get the VideoOverlay element */
1804 GstVideoOverlay *overlay = NULL;
1806 GST_OBJECT_LOCK (playsink);
1807 if (playsink->overlay_element)
1808 gst_object_unref (playsink->overlay_element);
1809 playsink->overlay_element =
1810 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1811 GST_TYPE_VIDEO_OVERLAY));
1812 if (playsink->overlay_element)
1813 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1814 GST_OBJECT_UNLOCK (playsink);
1817 if (playsink->overlay_handle_set)
1818 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1819 if (playsink->overlay_handle_events_set)
1820 gst_video_overlay_handle_events (overlay,
1821 playsink->overlay_handle_events);
1822 if (playsink->overlay_render_rectangle_set)
1823 gst_video_overlay_set_render_rectangle (overlay,
1824 playsink->overlay_x, playsink->overlay_y,
1825 playsink->overlay_width, playsink->overlay_height);
1826 gst_object_unref (overlay);
1830 /* find ts-offset element */
1831 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1832 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1835 /* if we can disable async behaviour of the sink, we can avoid adding a
1836 * queue for the audio chain. */
1838 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1841 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1842 async, GST_ELEMENT_NAME (elem));
1843 g_object_set (elem, "async", async, NULL);
1844 chain->async = async;
1846 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1847 chain->async = TRUE;
1850 /* Make sure the aspect ratio is kept */
1852 gst_play_sink_find_property_sinks (playsink, chain->sink,
1853 "force-aspect-ratio", G_TYPE_BOOLEAN);
1855 g_object_set (elem, "force-aspect-ratio", TRUE, NULL);
1857 GST_OBJECT_LOCK (playsink);
1858 if (playsink->colorbalance_element) {
1859 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
1860 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1861 gst_object_unref (playsink->colorbalance_element);
1863 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1864 GST_OBJECT_UNLOCK (playsink);
1867 gboolean use_balance = !playsink->colorbalance_element
1868 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1870 g_object_set (chain->conv, "use-balance", use_balance, NULL);
1872 GST_OBJECT_LOCK (playsink);
1873 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance)
1874 playsink->colorbalance_element =
1875 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1876 (chain->conv)->balance));
1877 GST_OBJECT_UNLOCK (playsink);
1880 update_colorbalance (playsink);
1886 _generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
1890 GstStructure *structure;
1891 event = gst_event_new_segment (segment);
1892 structure = gst_event_writable_structure (event);
1893 gst_structure_id_set (structure,
1894 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
1899 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
1900 const gchar * sink_type,
1901 gboolean * sink_ignore_wrong_state,
1902 gboolean * sink_custom_flush_finished,
1903 gboolean * sink_pending_flush, GstSegment * sink_segment)
1905 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
1907 const GstStructure *structure = gst_event_get_structure (event);
1909 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
1910 gchar *custom_flush;
1911 gchar *custom_flush_finish;
1913 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
1914 custom_flush_finish =
1915 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
1916 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
1917 GST_DEBUG_OBJECT (pad,
1918 "Custom %s flush event received, marking to flush %s", sink_type,
1920 GST_PLAY_SINK_LOCK (playsink);
1921 *sink_ignore_wrong_state = TRUE;
1922 *sink_custom_flush_finished = FALSE;
1923 GST_PLAY_SINK_UNLOCK (playsink);
1924 } else if (strcmp (gst_structure_get_name (structure),
1925 custom_flush_finish) == 0) {
1926 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
1928 GST_PLAY_SINK_LOCK (playsink);
1929 *sink_pending_flush = TRUE;
1930 *sink_custom_flush_finished = TRUE;
1931 GST_PLAY_SINK_UNLOCK (playsink);
1934 g_free (custom_flush);
1935 g_free (custom_flush_finish);
1936 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1937 GST_PLAY_SINK_LOCK (playsink);
1938 GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
1940 gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
1941 GST_PLAY_SINK_UNLOCK (playsink);
1944 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
1945 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
1947 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1948 const GstSegment *segment;
1950 gst_event_parse_segment (event, &segment);
1951 GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
1953 GST_PLAY_SINK_LOCK (playsink);
1954 if (sink_segment->format != segment->format) {
1955 GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
1957 gst_format_get_name (sink_segment->format),
1958 gst_format_get_name (segment->format));
1959 gst_segment_init (sink_segment, segment->format);
1962 GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
1963 sink_type, sink_segment);
1964 gst_segment_copy_into (&playsink->text_segment, sink_segment);
1965 GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
1966 sink_type, sink_segment);
1967 GST_PLAY_SINK_UNLOCK (playsink);
1970 gst_event_unref (event);
1971 gst_object_unref (playsink);
1975 static GstFlowReturn
1976 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
1977 const gchar * sink_type,
1978 gboolean * sink_ignore_wrong_state,
1979 gboolean * sink_custom_flush_finished,
1980 gboolean * sink_pending_flush, GstSegment * sink_segment)
1982 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
1983 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
1986 GST_PLAY_SINK_LOCK (playsink);
1988 if (*sink_pending_flush) {
1990 GstStructure *structure;
1992 *sink_pending_flush = FALSE;
1994 GST_PLAY_SINK_UNLOCK (playsink);
1996 /* make the bin drop all cached data.
1997 * This event will be dropped on the src pad, if any. */
1998 event = gst_event_new_flush_start ();
1999 structure = gst_event_writable_structure (event);
2000 gst_structure_id_set (structure,
2001 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2003 GST_DEBUG_OBJECT (pad,
2004 "Pushing %s flush-start event with reset segment marker set: %"
2005 GST_PTR_FORMAT, sink_type, event);
2006 gst_pad_send_event (pad, event);
2008 /* make queue drop all cached data.
2009 * This event will be dropped on the src pad. */
2010 event = gst_event_new_flush_stop (TRUE);
2011 structure = gst_event_writable_structure (event);
2012 gst_structure_id_set (structure,
2013 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2015 GST_DEBUG_OBJECT (pad,
2016 "Pushing %s flush-stop event with reset segment marker set: %"
2017 GST_PTR_FORMAT, sink_type, event);
2018 gst_pad_send_event (pad, event);
2020 /* Re-sync queue segment info after flush-stop.
2021 * This event will be dropped on the src pad. */
2022 if (sink_segment->format != GST_FORMAT_UNDEFINED) {
2025 _generate_update_newsegment_event (pad, sink_segment, &event1);
2026 GST_DEBUG_OBJECT (playsink,
2027 "Pushing segment event with reset "
2028 "segment marker set: %" GST_PTR_FORMAT, event1);
2029 gst_pad_send_event (pad, event1);
2032 GST_PLAY_SINK_UNLOCK (playsink);
2035 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2037 GST_PLAY_SINK_LOCK (playsink);
2038 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2039 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2041 if (*sink_custom_flush_finished) {
2042 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2043 "wrong state for %s", sink_type);
2044 *sink_ignore_wrong_state = FALSE;
2049 GST_PLAY_SINK_UNLOCK (playsink);
2051 gst_object_unref (playsink);
2052 gst_object_unref (tbin);
2056 /* sending audio/video flushes break stream changes when the pipeline
2057 * is paused and played again in 0.10 */
2060 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2062 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2063 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2066 ret = gst_play_sink_sink_event (pad, event, "video",
2067 &playsink->video_ignore_wrong_state,
2068 &playsink->video_custom_flush_finished,
2069 &playsink->video_pending_flush, &playsink->video_segment);
2071 gst_object_unref (playsink);
2072 gst_object_unref (tbin);
2076 static GstFlowReturn
2077 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2079 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2080 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2083 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2084 &playsink->video_ignore_wrong_state,
2085 &playsink->video_custom_flush_finished,
2086 &playsink->video_pending_flush, &playsink->video_segment);
2088 gst_object_unref (playsink);
2089 gst_object_unref (tbin);
2094 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2096 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2097 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2100 ret = gst_play_sink_sink_event (pad, event, "audio",
2101 &playsink->audio_ignore_wrong_state,
2102 &playsink->audio_custom_flush_finished,
2103 &playsink->audio_pending_flush, &playsink->audio_segment);
2105 gst_object_unref (playsink);
2106 gst_object_unref (tbin);
2110 static GstFlowReturn
2111 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2113 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2114 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2117 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2118 &playsink->audio_ignore_wrong_state,
2119 &playsink->audio_custom_flush_finished,
2120 &playsink->audio_pending_flush, &playsink->audio_segment);
2122 gst_object_unref (playsink);
2123 gst_object_unref (tbin);
2129 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2132 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2135 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2136 &playsink->text_ignore_wrong_state,
2137 &playsink->text_custom_flush_finished,
2138 &playsink->text_pending_flush, &playsink->text_segment);
2140 gst_object_unref (playsink);
2145 static GstFlowReturn
2146 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2150 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2152 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2153 &playsink->text_ignore_wrong_state,
2154 &playsink->text_custom_flush_finished,
2155 &playsink->text_pending_flush, &playsink->text_segment);
2157 gst_object_unref (playsink);
2162 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2166 const GstStructure *structure;
2168 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2170 structure = gst_event_get_structure (event);
2173 gst_structure_id_has_field (structure,
2174 _playsink_reset_segment_event_marker_id)) {
2175 /* the events marked with a reset segment marker
2176 * are sent internally to reset the queue and
2177 * must be dropped here */
2178 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2179 "segment marker set: %" GST_PTR_FORMAT, event);
2184 ret = gst_proxy_pad_event_default (pad, parent, gst_event_ref (event));
2187 gst_event_unref (event);
2191 /* make an element for playback of video with subtitles embedded.
2192 * Only used for *raw* video streams.
2194 * +--------------------------------------------+
2196 * | +--------+ +-----------------+ |
2197 * | | queue | | subtitleoverlay | |
2198 * video--src sink---video_sink | |
2199 * | +--------+ | src--src
2200 * text------------------text_sink | |
2201 * | +-----------------+ |
2202 * +--------------------------------------------+
2205 static GstPlayTextChain *
2206 gen_text_chain (GstPlaySink * playsink)
2208 GstPlayTextChain *chain;
2211 GstPad *videosinkpad, *textsinkpad, *srcpad;
2213 chain = g_new0 (GstPlayTextChain, 1);
2214 chain->chain.playsink = playsink;
2216 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2218 chain->chain.bin = gst_bin_new ("tbin");
2219 bin = GST_BIN_CAST (chain->chain.bin);
2220 gst_object_ref_sink (bin);
2222 videosinkpad = textsinkpad = srcpad = NULL;
2224 /* first try to hook the text pad to the custom sink */
2225 if (playsink->text_sink) {
2226 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2227 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2230 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2233 /* make sure the sparse subtitles don't participate in the preroll */
2234 g_object_set (elem, "async", FALSE, NULL);
2235 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2236 gst_bin_add (bin, chain->sink);
2237 /* NOTE streamsynchronizer needs streams decoupled */
2238 /* make a little queue */
2239 chain->queue = gst_element_factory_make ("queue", "subqueue");
2240 if (chain->queue == NULL) {
2241 post_missing_element_message (playsink, "queue");
2242 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2243 (_("Missing element '%s' - check your GStreamer installation."),
2244 "queue"), ("rendering might be suboptimal"));
2246 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2247 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2248 "silent", TRUE, NULL);
2249 gst_bin_add (bin, chain->queue);
2251 /* we have a custom sink, this will be our textsinkpad */
2252 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2253 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2254 /* we're all fine now and we can add the sink to the chain */
2255 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2256 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2258 GST_WARNING_OBJECT (playsink,
2259 "can't find a sink pad on custom text sink");
2260 gst_bin_remove (bin, chain->sink);
2261 gst_bin_remove (bin, chain->queue);
2263 chain->queue = NULL;
2265 /* try to set sync to true but it's no biggie when we can't */
2266 if (chain->sink && (elem =
2267 gst_play_sink_find_property_sinks (playsink, chain->sink,
2268 "sync", G_TYPE_BOOLEAN)))
2269 g_object_set (elem, "sync", TRUE, NULL);
2272 gst_bin_remove (bin, chain->sink);
2274 GST_WARNING_OBJECT (playsink,
2275 "can't find async property in custom text sink");
2278 if (textsinkpad == NULL) {
2279 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2280 (_("Custom text sink element is not usable.")),
2281 ("fallback to default subtitleoverlay"));
2285 if (textsinkpad == NULL) {
2286 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2287 /* make a little queue */
2288 chain->queue = gst_element_factory_make ("queue", "vqueue");
2289 if (chain->queue == NULL) {
2290 post_missing_element_message (playsink, "queue");
2291 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2292 (_("Missing element '%s' - check your GStreamer installation."),
2293 "queue"), ("video rendering might be suboptimal"));
2295 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2296 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2297 "silent", TRUE, NULL);
2298 gst_bin_add (bin, chain->queue);
2299 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2303 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2304 if (chain->overlay == NULL) {
2305 post_missing_element_message (playsink, "subtitleoverlay");
2306 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2307 (_("Missing element '%s' - check your GStreamer installation."),
2308 "subtitleoverlay"), ("subtitle rendering disabled"));
2310 GstElement *element;
2312 gst_bin_add (bin, chain->overlay);
2314 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2315 if (playsink->font_desc) {
2316 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2317 playsink->font_desc, NULL);
2319 if (playsink->subtitle_encoding) {
2320 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2321 playsink->subtitle_encoding, NULL);
2324 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2325 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2327 /* make another little queue to decouple streams */
2328 element = gst_element_factory_make ("queue", "subqueue");
2329 if (element == NULL) {
2330 post_missing_element_message (playsink, "queue");
2331 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2332 (_("Missing element '%s' - check your GStreamer installation."),
2333 "queue"), ("rendering might be suboptimal"));
2335 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2336 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2337 "silent", TRUE, NULL);
2338 gst_bin_add (bin, element);
2339 if (gst_element_link_pads_full (element, "src", chain->overlay,
2340 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2341 textsinkpad = gst_element_get_static_pad (element, "sink");
2342 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2344 gst_bin_remove (bin, chain->sink);
2345 gst_bin_remove (bin, chain->overlay);
2347 chain->overlay = NULL;
2348 gst_object_unref (videosinkpad);
2349 videosinkpad = NULL;
2356 if (videosinkpad == NULL) {
2357 /* if we still don't have a videosink, we don't have an overlay. the only
2358 * thing we can do is insert an identity and ghost the src
2360 chain->identity = gst_element_factory_make ("identity", "tidentity");
2361 if (chain->identity == NULL) {
2362 post_missing_element_message (playsink, "identity");
2363 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2364 (_("Missing element '%s' - check your GStreamer installation."),
2365 "identity"), (NULL));
2367 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2368 g_object_set (chain->identity, "silent", TRUE, NULL);
2369 gst_bin_add (bin, chain->identity);
2370 srcpad = gst_element_get_static_pad (chain->identity, "src");
2371 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2375 /* expose the ghostpads */
2377 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2378 gst_object_unref (videosinkpad);
2379 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2382 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2383 gst_object_unref (textsinkpad);
2385 gst_pad_set_event_function (chain->textsinkpad,
2386 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2387 gst_pad_set_chain_function (chain->textsinkpad,
2388 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2390 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2393 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2394 gst_object_unref (srcpad);
2396 gst_pad_set_event_function (chain->srcpad,
2397 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2399 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2406 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2410 g_object_get (object, "volume", &vol, NULL);
2411 playsink->volume = vol;
2413 g_object_notify (G_OBJECT (playsink), "volume");
2417 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2421 g_object_get (object, "mute", &mute, NULL);
2422 playsink->mute = mute;
2424 g_object_notify (G_OBJECT (playsink), "mute");
2427 /* make the chain that contains the elements needed to perform
2430 * We add a tee as the first element so that we can link the visualisation chain
2431 * to it when requested.
2433 * +-------------------------------------------------------------+
2435 * | +---------+ +----------+ +---------+ +---------+ |
2436 * | |audioconv| |audioscale| | volume | |audiosink| |
2437 * | +-srck src-sink src-sink src-sink | |
2438 * | | +---------+ +----------+ +---------+ +---------+ |
2440 * +-------------------------------------------------------------+
2442 static GstPlayAudioChain *
2443 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2445 GstPlayAudioChain *chain;
2447 gboolean have_volume;
2449 GstElement *head, *prev, *elem = NULL;
2451 chain = g_new0 (GstPlayAudioChain, 1);
2452 chain->chain.playsink = playsink;
2453 chain->chain.raw = raw;
2455 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2457 if (playsink->audio_sink) {
2458 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2459 playsink->audio_sink);
2460 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2462 /* only try fallback if no specific sink was chosen */
2463 if (chain->sink == NULL) {
2464 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2465 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2466 chain->sink = try_element (playsink, elem, TRUE);
2468 if (chain->sink == NULL) {
2469 /* if default sink from config.h is different then try it too */
2470 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2471 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2472 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2473 chain->sink = try_element (playsink, elem, TRUE);
2477 playsink->audio_sink = gst_object_ref (chain->sink);
2479 if (chain->sink == NULL)
2482 chain->chain.bin = gst_bin_new ("abin");
2483 bin = GST_BIN_CAST (chain->chain.bin);
2484 gst_object_ref_sink (bin);
2485 gst_bin_add (bin, chain->sink);
2487 /* we have to add a queue when we need to decouple for the video sink in
2488 * visualisations and for streamsynchronizer */
2489 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2490 chain->queue = gst_element_factory_make ("queue", "aqueue");
2491 if (chain->queue == NULL) {
2492 post_missing_element_message (playsink, "queue");
2493 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2494 (_("Missing element '%s' - check your GStreamer installation."),
2495 "queue"), ("audio playback and visualizations might not work"));
2499 g_object_set (chain->queue, "silent", TRUE, NULL);
2500 gst_bin_add (bin, chain->queue);
2501 prev = head = chain->queue;
2504 /* find ts-offset element */
2505 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2506 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2509 /* check if the sink, or something within the sink, has the volume property.
2510 * If it does we don't need to add a volume element. */
2512 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2515 chain->volume = elem;
2517 g_signal_connect (chain->volume, "notify::volume",
2518 G_CALLBACK (notify_volume_cb), playsink);
2520 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2522 chain->sink_volume = TRUE;
2523 /* if the sink also has a mute property we can use this as well. We'll only
2524 * use the mute property if there is a volume property. We can simulate the
2525 * mute with the volume otherwise. */
2527 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2530 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2531 g_signal_connect (chain->mute, "notify::mute",
2532 G_CALLBACK (notify_mute_cb), playsink);
2534 /* use the sink to control the volume and mute */
2535 if (playsink->volume_changed) {
2536 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2537 playsink->volume_changed = FALSE;
2539 if (playsink->mute_changed) {
2541 g_object_set (chain->mute, "mute", playsink->mute, NULL);
2544 g_object_set (chain->volume, "volume", (gdouble) 0.0, NULL);
2546 playsink->mute_changed = FALSE;
2549 /* no volume, we need to add a volume element when we can */
2550 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2551 have_volume = FALSE;
2552 chain->sink_volume = FALSE;
2555 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2556 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2557 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2558 gboolean use_volume =
2559 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2560 GST_DEBUG_OBJECT (playsink,
2561 "creating audioconvert with use-converters %d, use-volume %d",
2562 use_converters, use_volume);
2564 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2565 "use-converters", use_converters, "use-volume", use_volume, NULL);
2566 gst_bin_add (bin, chain->conv);
2568 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2569 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2576 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2577 GstPlaySinkAudioConvert *conv =
2578 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2581 chain->volume = conv->volume;
2584 g_signal_connect (chain->volume, "notify::volume",
2585 G_CALLBACK (notify_volume_cb), playsink);
2587 /* volume also has the mute property */
2588 chain->mute = chain->volume;
2589 g_signal_connect (chain->mute, "notify::mute",
2590 G_CALLBACK (notify_mute_cb), playsink);
2592 /* configure with the latest volume and mute */
2593 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2595 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2601 /* we only have to link to the previous element if we have something in
2602 * front of the sink */
2603 GST_DEBUG_OBJECT (playsink, "linking to sink");
2604 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2605 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2609 /* post a warning if we have no way to configure the volume */
2611 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2612 (_("No volume control found")), ("Volume/mute is not available"));
2615 /* and ghost the sinkpad of the headmost element */
2616 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2617 pad = gst_element_get_static_pad (head, "sink");
2618 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2620 /* sending audio/video flushes break stream changes when the pipeline
2621 * is paused and played again in 0.10 */
2623 gst_pad_set_event_function (chain->sinkpad,
2624 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2625 gst_pad_set_chain_function (chain->sinkpad,
2626 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2629 gst_object_unref (pad);
2630 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2637 if (!elem && !playsink->audio_sink) {
2638 post_missing_element_message (playsink, "autoaudiosink");
2639 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2640 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2641 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2642 (_("Both autoaudiosink and %s elements are missing."),
2643 DEFAULT_AUDIOSINK), (NULL));
2645 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2646 (_("The autoaudiosink element is missing.")), (NULL));
2649 if (playsink->audio_sink) {
2650 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2651 (_("Configured audiosink %s is not working."),
2652 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2653 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2654 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2655 (_("Both autoaudiosink and %s elements are not working."),
2656 DEFAULT_AUDIOSINK), (NULL));
2658 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2659 (_("The autoaudiosink element is not working.")), (NULL));
2662 free_chain ((GstPlayChain *) chain);
2667 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2668 (NULL), ("Failed to configure the audio sink."));
2669 /* checking sink made it READY */
2670 gst_element_set_state (chain->sink, GST_STATE_NULL);
2671 /* Remove chain from the bin to allow reuse later */
2672 gst_bin_remove (bin, chain->sink);
2673 free_chain ((GstPlayChain *) chain);
2679 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2682 GstPlayAudioChain *chain;
2683 GstStateChangeReturn ret;
2684 GstPlaySinkAudioConvert *conv;
2686 chain = playsink->audiochain;
2687 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2689 chain->chain.raw = raw;
2691 /* if the chain was active we don't do anything */
2692 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
2695 /* try to set the sink element to READY again */
2696 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2697 if (ret == GST_STATE_CHANGE_FAILURE)
2700 /* find ts-offset element */
2701 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2702 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2705 /* check if the sink, or something within the sink, has the volume property.
2706 * If it does we don't need to add a volume element. */
2708 gst_play_sink_find_property_sinks (playsink, chain->sink, "volume",
2711 chain->volume = elem;
2713 if (playsink->volume_changed) {
2714 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
2716 /* use the sink to control the volume */
2717 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2718 playsink->volume_changed = FALSE;
2721 g_signal_connect (chain->volume, "notify::volume",
2722 G_CALLBACK (notify_volume_cb), playsink);
2723 /* if the sink also has a mute property we can use this as well. We'll only
2724 * use the mute property if there is a volume property. We can simulate the
2725 * mute with the volume otherwise. */
2727 gst_play_sink_find_property_sinks (playsink, chain->sink, "mute",
2730 GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
2731 g_signal_connect (chain->mute, "notify::mute",
2732 G_CALLBACK (notify_mute_cb), playsink);
2735 g_object_set (chain->conv, "use-volume", FALSE, NULL);
2737 /* no volume, we need to add a volume element when we can */
2738 g_object_set (chain->conv, "use-volume",
2739 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
2740 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2742 /* Disconnect signals */
2743 disconnect_chain (chain, playsink);
2745 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2746 chain->volume = conv->volume;
2747 chain->mute = chain->volume;
2749 g_signal_connect (chain->volume, "notify::volume",
2750 G_CALLBACK (notify_volume_cb), playsink);
2752 g_signal_connect (chain->mute, "notify::mute",
2753 G_CALLBACK (notify_mute_cb), playsink);
2755 /* configure with the latest volume and mute */
2756 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2757 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
2760 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
2766 * +-------------------------------------------------------------------+
2768 * | +----------+ +------------+ +----------+ +-------+ |
2769 * | | visqueue | | audioconv | | audiores | | vis | |
2770 * | +-sink src-sink + samp src-sink src-sink src-+ |
2771 * | | +----------+ +------------+ +----------+ +-------+ | |
2773 * +-------------------------------------------------------------------+
2776 static GstPlayVisChain *
2777 gen_vis_chain (GstPlaySink * playsink)
2779 GstPlayVisChain *chain;
2785 chain = g_new0 (GstPlayVisChain, 1);
2786 chain->chain.playsink = playsink;
2788 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
2790 chain->chain.bin = gst_bin_new ("visbin");
2791 bin = GST_BIN_CAST (chain->chain.bin);
2792 gst_object_ref_sink (bin);
2794 /* we're queuing raw audio here, we can remove this queue when we can disable
2795 * async behaviour in the video sink. */
2796 chain->queue = gst_element_factory_make ("queue", "visqueue");
2797 if (chain->queue == NULL)
2799 g_object_set (chain->queue, "silent", TRUE, NULL);
2800 gst_bin_add (bin, chain->queue);
2802 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
2803 if (chain->conv == NULL)
2804 goto no_audioconvert;
2805 gst_bin_add (bin, chain->conv);
2807 chain->resample = gst_element_factory_make ("audioresample", "aresample");
2808 if (chain->resample == NULL)
2809 goto no_audioresample;
2810 gst_bin_add (bin, chain->resample);
2812 /* this pad will be used for blocking the dataflow and switching the vis
2814 chain->blockpad = gst_element_get_static_pad (chain->resample, "src");
2816 if (playsink->visualisation) {
2817 GST_DEBUG_OBJECT (playsink, "trying configure vis");
2818 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
2820 if (chain->vis == NULL) {
2821 GST_DEBUG_OBJECT (playsink, "trying goom");
2822 elem = gst_element_factory_make ("goom", "vis");
2823 chain->vis = try_element (playsink, elem, TRUE);
2825 if (chain->vis == NULL)
2828 gst_bin_add (bin, chain->vis);
2830 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
2831 GST_PAD_LINK_CHECK_NOTHING);
2833 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
2834 GST_PAD_LINK_CHECK_NOTHING);
2836 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
2837 GST_PAD_LINK_CHECK_NOTHING);
2841 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
2842 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
2844 pad = gst_element_get_static_pad (chain->queue, "sink");
2845 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2846 gst_object_unref (pad);
2847 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2849 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
2850 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2857 post_missing_element_message (playsink, "queue");
2858 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2859 (_("Missing element '%s' - check your GStreamer installation."),
2861 free_chain ((GstPlayChain *) chain);
2866 post_missing_element_message (playsink, "audioconvert");
2867 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2868 (_("Missing element '%s' - check your GStreamer installation."),
2869 "audioconvert"), ("possibly a liboil version mismatch?"));
2870 free_chain ((GstPlayChain *) chain);
2875 post_missing_element_message (playsink, "audioresample");
2876 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2877 (_("Missing element '%s' - check your GStreamer installation."),
2878 "audioresample"), (NULL));
2879 free_chain ((GstPlayChain *) chain);
2884 post_missing_element_message (playsink, "goom");
2885 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2886 (_("Missing element '%s' - check your GStreamer installation."),
2888 free_chain ((GstPlayChain *) chain);
2893 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2894 (NULL), ("Failed to configure the visualisation element."));
2895 /* element made it to READY */
2896 gst_element_set_state (chain->vis, GST_STATE_NULL);
2897 free_chain ((GstPlayChain *) chain);
2902 /* this function is called when all the request pads are requested and when we
2903 * have to construct the final pipeline. Based on the flags we construct the
2904 * final output pipelines.
2907 gst_play_sink_reconfigure (GstPlaySink * playsink)
2910 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
2912 GST_DEBUG_OBJECT (playsink, "reconfiguring");
2914 /* assume we need nothing */
2915 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
2917 GST_PLAY_SINK_LOCK (playsink);
2918 GST_OBJECT_LOCK (playsink);
2919 /* get flags, there are protected with the object lock */
2920 flags = playsink->flags;
2921 GST_OBJECT_UNLOCK (playsink);
2923 /* figure out which components we need */
2924 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
2925 /* we have subtitles and we are requested to show it */
2929 GST_OBJECT_LOCK (playsink);
2930 if (playsink->overlay_element)
2931 gst_object_unref (playsink->overlay_element);
2932 playsink->overlay_element = NULL;
2934 if (playsink->colorbalance_element) {
2935 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
2936 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2937 gst_object_unref (playsink->colorbalance_element);
2939 playsink->colorbalance_element = NULL;
2940 GST_OBJECT_UNLOCK (playsink);
2942 if (((flags & GST_PLAY_FLAG_VIDEO)
2943 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
2944 /* we have video and we are requested to show it */
2947 /* we only deinterlace if native video is not requested and
2948 * we have raw video */
2949 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
2950 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
2951 need_deinterlace = TRUE;
2954 if (playsink->audio_pad) {
2955 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
2958 if (playsink->audio_pad_raw) {
2959 /* only can do vis with raw uncompressed audio */
2960 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
2961 /* also add video when we add visualisation */
2968 /* we have a text_pad and we need text rendering, in this case we need a
2969 * video_pad to combine the video with the text or visualizations */
2970 if (need_text && !need_video && !playsink->text_sink) {
2971 if (playsink->video_pad) {
2973 } else if (need_audio) {
2974 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
2975 (_("Can't play a text file without video or visualizations.")),
2976 ("Have text pad but no video pad or visualizations"));
2979 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
2980 (_("Can't play a text file without video or visualizations.")),
2981 ("Have text pad but no video pad or visualizations"));
2982 GST_PLAY_SINK_UNLOCK (playsink);
2987 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
2988 need_video, need_vis, need_text);
2990 /* set up video pipeline */
2992 gboolean raw, async;
2994 /* we need a raw sink when we do vis or when we have a raw pad */
2995 raw = need_vis ? TRUE : playsink->video_pad_raw;
2996 /* we try to set the sink async=FALSE when we need vis, this way we can
2997 * avoid a queue in the audio chain. */
3000 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3001 playsink->video_pad_raw);
3003 if (playsink->videochain) {
3004 /* try to reactivate the chain */
3005 if (!setup_video_chain (playsink, raw, async)) {
3006 if (playsink->video_sinkpad_stream_synchronizer) {
3007 gst_element_release_request_pad (GST_ELEMENT_CAST
3008 (playsink->stream_synchronizer),
3009 playsink->video_sinkpad_stream_synchronizer);
3010 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3011 playsink->video_sinkpad_stream_synchronizer = NULL;
3012 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3013 playsink->video_srcpad_stream_synchronizer = NULL;
3016 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3018 /* Remove the sink from the bin to keep its state
3019 * and unparent it to allow reuse */
3020 if (playsink->videochain->sink)
3021 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3022 playsink->videochain->sink);
3024 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3025 free_chain ((GstPlayChain *) playsink->videochain);
3026 playsink->videochain = NULL;
3030 if (!playsink->videochain)
3031 playsink->videochain = gen_video_chain (playsink, raw, async);
3032 if (!playsink->videochain)
3035 if (!playsink->video_sinkpad_stream_synchronizer) {
3036 GValue item = { 0, };
3039 playsink->video_sinkpad_stream_synchronizer =
3040 gst_element_get_request_pad (GST_ELEMENT_CAST
3041 (playsink->stream_synchronizer), "sink_%u");
3042 it = gst_pad_iterate_internal_links
3043 (playsink->video_sinkpad_stream_synchronizer);
3045 gst_iterator_next (it, &item);
3046 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3047 g_value_unset (&item);
3048 g_assert (playsink->video_srcpad_stream_synchronizer);
3049 gst_iterator_free (it);
3052 if (playsink->video_pad)
3053 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3054 playsink->video_sinkpad_stream_synchronizer);
3056 if (need_deinterlace) {
3057 if (!playsink->videodeinterlacechain)
3058 playsink->videodeinterlacechain =
3059 gen_video_deinterlace_chain (playsink);
3060 if (!playsink->videodeinterlacechain)
3063 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3065 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3067 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3068 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3070 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3071 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3073 if (playsink->videodeinterlacechain) {
3074 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3075 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3080 GST_DEBUG_OBJECT (playsink, "adding video chain");
3081 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3082 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3083 /* if we are not part of vis or subtitles, set the ghostpad target */
3084 if (!need_vis && !need_text && (!playsink->textchain
3085 || !playsink->text_pad)) {
3086 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3087 if (need_deinterlace)
3088 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3089 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3091 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3092 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3095 GST_DEBUG_OBJECT (playsink, "no video needed");
3096 if (playsink->videochain) {
3097 GST_DEBUG_OBJECT (playsink, "removing video chain");
3098 if (playsink->vischain) {
3101 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3103 /* also had visualisation, release the tee srcpad before we then
3104 * unlink the video from it */
3105 if (playsink->audio_tee_vissrc) {
3106 gst_element_release_request_pad (playsink->audio_tee,
3107 playsink->audio_tee_vissrc);
3108 gst_object_unref (playsink->audio_tee_vissrc);
3109 playsink->audio_tee_vissrc = NULL;
3112 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3113 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3116 if (playsink->video_sinkpad_stream_synchronizer) {
3117 gst_element_release_request_pad (GST_ELEMENT_CAST
3118 (playsink->stream_synchronizer),
3119 playsink->video_sinkpad_stream_synchronizer);
3120 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3121 playsink->video_sinkpad_stream_synchronizer = NULL;
3122 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3123 playsink->video_srcpad_stream_synchronizer = NULL;
3126 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3127 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3128 if (playsink->videochain->ts_offset)
3129 gst_object_unref (playsink->videochain->ts_offset);
3130 playsink->videochain->ts_offset = NULL;
3133 if (playsink->videodeinterlacechain) {
3134 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3135 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3138 if (playsink->video_pad)
3139 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3145 GST_DEBUG_OBJECT (playsink, "adding audio");
3147 /* get a raw sink if we are asked for a raw pad */
3148 raw = playsink->audio_pad_raw;
3150 if (playsink->audiochain) {
3151 /* try to reactivate the chain */
3152 if (!setup_audio_chain (playsink, raw)) {
3153 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3154 if (playsink->audio_tee_asrc) {
3155 gst_element_release_request_pad (playsink->audio_tee,
3156 playsink->audio_tee_asrc);
3157 gst_object_unref (playsink->audio_tee_asrc);
3158 playsink->audio_tee_asrc = NULL;
3161 if (playsink->audio_sinkpad_stream_synchronizer) {
3162 gst_element_release_request_pad (GST_ELEMENT_CAST
3163 (playsink->stream_synchronizer),
3164 playsink->audio_sinkpad_stream_synchronizer);
3165 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3166 playsink->audio_sinkpad_stream_synchronizer = NULL;
3167 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3168 playsink->audio_srcpad_stream_synchronizer = NULL;
3171 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3173 /* Remove the sink from the bin to keep its state
3174 * and unparent it to allow reuse */
3175 if (playsink->audiochain->sink)
3176 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3177 playsink->audiochain->sink);
3179 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3180 disconnect_chain (playsink->audiochain, playsink);
3181 playsink->audiochain->volume = NULL;
3182 playsink->audiochain->mute = NULL;
3183 if (playsink->audiochain->ts_offset)
3184 gst_object_unref (playsink->audiochain->ts_offset);
3185 playsink->audiochain->ts_offset = NULL;
3186 free_chain ((GstPlayChain *) playsink->audiochain);
3187 playsink->audiochain = NULL;
3188 playsink->volume_changed = playsink->mute_changed = FALSE;
3192 if (!playsink->audiochain) {
3193 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3194 playsink->audiochain = gen_audio_chain (playsink, raw);
3197 if (!playsink->audio_sinkpad_stream_synchronizer) {
3198 GValue item = { 0, };
3201 playsink->audio_sinkpad_stream_synchronizer =
3202 gst_element_get_request_pad (GST_ELEMENT_CAST
3203 (playsink->stream_synchronizer), "sink_%u");
3204 it = gst_pad_iterate_internal_links
3205 (playsink->audio_sinkpad_stream_synchronizer);
3207 gst_iterator_next (it, &item);
3208 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3209 g_value_unset (&item);
3210 g_assert (playsink->audio_srcpad_stream_synchronizer);
3211 gst_iterator_free (it);
3214 if (playsink->audiochain) {
3215 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3216 if (playsink->audio_tee_asrc == NULL) {
3217 playsink->audio_tee_asrc =
3218 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3220 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3221 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3222 gst_pad_link_full (playsink->audio_tee_asrc,
3223 playsink->audio_sinkpad_stream_synchronizer,
3224 GST_PAD_LINK_CHECK_NOTHING);
3225 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3226 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3229 GST_DEBUG_OBJECT (playsink, "no audio needed");
3230 /* we have no audio or we are requested to not play audio */
3231 if (playsink->audiochain) {
3232 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3233 /* release the audio pad */
3234 if (playsink->audio_tee_asrc) {
3235 gst_element_release_request_pad (playsink->audio_tee,
3236 playsink->audio_tee_asrc);
3237 gst_object_unref (playsink->audio_tee_asrc);
3238 playsink->audio_tee_asrc = NULL;
3241 if (playsink->audio_sinkpad_stream_synchronizer) {
3242 gst_element_release_request_pad (GST_ELEMENT_CAST
3243 (playsink->stream_synchronizer),
3244 playsink->audio_sinkpad_stream_synchronizer);
3245 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3246 playsink->audio_sinkpad_stream_synchronizer = NULL;
3247 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3248 playsink->audio_srcpad_stream_synchronizer = NULL;
3251 if (playsink->audiochain->sink_volume) {
3252 disconnect_chain (playsink->audiochain, playsink);
3253 playsink->audiochain->volume = NULL;
3254 playsink->audiochain->mute = NULL;
3255 if (playsink->audiochain->ts_offset)
3256 gst_object_unref (playsink->audiochain->ts_offset);
3257 playsink->audiochain->ts_offset = NULL;
3259 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3260 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3267 if (!playsink->vischain)
3268 playsink->vischain = gen_vis_chain (playsink);
3270 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3272 if (playsink->vischain) {
3273 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3275 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3276 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3277 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3278 if (playsink->audio_tee_vissrc == NULL) {
3279 playsink->audio_tee_vissrc =
3280 gst_element_get_request_pad (playsink->audio_tee, "src_%u");
3282 gst_pad_link_full (playsink->audio_tee_vissrc,
3283 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3284 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3285 GST_PAD_LINK_CHECK_NOTHING);
3286 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3287 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3288 gst_object_unref (srcpad);
3291 GST_DEBUG_OBJECT (playsink, "no vis needed");
3292 if (playsink->vischain) {
3293 if (playsink->audio_tee_vissrc) {
3294 gst_element_release_request_pad (playsink->audio_tee,
3295 playsink->audio_tee_vissrc);
3296 gst_object_unref (playsink->audio_tee_vissrc);
3297 playsink->audio_tee_vissrc = NULL;
3299 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3300 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3301 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3306 GST_DEBUG_OBJECT (playsink, "adding text");
3307 if (!playsink->textchain) {
3308 GST_DEBUG_OBJECT (playsink, "creating text chain");
3309 playsink->textchain = gen_text_chain (playsink);
3311 if (playsink->textchain) {
3314 GST_DEBUG_OBJECT (playsink, "adding text chain");
3315 if (playsink->textchain->overlay)
3316 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3318 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3320 if (!playsink->text_sinkpad_stream_synchronizer) {
3321 GValue item = { 0, };
3323 playsink->text_sinkpad_stream_synchronizer =
3324 gst_element_get_request_pad (GST_ELEMENT_CAST
3325 (playsink->stream_synchronizer), "sink_%u");
3326 it = gst_pad_iterate_internal_links
3327 (playsink->text_sinkpad_stream_synchronizer);
3329 gst_iterator_next (it, &item);
3330 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3331 g_value_unset (&item);
3332 g_assert (playsink->text_srcpad_stream_synchronizer);
3333 gst_iterator_free (it);
3335 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3336 playsink->text_sinkpad_stream_synchronizer);
3337 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3338 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3341 if (need_vis || need_video) {
3346 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3347 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3348 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3349 GST_PAD_LINK_CHECK_NOTHING);
3350 gst_object_unref (srcpad);
3352 if (need_deinterlace)
3353 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3354 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3356 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3357 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3359 gst_pad_link_full (playsink->textchain->srcpad,
3360 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3363 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3366 GST_DEBUG_OBJECT (playsink, "no text needed");
3367 /* we have no subtitles/text or we are requested to not show them */
3369 if (playsink->text_sinkpad_stream_synchronizer) {
3370 gst_element_release_request_pad (GST_ELEMENT_CAST
3371 (playsink->stream_synchronizer),
3372 playsink->text_sinkpad_stream_synchronizer);
3373 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3374 playsink->text_sinkpad_stream_synchronizer = NULL;
3375 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3376 playsink->text_srcpad_stream_synchronizer = NULL;
3379 if (playsink->textchain) {
3380 if (playsink->text_pad == NULL) {
3381 /* no text pad, remove the chain entirely */
3382 GST_DEBUG_OBJECT (playsink, "removing text chain");
3383 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3384 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3386 /* we have a chain and a textpad, turn the subtitles off */
3387 GST_DEBUG_OBJECT (playsink, "turning off the text");
3388 if (playsink->textchain->overlay)
3389 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3393 if (!need_video && playsink->video_pad) {
3394 if (playsink->video_sinkpad_stream_synchronizer) {
3395 gst_element_release_request_pad (GST_ELEMENT_CAST
3396 (playsink->stream_synchronizer),
3397 playsink->video_sinkpad_stream_synchronizer);
3398 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3399 playsink->video_sinkpad_stream_synchronizer = NULL;
3400 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3401 playsink->video_srcpad_stream_synchronizer = NULL;
3404 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3407 if (playsink->text_pad && !playsink->textchain)
3408 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad), NULL);
3410 update_av_offset (playsink);
3411 do_async_done (playsink);
3412 GST_PLAY_SINK_UNLOCK (playsink);
3419 /* gen_ chain already posted error */
3420 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3421 GST_PLAY_SINK_UNLOCK (playsink);
3427 * gst_play_sink_set_flags:
3428 * @playsink: a #GstPlaySink
3429 * @flags: #GstPlayFlags
3431 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3432 * when constructing the sink pipelins.
3434 * Returns: TRUE if the flags could be configured.
3437 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3439 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3441 GST_OBJECT_LOCK (playsink);
3442 playsink->flags = flags;
3443 GST_OBJECT_UNLOCK (playsink);
3449 * gst_play_sink_get_flags:
3450 * @playsink: a #GstPlaySink
3452 * Get the flags of @playsink. That flags control the behaviour of the sink when
3453 * it constructs the sink pipelines.
3455 * Returns: the currently configured #GstPlayFlags.
3458 gst_play_sink_get_flags (GstPlaySink * playsink)
3462 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3464 GST_OBJECT_LOCK (playsink);
3465 res = playsink->flags;
3466 GST_OBJECT_UNLOCK (playsink);
3472 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3474 GstPlayTextChain *chain;
3476 GST_PLAY_SINK_LOCK (playsink);
3477 chain = (GstPlayTextChain *) playsink->textchain;
3478 g_free (playsink->font_desc);
3479 playsink->font_desc = g_strdup (desc);
3480 if (chain && chain->overlay) {
3481 g_object_set (chain->overlay, "font-desc", desc, NULL);
3483 GST_PLAY_SINK_UNLOCK (playsink);
3487 gst_play_sink_get_font_desc (GstPlaySink * playsink)
3489 gchar *result = NULL;
3490 GstPlayTextChain *chain;
3492 GST_PLAY_SINK_LOCK (playsink);
3493 chain = (GstPlayTextChain *) playsink->textchain;
3494 if (chain && chain->overlay) {
3495 g_object_get (chain->overlay, "font-desc", &result, NULL);
3496 playsink->font_desc = g_strdup (result);
3498 result = g_strdup (playsink->font_desc);
3500 GST_PLAY_SINK_UNLOCK (playsink);
3506 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
3507 const gchar * encoding)
3509 GstPlayTextChain *chain;
3511 GST_PLAY_SINK_LOCK (playsink);
3512 chain = (GstPlayTextChain *) playsink->textchain;
3513 g_free (playsink->subtitle_encoding);
3514 playsink->subtitle_encoding = g_strdup (encoding);
3515 if (chain && chain->overlay) {
3516 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
3518 GST_PLAY_SINK_UNLOCK (playsink);
3522 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
3524 gchar *result = NULL;
3525 GstPlayTextChain *chain;
3527 GST_PLAY_SINK_LOCK (playsink);
3528 chain = (GstPlayTextChain *) playsink->textchain;
3529 if (chain && chain->overlay) {
3530 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
3531 playsink->subtitle_encoding = g_strdup (result);
3533 result = g_strdup (playsink->subtitle_encoding);
3535 GST_PLAY_SINK_UNLOCK (playsink);
3541 update_av_offset (GstPlaySink * playsink)
3544 GstPlayAudioChain *achain;
3545 GstPlayVideoChain *vchain;
3547 av_offset = playsink->av_offset;
3548 achain = (GstPlayAudioChain *) playsink->audiochain;
3549 vchain = (GstPlayVideoChain *) playsink->videochain;
3551 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
3552 g_object_set (achain->ts_offset, "ts-offset", MAX (0, -av_offset), NULL);
3553 g_object_set (vchain->ts_offset, "ts-offset", MAX (0, av_offset), NULL);
3555 GST_LOG_OBJECT (playsink, "no ts_offset elements");
3560 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
3562 GST_PLAY_SINK_LOCK (playsink);
3563 playsink->av_offset = av_offset;
3564 update_av_offset (playsink);
3565 GST_PLAY_SINK_UNLOCK (playsink);
3569 gst_play_sink_get_av_offset (GstPlaySink * playsink)
3573 GST_PLAY_SINK_LOCK (playsink);
3574 result = playsink->av_offset;
3575 GST_PLAY_SINK_UNLOCK (playsink);
3581 * gst_play_sink_get_last_sample:
3582 * @playsink: a #GstPlaySink
3584 * Get the last displayed sample from @playsink. This sample is in the native
3585 * format of the sink element, the caps in the result sample contain the format
3586 * of the frame data.
3588 * Returns: a #GstSample with the frame data or %NULL when no video frame is
3592 gst_play_sink_get_last_sample (GstPlaySink * playsink)
3594 GstSample *result = NULL;
3595 GstPlayVideoChain *chain;
3597 GST_PLAY_SINK_LOCK (playsink);
3598 GST_DEBUG_OBJECT (playsink, "taking last sample");
3599 /* get the video chain if we can */
3600 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
3601 GST_DEBUG_OBJECT (playsink, "found video chain");
3602 /* see if the chain is active */
3603 if (chain->chain.activated && chain->sink) {
3606 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
3608 /* find and get the last-buffer property now */
3610 gst_play_sink_find_property (playsink, chain->sink,
3611 "last-sample", GST_TYPE_SAMPLE))) {
3612 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
3613 g_object_get (elem, "last-sample", &result, NULL);
3614 gst_object_unref (elem);
3618 GST_PLAY_SINK_UNLOCK (playsink);
3624 * gst_play_sink_convert_sample:
3625 * @playsink: a #GstPlaySink
3628 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
3629 * be in the native format of the sink element and the caps on the buffer
3630 * describe the format of the frame. If @caps is not %NULL, the video
3631 * frame will be converted to the format of the caps.
3633 * Returns: a #GstBuffer with the frame data or %NULL when no video frame is
3634 * available or when the conversion failed.
3637 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
3642 result = gst_play_sink_get_last_sample (playsink);
3643 if (result != NULL && caps != NULL) {
3646 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
3647 if (temp == NULL && err)
3650 gst_sample_unref (result);
3658 /* I'm really uncertain whether we should make playsink post an error
3659 * on the bus or not. It's not like it's a critical issue regarding
3660 * playsink behaviour. */
3661 GST_ERROR ("Error converting frame: %s", err->message);
3662 gst_sample_unref (result);
3669 is_raw_structure (GstStructure * s)
3673 name = gst_structure_get_name (s);
3675 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
3681 is_raw_pad (GstPad * pad)
3683 GstPad *peer = gst_pad_get_peer (pad);
3685 gboolean raw = TRUE;
3690 caps = gst_pad_get_current_caps (peer);
3694 caps = gst_pad_query_caps (peer, NULL);
3696 n = gst_caps_get_size (caps);
3697 for (i = 0; i < n; i++) {
3698 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
3702 } else if (raw != r) {
3703 GST_ERROR_OBJECT (pad,
3704 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
3710 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
3712 gst_caps_unref (caps);
3713 gst_object_unref (peer);
3718 static GstPadProbeReturn
3719 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3720 gpointer user_data);
3723 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
3725 if (playsink->video_pad) {
3727 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3728 (playsink->video_pad)));
3729 if (blocked && playsink->video_block_id == 0) {
3730 playsink->video_block_id =
3731 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3732 sinkpad_blocked_cb, playsink, NULL);
3733 } else if (!blocked && playsink->video_block_id) {
3734 gst_pad_remove_probe (opad, playsink->video_block_id);
3735 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
3736 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
3737 playsink->video_block_id = 0;
3738 playsink->video_pad_blocked = FALSE;
3740 gst_object_unref (opad);
3745 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
3747 if (playsink->audio_pad) {
3749 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3750 (playsink->audio_pad)));
3751 if (blocked && playsink->audio_block_id == 0) {
3752 playsink->audio_block_id =
3753 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3754 sinkpad_blocked_cb, playsink, NULL);
3755 } else if (!blocked && playsink->audio_block_id) {
3756 gst_pad_remove_probe (opad, playsink->audio_block_id);
3757 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
3758 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
3759 playsink->audio_block_id = 0;
3760 playsink->audio_pad_blocked = FALSE;
3762 gst_object_unref (opad);
3767 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
3769 if (playsink->text_pad) {
3771 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
3772 (playsink->text_pad)));
3773 if (blocked && playsink->text_block_id == 0) {
3774 playsink->text_block_id =
3775 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3776 sinkpad_blocked_cb, playsink, NULL);
3777 } else if (!blocked && playsink->text_block_id) {
3778 gst_pad_remove_probe (opad, playsink->text_block_id);
3779 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
3780 playsink->text_block_id = 0;
3781 playsink->text_pad_blocked = FALSE;
3783 gst_object_unref (opad);
3787 static GstPadProbeReturn
3788 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
3791 GstPlaySink *playsink = (GstPlaySink *) user_data;
3794 GST_PLAY_SINK_LOCK (playsink);
3796 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
3797 if (pad == playsink->video_pad) {
3798 playsink->video_pad_blocked = TRUE;
3799 GST_DEBUG_OBJECT (pad, "Video pad blocked");
3800 } else if (pad == playsink->audio_pad) {
3801 playsink->audio_pad_blocked = TRUE;
3802 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
3803 } else if (pad == playsink->text_pad) {
3804 playsink->text_pad_blocked = TRUE;
3805 GST_DEBUG_OBJECT (pad, "Text pad blocked");
3808 /* We reconfigure when for ALL streams:
3809 * * there isn't a pad
3810 * * OR the pad is blocked
3811 * * OR there are no pending blocks on that pad
3814 if ((!playsink->video_pad || playsink->video_pad_blocked
3815 || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
3816 || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
3817 && (!playsink->text_pad || playsink->text_pad_blocked
3818 || !PENDING_TEXT_BLOCK (playsink))) {
3819 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
3821 if (playsink->video_pad) {
3822 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3823 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3824 playsink->video_pad_raw);
3827 if (playsink->audio_pad) {
3828 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3829 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3830 playsink->audio_pad_raw);
3833 gst_play_sink_reconfigure (playsink);
3835 video_set_blocked (playsink, FALSE);
3836 audio_set_blocked (playsink, FALSE);
3837 text_set_blocked (playsink, FALSE);
3840 gst_object_unref (pad);
3842 GST_PLAY_SINK_UNLOCK (playsink);
3844 return GST_PAD_PROBE_OK;
3848 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
3850 gboolean reconfigure = FALSE;
3854 g_object_get (pad, "caps", &caps, NULL);
3858 if (pad == playsink->audio_pad) {
3859 raw = is_raw_pad (pad);
3860 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
3861 && playsink->audiochain;
3862 GST_DEBUG_OBJECT (pad,
3863 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3865 } else if (pad == playsink->video_pad) {
3866 raw = is_raw_pad (pad);
3867 reconfigure = (! !playsink->video_pad_raw != ! !raw)
3868 && playsink->videochain;
3869 GST_DEBUG_OBJECT (pad,
3870 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
3874 gst_caps_unref (caps);
3877 GST_PLAY_SINK_LOCK (playsink);
3878 video_set_blocked (playsink, TRUE);
3879 audio_set_blocked (playsink, TRUE);
3880 text_set_blocked (playsink, TRUE);
3881 GST_PLAY_SINK_UNLOCK (playsink);
3886 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
3887 GstPlaySinkType type)
3889 gulong *block_id = NULL;
3891 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
3893 GST_PLAY_SINK_LOCK (playsink);
3894 if (pad == playsink->video_pad) {
3895 if (type != GST_PLAY_SINK_TYPE_VIDEO_RAW &&
3896 type != GST_PLAY_SINK_TYPE_VIDEO)
3898 block_id = &playsink->video_block_id;
3899 } else if (pad == playsink->audio_pad) {
3900 if (type != GST_PLAY_SINK_TYPE_AUDIO_RAW &&
3901 type != GST_PLAY_SINK_TYPE_AUDIO)
3903 block_id = &playsink->audio_block_id;
3904 } else if (pad == playsink->text_pad) {
3905 if (type != GST_PLAY_SINK_TYPE_TEXT)
3907 block_id = &playsink->text_block_id;
3910 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
3912 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
3915 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3916 sinkpad_blocked_cb, playsink, NULL);
3917 PENDING_FLAG_SET (playsink, type);
3918 gst_object_unref (blockpad);
3920 GST_PLAY_SINK_UNLOCK (playsink);
3927 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
3929 GST_PLAY_SINK_UNLOCK (playsink);
3935 * gst_play_sink_request_pad
3936 * @playsink: a #GstPlaySink
3937 * @type: a #GstPlaySinkType
3939 * Create or return a pad of @type.
3941 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
3944 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
3947 gboolean created = FALSE;
3948 gboolean activate = TRUE;
3949 const gchar *pad_name = NULL;
3950 gulong *block_id = NULL;
3952 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
3954 GST_PLAY_SINK_LOCK (playsink);
3956 case GST_PLAY_SINK_TYPE_AUDIO_RAW:
3957 case GST_PLAY_SINK_TYPE_AUDIO:
3958 pad_name = "audio_sink";
3959 if (!playsink->audio_tee) {
3960 GST_LOG_OBJECT (playsink, "creating tee");
3961 /* create tee when needed. This element will feed the audio sink chain
3962 * and the vis chain. */
3963 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
3964 if (playsink->audio_tee == NULL) {
3965 post_missing_element_message (playsink, "tee");
3966 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3967 (_("Missing element '%s' - check your GStreamer installation."),
3972 playsink->audio_tee_sink =
3973 gst_element_get_static_pad (playsink->audio_tee, "sink");
3974 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
3975 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3978 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
3980 if (!playsink->audio_pad) {
3981 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
3982 playsink->audio_pad =
3983 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
3984 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
3985 G_CALLBACK (caps_notify_cb), playsink);
3988 playsink->audio_pad_raw = FALSE;
3989 res = playsink->audio_pad;
3990 block_id = &playsink->audio_block_id;
3992 case GST_PLAY_SINK_TYPE_VIDEO_RAW:
3993 case GST_PLAY_SINK_TYPE_VIDEO:
3994 pad_name = "video_sink";
3995 if (!playsink->video_pad) {
3996 GST_LOG_OBJECT (playsink, "ghosting videosink");
3997 playsink->video_pad =
3998 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
3999 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4000 G_CALLBACK (caps_notify_cb), playsink);
4003 playsink->video_pad_raw = FALSE;
4004 res = playsink->video_pad;
4005 block_id = &playsink->video_block_id;
4007 case GST_PLAY_SINK_TYPE_TEXT:
4008 GST_LOG_OBJECT (playsink, "ghosting text");
4009 if (!playsink->text_pad) {
4010 playsink->text_pad =
4011 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4014 res = playsink->text_pad;
4015 block_id = &playsink->text_block_id;
4017 case GST_PLAY_SINK_TYPE_FLUSHING:
4021 /* we need a unique padname for the flushing pad. */
4022 padname = g_strdup_printf ("flushing_%u", playsink->count);
4023 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4034 GST_PLAY_SINK_UNLOCK (playsink);
4036 if (created && res) {
4037 /* we have to add the pad when it's active or we get an error when the
4038 * element is 'running' */
4039 gst_pad_set_active (res, TRUE);
4040 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4041 if (block_id && *block_id == 0) {
4043 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4046 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4047 sinkpad_blocked_cb, playsink, NULL);
4048 PENDING_FLAG_SET (playsink, type);
4049 gst_object_unref (blockpad);
4052 gst_pad_set_active (res, activate);
4060 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4061 const gchar * name, const GstCaps * caps)
4065 GstPlaySinkType type;
4066 const gchar *tplname;
4068 g_return_val_if_fail (templ != NULL, NULL);
4070 GST_DEBUG_OBJECT (element, "name:%s", name);
4072 psink = GST_PLAY_SINK (element);
4073 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4075 /* Figure out the GstPlaySinkType based on the template */
4076 if (!strcmp (tplname, "audio_sink"))
4077 type = GST_PLAY_SINK_TYPE_AUDIO;
4078 else if (!strcmp (tplname, "audio_raw_sink"))
4079 type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
4080 else if (!strcmp (tplname, "video_sink"))
4081 type = GST_PLAY_SINK_TYPE_VIDEO;
4082 else if (!strcmp (tplname, "video_raw_sink"))
4083 type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
4084 else if (!strcmp (tplname, "text_sink"))
4085 type = GST_PLAY_SINK_TYPE_TEXT;
4087 goto unknown_template;
4089 pad = gst_play_sink_request_pad (psink, type);
4093 GST_WARNING_OBJECT (element, "Unknown pad template");
4098 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4100 GstPad **res = NULL;
4101 gboolean untarget = TRUE;
4103 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4105 GST_PLAY_SINK_LOCK (playsink);
4106 if (pad == playsink->video_pad) {
4107 res = &playsink->video_pad;
4108 g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
4110 } else if (pad == playsink->audio_pad) {
4111 res = &playsink->audio_pad;
4112 g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
4114 } else if (pad == playsink->text_pad) {
4115 res = &playsink->text_pad;
4117 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4121 GST_PLAY_SINK_UNLOCK (playsink);
4124 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4125 gst_pad_set_active (*res, FALSE);
4127 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4128 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4130 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4131 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4137 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4139 GstPlaySink *psink = GST_PLAY_SINK (element);
4141 gst_play_sink_release_pad (psink, pad);
4145 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4147 GstPlaySink *playsink;
4149 playsink = GST_PLAY_SINK_CAST (bin);
4151 switch (GST_MESSAGE_TYPE (message)) {
4152 case GST_MESSAGE_STEP_DONE:
4157 gboolean flush, intermediate, eos;
4160 GST_INFO_OBJECT (playsink, "Handling step-done message");
4161 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4162 &intermediate, &duration, &eos);
4164 if (format == GST_FORMAT_BUFFERS) {
4165 /* for the buffer format, we align the other streams */
4166 if (playsink->audiochain) {
4170 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4173 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4174 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4178 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4181 case GST_MESSAGE_ELEMENT:{
4182 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4183 GstVideoOverlay *overlay;
4185 GST_OBJECT_LOCK (playsink);
4186 if (playsink->overlay_element
4187 && GST_OBJECT_CAST (playsink->overlay_element) !=
4188 GST_MESSAGE_SRC (message)) {
4189 gst_object_unref (playsink->overlay_element);
4190 playsink->overlay_element = NULL;
4193 if (!playsink->overlay_element)
4194 playsink->overlay_element =
4195 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4197 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4198 GST_OBJECT_UNLOCK (playsink);
4200 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4202 if (playsink->overlay_handle_set)
4203 gst_video_overlay_set_window_handle (playsink->overlay_element,
4204 playsink->overlay_handle);
4205 if (playsink->overlay_handle_events_set)
4206 gst_video_overlay_handle_events (playsink->overlay_element,
4207 playsink->overlay_handle_events);
4208 if (playsink->overlay_render_rectangle_set)
4209 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4210 playsink->overlay_x, playsink->overlay_y,
4211 playsink->overlay_width, playsink->overlay_height);
4213 gst_object_unref (overlay);
4214 gst_message_unref (message);
4215 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4217 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4223 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4228 /* Send an event to our sinks until one of them works; don't then send to the
4229 * remaining sinks (unlike GstBin)
4230 * Special case: If a text sink is set we need to send the event
4231 * to them in case it's source is different from the a/v stream's source.
4234 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
4236 gboolean res = TRUE;
4237 if (playsink->send_event_mode == MODE_FIRST) {
4238 if (playsink->textchain && playsink->textchain->sink) {
4239 gst_event_ref (event);
4241 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4242 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4244 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4248 if (playsink->videochain) {
4249 gst_event_ref (event);
4251 gst_element_send_event (playsink->videochain->chain.bin,
4253 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4256 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4258 if (playsink->audiochain) {
4259 gst_event_ref (event);
4261 gst_element_send_event (playsink->audiochain->chain.bin,
4263 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4266 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4270 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4271 (GST_ELEMENT_CAST (playsink), event);
4275 gst_event_unref (event);
4279 /* We only want to send the event to a single sink (overriding GstBin's
4280 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4281 * events appropriately. So, this is a messy duplication of code. */
4283 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4285 gboolean res = FALSE;
4286 GstEventType event_type = GST_EVENT_TYPE (event);
4287 GstPlaySink *playsink;
4288 playsink = GST_PLAY_SINK_CAST (element);
4289 switch (event_type) {
4290 case GST_EVENT_SEEK:
4291 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4292 res = gst_play_sink_send_event_to_sink (playsink, event);
4294 case GST_EVENT_STEP:
4299 gboolean flush, intermediate;
4300 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4302 if (format == GST_FORMAT_BUFFERS) {
4303 /* for buffers, we will try to step video frames, for other formats we
4304 * send the step to all sinks */
4305 res = gst_play_sink_send_event_to_sink (playsink, event);
4308 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4315 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4322 static GstStateChangeReturn
4323 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4325 GstStateChangeReturn ret;
4326 GstStateChangeReturn bret;
4327 GstPlaySink *playsink;
4328 playsink = GST_PLAY_SINK (element);
4329 switch (transition) {
4330 case GST_STATE_CHANGE_READY_TO_PAUSED:
4331 gst_segment_init (&playsink->text_segment, GST_FORMAT_UNDEFINED);
4333 playsink->need_async_start = TRUE;
4334 /* we want to go async to PAUSED until we managed to configure and add the
4336 do_async_start (playsink);
4337 ret = GST_STATE_CHANGE_ASYNC;
4339 /* block all pads here */
4340 GST_PLAY_SINK_LOCK (playsink);
4341 if (playsink->video_pad && playsink->video_block_id == 0) {
4343 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4344 (playsink->video_pad)));
4345 playsink->video_block_id =
4346 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4347 sinkpad_blocked_cb, playsink, NULL);
4348 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4349 gst_object_unref (opad);
4352 if (playsink->audio_pad && playsink->audio_block_id == 0) {
4354 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4355 (playsink->audio_pad)));
4356 playsink->audio_block_id =
4357 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4358 sinkpad_blocked_cb, playsink, NULL);
4359 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4360 gst_object_unref (opad);
4363 if (playsink->text_pad && playsink->text_block_id == 0) {
4365 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4366 (playsink->text_pad)));
4367 playsink->text_block_id =
4368 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4369 sinkpad_blocked_cb, playsink, NULL);
4370 PENDING_FLAG_SET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4371 gst_object_unref (opad);
4373 GST_PLAY_SINK_UNLOCK (playsink);
4375 case GST_STATE_CHANGE_PAUSED_TO_READY:
4376 /* unblock all pads here */
4377 GST_PLAY_SINK_LOCK (playsink);
4378 video_set_blocked (playsink, FALSE);
4379 audio_set_blocked (playsink, FALSE);
4380 text_set_blocked (playsink, FALSE);
4381 GST_PLAY_SINK_UNLOCK (playsink);
4383 case GST_STATE_CHANGE_READY_TO_NULL:
4384 if (playsink->audiochain && playsink->audiochain->sink_volume) {
4385 /* remove our links to the mute and volume elements when they were
4386 * provided by a sink */
4387 disconnect_chain (playsink->audiochain, playsink);
4388 playsink->audiochain->volume = NULL;
4389 playsink->audiochain->mute = NULL;
4392 if (playsink->audiochain && playsink->audiochain->ts_offset) {
4393 gst_object_unref (playsink->audiochain->ts_offset);
4394 playsink->audiochain->ts_offset = NULL;
4397 if (playsink->videochain && playsink->videochain->ts_offset) {
4398 gst_object_unref (playsink->videochain->ts_offset);
4399 playsink->videochain->ts_offset = NULL;
4402 GST_OBJECT_LOCK (playsink);
4403 if (playsink->overlay_element)
4404 gst_object_unref (playsink->overlay_element);
4405 playsink->overlay_element = NULL;
4407 if (playsink->colorbalance_element) {
4408 g_signal_handlers_disconnect_by_func (playsink->colorbalance_element,
4409 G_CALLBACK (colorbalance_value_changed_cb), playsink);
4410 gst_object_unref (playsink->colorbalance_element);
4412 playsink->colorbalance_element = NULL;
4413 GST_OBJECT_UNLOCK (playsink);
4415 ret = GST_STATE_CHANGE_SUCCESS;
4418 /* all other state changes return SUCCESS by default, this value can be
4419 * overridden by the result of the children */
4420 ret = GST_STATE_CHANGE_SUCCESS;
4424 /* do the state change of the children */
4426 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
4428 /* now look at the result of our children and adjust the return value */
4430 case GST_STATE_CHANGE_FAILURE:
4431 /* failure, we stop */
4432 goto activate_failed;
4433 case GST_STATE_CHANGE_NO_PREROLL:
4434 /* some child returned NO_PREROLL. This is strange but we never know. We
4435 * commit our async state change (if any) and return the NO_PREROLL */
4436 do_async_done (playsink);
4439 case GST_STATE_CHANGE_ASYNC:
4440 /* some child was async, return this */
4444 /* return our previously configured return value */
4448 switch (transition) {
4449 case GST_STATE_CHANGE_READY_TO_PAUSED:
4451 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4452 /* FIXME Release audio device when we implement that */
4453 playsink->need_async_start = TRUE;
4455 case GST_STATE_CHANGE_PAUSED_TO_READY:{
4456 if (playsink->video_sinkpad_stream_synchronizer) {
4457 gst_element_release_request_pad (GST_ELEMENT_CAST
4458 (playsink->stream_synchronizer),
4459 playsink->video_sinkpad_stream_synchronizer);
4460 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
4461 playsink->video_sinkpad_stream_synchronizer = NULL;
4462 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
4463 playsink->video_srcpad_stream_synchronizer = NULL;
4465 if (playsink->audio_sinkpad_stream_synchronizer) {
4466 gst_element_release_request_pad (GST_ELEMENT_CAST
4467 (playsink->stream_synchronizer),
4468 playsink->audio_sinkpad_stream_synchronizer);
4469 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
4470 playsink->audio_sinkpad_stream_synchronizer = NULL;
4471 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
4472 playsink->audio_srcpad_stream_synchronizer = NULL;
4474 if (playsink->text_sinkpad_stream_synchronizer) {
4475 gst_element_release_request_pad (GST_ELEMENT_CAST
4476 (playsink->stream_synchronizer),
4477 playsink->text_sinkpad_stream_synchronizer);
4478 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
4479 playsink->text_sinkpad_stream_synchronizer = NULL;
4480 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
4481 playsink->text_srcpad_stream_synchronizer = NULL;
4485 case GST_STATE_CHANGE_READY_TO_NULL:
4486 /* remove sinks we added */
4487 if (playsink->videodeinterlacechain) {
4488 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
4490 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
4492 if (playsink->videochain) {
4493 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4494 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
4496 if (playsink->audiochain) {
4497 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4498 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
4500 if (playsink->vischain) {
4501 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4502 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
4504 if (playsink->textchain) {
4505 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4506 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
4508 do_async_done (playsink);
4509 /* when going to READY, keep elements around as long as possible,
4510 * so they may be re-used faster next time/url around.
4511 * when really going to NULL, clean up everything completely. */
4512 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
4514 /* Unparent the sinks to allow reuse */
4515 if (playsink->videochain && playsink->videochain->sink)
4516 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
4517 playsink->videochain->sink);
4518 if (playsink->audiochain && playsink->audiochain->sink)
4519 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
4520 playsink->audiochain->sink);
4521 if (playsink->textchain && playsink->textchain->sink)
4522 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
4523 playsink->textchain->sink);
4524 if (playsink->audio_sink != NULL)
4525 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
4526 if (playsink->video_sink != NULL)
4527 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
4528 if (playsink->visualisation != NULL)
4529 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
4530 if (playsink->text_sink != NULL)
4531 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
4532 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
4533 playsink->videodeinterlacechain = NULL;
4534 free_chain ((GstPlayChain *) playsink->videochain);
4535 playsink->videochain = NULL;
4536 free_chain ((GstPlayChain *) playsink->audiochain);
4537 playsink->audiochain = NULL;
4538 free_chain ((GstPlayChain *) playsink->vischain);
4539 playsink->vischain = NULL;
4540 free_chain ((GstPlayChain *) playsink->textchain);
4541 playsink->textchain = NULL;
4551 GST_DEBUG_OBJECT (element,
4552 "element failed to change states -- activation problem?");
4553 return GST_STATE_CHANGE_FAILURE;
4558 gst_play_sink_set_property (GObject * object, guint prop_id,
4559 const GValue * value, GParamSpec * spec)
4561 GstPlaySink *playsink = GST_PLAY_SINK (object);
4564 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
4567 gst_play_sink_set_volume (playsink, g_value_get_double (value));
4570 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
4572 case PROP_FONT_DESC:
4573 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
4575 case PROP_SUBTITLE_ENCODING:
4576 gst_play_sink_set_subtitle_encoding (playsink,
4577 g_value_get_string (value));
4579 case PROP_VIS_PLUGIN:
4580 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
4582 case PROP_AV_OFFSET:
4583 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
4585 case PROP_VIDEO_SINK:
4586 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
4587 g_value_get_object (value));
4589 case PROP_AUDIO_SINK:
4590 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
4591 g_value_get_object (value));
4593 case PROP_TEXT_SINK:
4594 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
4595 g_value_get_object (value));
4597 case PROP_SEND_EVENT_MODE:
4598 playsink->send_event_mode = g_value_get_enum (value);
4601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4607 gst_play_sink_get_property (GObject * object, guint prop_id,
4608 GValue * value, GParamSpec * spec)
4610 GstPlaySink *playsink = GST_PLAY_SINK (object);
4613 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
4616 g_value_set_double (value, gst_play_sink_get_volume (playsink));
4619 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
4621 case PROP_FONT_DESC:
4622 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
4624 case PROP_SUBTITLE_ENCODING:
4625 g_value_take_string (value,
4626 gst_play_sink_get_subtitle_encoding (playsink));
4628 case PROP_VIS_PLUGIN:
4629 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
4632 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
4634 case PROP_AV_OFFSET:
4635 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
4637 case PROP_VIDEO_SINK:
4638 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4639 GST_PLAY_SINK_TYPE_VIDEO));
4641 case PROP_AUDIO_SINK:
4642 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4643 GST_PLAY_SINK_TYPE_AUDIO));
4645 case PROP_TEXT_SINK:
4646 g_value_take_object (value, gst_play_sink_get_sink (playsink,
4647 GST_PLAY_SINK_TYPE_TEXT));
4649 case PROP_SEND_EVENT_MODE:
4650 g_value_set_enum (value, playsink->send_event_mode);
4653 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
4659 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
4661 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4662 GstVideoOverlay *overlay_element;
4664 GST_OBJECT_LOCK (playsink);
4665 if (playsink->overlay_element)
4667 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4669 overlay_element = NULL;
4670 GST_OBJECT_UNLOCK (playsink);
4672 if (overlay_element) {
4673 gst_video_overlay_expose (overlay_element);
4674 gst_object_unref (overlay_element);
4679 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
4680 gboolean handle_events)
4682 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4683 GstVideoOverlay *overlay_element;
4685 GST_OBJECT_LOCK (playsink);
4686 if (playsink->overlay_element)
4688 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4690 overlay_element = NULL;
4691 GST_OBJECT_UNLOCK (playsink);
4693 playsink->overlay_handle_events_set = TRUE;
4694 playsink->overlay_handle_events = handle_events;
4696 if (overlay_element) {
4697 gst_video_overlay_handle_events (overlay_element, handle_events);
4698 gst_object_unref (overlay_element);
4703 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
4704 gint y, gint width, gint height)
4706 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4707 GstVideoOverlay *overlay_element;
4709 GST_OBJECT_LOCK (playsink);
4710 if (playsink->overlay_element)
4712 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4714 overlay_element = NULL;
4715 GST_OBJECT_UNLOCK (playsink);
4717 playsink->overlay_render_rectangle_set = TRUE;
4718 playsink->overlay_x = x;
4719 playsink->overlay_y = y;
4720 playsink->overlay_width = width;
4721 playsink->overlay_height = height;
4723 if (overlay_element) {
4724 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
4726 gst_object_unref (overlay_element);
4731 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
4734 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
4735 GstVideoOverlay *overlay_element;
4737 GST_OBJECT_LOCK (playsink);
4738 if (playsink->overlay_element)
4740 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4742 overlay_element = NULL;
4743 GST_OBJECT_UNLOCK (playsink);
4745 playsink->overlay_handle_set = TRUE;
4746 playsink->overlay_handle = handle;
4748 if (overlay_element) {
4749 gst_video_overlay_set_window_handle (overlay_element, handle);
4750 gst_object_unref (overlay_element);
4755 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
4757 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
4758 iface->expose = gst_play_sink_overlay_expose;
4759 iface->handle_events = gst_play_sink_overlay_handle_events;
4760 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
4761 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
4765 gst_play_sink_navigation_send_event (GstNavigation * navigation,
4766 GstStructure * structure)
4768 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
4771 GST_PLAY_SINK_LOCK (playsink);
4772 if (playsink->videochain && playsink->videochain->chain.bin)
4773 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
4774 GST_PLAY_SINK_UNLOCK (playsink);
4777 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
4780 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
4782 gst_object_unref (nav);
4784 GstEvent *event = gst_event_new_navigation (structure);
4786 gst_element_send_event (GST_ELEMENT (bin), event);
4789 gst_object_unref (bin);
4793 gst_structure_free (structure);
4797 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
4799 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
4801 iface->send_event = gst_play_sink_navigation_send_event;
4804 static const GList *
4805 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
4807 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4809 return playsink->colorbalance_channels;
4813 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
4814 GstColorBalanceChannel * proxy, gint value)
4816 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4819 GstColorBalance *balance_element = NULL;
4821 GST_OBJECT_LOCK (playsink);
4822 if (playsink->colorbalance_element)
4824 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4825 GST_OBJECT_UNLOCK (playsink);
4827 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4828 GstColorBalanceChannel *proxy_tmp = l->data;
4831 if (proxy_tmp != proxy)
4834 playsink->colorbalance_values[i] = value;
4836 if (balance_element) {
4837 GstColorBalanceChannel *channel = NULL;
4838 const GList *channels, *k;
4840 channels = gst_color_balance_list_channels (balance_element);
4841 for (k = channels; k; k = k->next) {
4842 GstColorBalanceChannel *tmp = l->data;
4844 if (g_strrstr (tmp->label, proxy->label)) {
4852 /* Convert to [0, 1] range */
4855 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
4856 (gdouble) proxy->min_value);
4857 /* Convert to channel range */
4859 channel->min_value + new_val * ((gdouble) channel->max_value -
4860 (gdouble) channel->min_value);
4862 gst_color_balance_set_value (balance_element, channel,
4863 (gint) (new_val + 0.5));
4865 gst_object_unref (balance_element);
4868 gst_color_balance_value_changed (balance, proxy, value);
4874 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
4875 GstColorBalanceChannel * proxy)
4877 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4881 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
4882 GstColorBalanceChannel *proxy_tmp = l->data;
4884 if (proxy_tmp != proxy)
4887 return playsink->colorbalance_values[i];
4890 g_return_val_if_reached (0);
4893 static GstColorBalanceType
4894 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
4896 GstPlaySink *playsink = GST_PLAY_SINK (balance);
4897 GstColorBalance *balance_element = NULL;
4898 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
4900 GST_OBJECT_LOCK (playsink);
4901 if (playsink->colorbalance_element)
4903 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
4904 GST_OBJECT_UNLOCK (playsink);
4906 if (balance_element) {
4907 t = gst_color_balance_get_balance_type (balance_element);
4908 gst_object_unref (balance_element);
4915 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
4917 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
4919 iface->list_channels = gst_play_sink_colorbalance_list_channels;
4920 iface->set_value = gst_play_sink_colorbalance_set_value;
4921 iface->get_value = gst_play_sink_colorbalance_get_value;
4922 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
4926 gst_play_sink_plugin_init (GstPlugin * plugin)
4928 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
4929 return gst_element_register (plugin, "playsink", GST_RANK_NONE,
4930 GST_TYPE_PLAY_SINK);